-- =============================================================================
-- DC_UTILS_PKG
-- Hilfsprozeduren und -funktionen für den Dokumenten-Check
--
-- Deployment:
--   sql user/pass@db @"Scripts/DC_UTILS_PKG.sql"
-- =============================================================================


-- =============================================================================
-- Package Spec
-- =============================================================================
CREATE OR REPLACE PACKAGE DC_UTILS_PKG AS

    -- Absender-Adresse für Berichts-Mails (anpassen falls nötig)
    c_mail_from CONSTANT VARCHAR2(100) := 'express-o@frigosped.de';

    -- Basis-URL der APEX-App für Direktlinks in Mails (Friendly URL, ohne
    -- Session-ID -> APEX Deep-Linking leitet bei Bedarf über den Login).
    -- Die Projekt-ID wird angehängt: ...?p7_project_id=<id>
    c_app_link_base CONSTANT VARCHAR2(300) :=
        'https://apex-ai.express-o.org/ords/r/ai_dev/dokumentencheck/' ||
        'projekt-bearbeiten?p7_project_id=';

    -- -------------------------------------------------------------------------
    -- Erstellt einen Markdown-Prüfbericht für ein Projekt und speichert ihn
    -- in dc_projects.report_markdown. Anschließend wird der Bericht per Mail
    -- an notification_email versendet (sofern gesetzt).
    --
    -- Aufbau des Berichts:
    --   1. Kopf (Projektname, Abschlussdatum)
    --   2. Zusammenfassung (OK / Hinweise / Abweichungen) – gefiltert nach notify_*
    --   3. Ergebnisse: je Dokument → Kategorie (order_nr) → Frage (order_nr)
    --      Icons: ✅ OK  |  ⚠️ Hinweis  |  ❌ Abweichung  |  ❓ Unklar
    --
    -- Beispiel:
    --   BEGIN DC_UTILS_PKG.generate_report(p_project_id => 1); COMMIT; END;
    -- -------------------------------------------------------------------------
    PROCEDURE generate_report(p_project_id IN NUMBER);

    -- -------------------------------------------------------------------------
    -- Versendet den gespeicherten Markdown-Bericht per E-Mail als PDF-Anhang.
    -- Wird automatisch von generate_report aufgerufen; kann aber auch
    -- manuell ausgelöst werden (z.B. für erneuten Versand).
    -- Tut nichts wenn notification_email oder report_markdown leer ist.
    -- p_skip_pdf = TRUE: kein HTTP-Rückruf ans Java-Backend (nur Text-Mail).
    -- -------------------------------------------------------------------------
    PROCEDURE send_report_email(p_project_id IN NUMBER,
                                p_skip_pdf   IN BOOLEAN DEFAULT FALSE,
                                p_pdf        IN BLOB    DEFAULT NULL);

END DC_UTILS_PKG;
/


-- =============================================================================
-- Package Body
-- =============================================================================
CREATE OR REPLACE PACKAGE BODY DC_UTILS_PKG AS

    -- -------------------------------------------------------------------------
    PROCEDURE generate_report(p_project_id IN NUMBER) IS

        v_md       CLOB;
        v_name     dc_projects.name%TYPE;
        v_status   dc_projects.status%TYPE;
        v_done     dc_projects.completed_at%TYPE;

        v_ok_cnt    NUMBER := 0;
        v_warn_cnt  NUMBER := 0;
        v_dev_cnt   NUMBER := 0;
        v_unkl_cnt  NUMBER := 0;
        v_total     NUMBER := 0;
        v_total_all NUMBER := 0;

        -- Zähler pro Dokument
        v_doc_ok_cnt    NUMBER := 0;
        v_doc_warn_cnt  NUMBER := 0;
        v_doc_dev_cnt   NUMBER := 0;
        v_doc_unkl_cnt  NUMBER := 0;
        v_doc_total     NUMBER := 0;  -- gefiltert (für ok_cnt-Berechnung)
        v_doc_total_all NUMBER := 0;  -- ungefiltert (für Gesamt-Zeile)

        v_notify_ok           dc_projects.notify_ok%TYPE;
        v_notify_hinweis      dc_projects.notify_hinweis%TYPE;
        v_notify_abweichend   dc_projects.notify_abweichend%TYPE;

        -- Emoji-Konstanten via UNISTR (vermeidet Quellcode-Encoding-Probleme)
        C_OK    CONSTANT VARCHAR2(10 CHAR) := UNISTR('\2705');           -- ✅
        C_NOK   CONSTANT VARCHAR2(10 CHAR) := UNISTR('\274C');           -- ❌
        C_WARN  CONSTANT VARCHAR2(10 CHAR) := UNISTR('\26A0\FE0F');      -- ⚠️
        C_UNKL  CONSTANT VARCHAR2(10 CHAR) := UNISTR('\2753');           -- ❓
        C_DOC   CONSTANT VARCHAR2(10 CHAR) := UNISTR('\D83D\DCC4');      -- 📄

        CURSOR c_docs IS
            SELECT d.id, d.filename
            FROM   dc_project_documents d
            WHERE  d.project_id = p_project_id
            ORDER  BY d.uploaded_at, d.id;

        CURSOR c_cats(p_doc_id NUMBER) IS
            SELECT DISTINCT qc.id, qc.name, qc.order_nr
            FROM   dc_question_categories qc
            JOIN   dc_questions           q  ON q.category_id  = qc.id
            JOIN   dc_results             r  ON r.question_id  = q.id
            WHERE  r.project_id = p_project_id
              AND  r.doc_id     = p_doc_id
              AND  (   (r.deviation = 1 AND v_notify_abweichend = 'Y')
                    OR (r.warning   = 1 AND v_notify_hinweis    = 'Y')
                    OR (r.deviation = 0 AND r.warning = 0 AND v_notify_ok = 'Y')
                   )
            ORDER  BY qc.order_nr, qc.name;

        CURSOR c_qs(p_doc_id NUMBER, p_cat_id NUMBER) IS
            SELECT q.question_text,
                   r.result_type,
                   r.score,
                   r.deviation,
                   r.warning,
                   r.answer,
                   r.the_comment
            FROM   dc_questions q
            JOIN   dc_results   r ON r.question_id = q.id
            WHERE  r.project_id  = p_project_id
              AND  r.doc_id      = p_doc_id
              AND  q.category_id = p_cat_id
              AND  (   (r.deviation = 1 AND v_notify_abweichend = 'Y')
                    OR (r.warning   = 1 AND v_notify_hinweis    = 'Y')
                    OR (r.deviation = 0 AND r.warning = 0 AND v_notify_ok = 'Y')
                   )
            ORDER  BY q.order_nr, q.id;

        v_icon VARCHAR2(20 CHAR);

        -- Fügt eine Zeile + Newline an den CLOB an. Akzeptiert CLOB damit kein
        -- implizites CLOB→VARCHAR2-Cast nötig ist (question_text etc. sind CLOBs).
        PROCEDURE ln(p_text IN CLOB DEFAULT NULL) IS
        BEGIN
            IF p_text IS NOT NULL AND DBMS_LOB.GETLENGTH(p_text) > 0 THEN
                DBMS_LOB.APPEND(v_md, p_text);
            END IF;
            DBMS_LOB.APPEND(v_md, TO_CLOB(CHR(13)||CHR(10)));
        END ln;

    BEGIN
        DBMS_LOB.CREATETEMPORARY(v_md, TRUE);

        SELECT name, status, completed_at,
               notify_ok, notify_hinweis, notify_abweichend
        INTO   v_name, v_status, v_done,
               v_notify_ok, v_notify_hinweis, v_notify_abweichend
        FROM   dc_projects
        WHERE  id = p_project_id;

        SELECT COUNT(*)
        INTO   v_total_all
        FROM   dc_results
        WHERE  project_id = p_project_id;

        SELECT SUM(CASE WHEN deviation   = 1 THEN 1 ELSE 0 END),
               SUM(CASE WHEN warning     = 1 THEN 1 ELSE 0 END),
               SUM(CASE WHEN result_type = 'UNKLAR'
                             AND deviation = 0
                             AND warning   = 0 THEN 1 ELSE 0 END),
               COUNT(*)
        INTO   v_dev_cnt, v_warn_cnt, v_unkl_cnt, v_total
        FROM   dc_results
        WHERE  project_id = p_project_id
          AND  (   (deviation = 1 AND v_notify_abweichend = 'Y')
               OR  (warning   = 1 AND v_notify_hinweis    = 'Y')
               OR  (deviation = 0 AND warning = 0 AND v_notify_ok = 'Y')
               );

        v_ok_cnt := v_total - v_dev_cnt - v_warn_cnt - v_unkl_cnt;

        -- =====================================================================
        -- Kopf
        -- =====================================================================
        ln('#  Prüfbericht: ' || v_name);
        ln();
        IF v_done IS NOT NULL THEN
            ln('**Abgeschlossen:** ' || TO_CHAR(v_done, 'DD.MM.YYYY HH24:MI'));
        ELSE
            ln('**Erstellt:** ' || TO_CHAR(SYSDATE, 'DD.MM.YYYY HH24:MI'));
        END IF;
        ln();
        ln('---');
        ln();

        -- =====================================================================
        -- Zusammenfassung (keine Tabelle – Plaintext für maximale Kompatibilität)
        -- =====================================================================
        ln('## Zusammenfassung');
        ln();
        IF v_notify_ok = 'Y' THEN
            ln(C_OK   || '  OK: '       || v_ok_cnt);
            IF v_unkl_cnt > 0 THEN
                ln(C_UNKL || '  Unklar: ' || v_unkl_cnt);
            END IF;
        END IF;
        IF v_notify_hinweis = 'Y' THEN
            ln(C_WARN || '  Hinweise: '     || v_warn_cnt);
        END IF;
        IF v_notify_abweichend = 'Y' THEN
            ln(C_NOK  || '  Abweichungen: ' || v_dev_cnt);
        END IF;
        ln('**Gesamt: ' || v_total_all || '**');
        ln();
        ln('---');
        ln();

        -- =====================================================================
        -- Ergebnisse je Dokument → Kategorie (order_nr) → Frage (order_nr)
        -- =====================================================================
        ln('## Ergebnisse');
        ln();

        FOR doc IN c_docs LOOP
            ln('## ' || C_DOC || ' ' || doc.filename);
            ln();

            -- Zusammenfassung je Dokument (gefiltert nach notify_*, analog zur Gesamt-Zusammenfassung)
            SELECT SUM(CASE WHEN deviation = 1
                                 AND v_notify_abweichend = 'Y'                    THEN 1 ELSE 0 END),
                   SUM(CASE WHEN warning   = 1 AND deviation = 0
                                 AND v_notify_hinweis    = 'Y'                    THEN 1 ELSE 0 END),
                   SUM(CASE WHEN result_type = 'UNKLAR' AND deviation = 0 AND warning = 0
                                 AND v_notify_ok         = 'Y'                    THEN 1 ELSE 0 END),
                   SUM(CASE WHEN (deviation = 1 AND v_notify_abweichend = 'Y')
                                 OR (warning = 1 AND v_notify_hinweis   = 'Y')
                                 OR (deviation = 0 AND warning = 0
                                     AND v_notify_ok = 'Y')                       THEN 1 ELSE 0 END),
                   COUNT(*)
            INTO   v_doc_dev_cnt, v_doc_warn_cnt, v_doc_unkl_cnt, v_doc_total, v_doc_total_all
            FROM   dc_results
            WHERE  project_id = p_project_id
              AND  doc_id     = doc.id;

            v_doc_ok_cnt := v_doc_total - v_doc_dev_cnt - v_doc_warn_cnt - v_doc_unkl_cnt;

            IF v_notify_ok = 'Y' THEN
                ln(C_OK   || '  OK:              ' || v_doc_ok_cnt);
                IF v_doc_unkl_cnt > 0 THEN
                    ln(C_UNKL || '  Unklar:          ' || v_doc_unkl_cnt);
                END IF;
            END IF;
            IF v_notify_hinweis = 'Y' THEN
                ln(C_WARN || '  Hinweise:        ' || v_doc_warn_cnt);
            END IF;
            IF v_notify_abweichend = 'Y' THEN
                ln(C_NOK  || '  Abweichungen:    ' || v_doc_dev_cnt);
            END IF;
            ln('**Gesamt: ' || v_doc_total_all || '**');
            ln();
            ln('---');
            ln();

            FOR cat IN c_cats(doc.id) LOOP
                ln();
                ln('### ' || cat.name);
                ln();

                FOR q IN c_qs(doc.id, cat.id) LOOP

                    IF    q.deviation   = 1 THEN v_icon := C_NOK;
                    ELSIF q.warning     = 1 THEN v_icon := C_WARN;
                    ELSIF q.result_type = 'UNKLAR' THEN v_icon := C_UNKL;
                    ELSE                            v_icon := C_OK;
                    END IF;


                    ln();
                    ln();
                    ln(v_icon || ' **' || q.question_text || '**');
                    ln();

                    IF q.answer IS NOT NULL AND DBMS_LOB.GETLENGTH(q.answer) > 0 THEN
                        ln(q.answer);
                        ln();
                    END IF;

                    IF q.score IS NOT NULL THEN
                        ln('*Score: ' || q.score || '%*');
                    END IF;

                    IF q.the_comment IS NOT NULL
                       AND DBMS_LOB.GETLENGTH(q.the_comment) > 0 THEN
                        ln();
                        ln('> ' || q.the_comment);
                    END IF;

                    ln();
                END LOOP;

            END LOOP;

            ln('---');
            ln();
        END LOOP;

        UPDATE dc_projects
        SET    report_markdown = v_md
        WHERE  id = p_project_id;

        DBMS_LOB.FREETEMPORARY(v_md);

        -- Mail-Versand (Fehler blockieren nie den Report-Build)
        BEGIN
            send_report_email(p_project_id);
        EXCEPTION WHEN OTHERS THEN NULL;
        END;

    EXCEPTION
        WHEN OTHERS THEN
            DBMS_LOB.FREETEMPORARY(v_md);
            RAISE;
    END generate_report;


    -- -------------------------------------------------------------------------
    PROCEDURE send_report_email(p_project_id IN NUMBER,
                                p_skip_pdf   IN BOOLEAN DEFAULT FALSE,
                                p_pdf        IN BLOB    DEFAULT NULL) IS

        v_email     dc_projects.notification_email%TYPE;
        v_name      dc_projects.name%TYPE;
        v_markdown  dc_projects.report_markdown%TYPE;
        v_done      dc_projects.completed_at%TYPE;
        v_mail_id   NUMBER;
        v_pdf       BLOB;
        v_body      VARCHAR2(4000);

        v_ok_cnt    NUMBER;
        v_warn_cnt  NUMBER;
        v_dev_cnt   NUMBER;
        v_total_all NUMBER;

        C_OK    CONSTANT VARCHAR2(10 CHAR) := UNISTR('\2705');       -- ✅
        C_NOK   CONSTANT VARCHAR2(10 CHAR) := UNISTR('\274C');       -- ❌
        C_WARN  CONSTANT VARCHAR2(10 CHAR) := UNISTR('\26A0\FE0F'); -- ⚠️

        CRLF CONSTANT VARCHAR2(2) := CHR(13) || CHR(10);

    BEGIN
        SELECT notification_email, name, report_markdown, completed_at
        INTO   v_email, v_name, v_markdown, v_done
        FROM   dc_projects
        WHERE  id = p_project_id;

        IF v_email IS NULL OR TRIM(v_email) IS NULL THEN RETURN; END IF;
        IF v_markdown IS NULL OR DBMS_LOB.GETLENGTH(v_markdown) = 0 THEN RETURN; END IF;

        -- Zusammenfassung aus dc_results (alle Ergebnisse, ungefiltert)
        SELECT COUNT(*),
               SUM(CASE WHEN deviation = 1                   THEN 1 ELSE 0 END),
               SUM(CASE WHEN warning   = 1 AND deviation = 0 THEN 1 ELSE 0 END)
        INTO   v_total_all, v_dev_cnt, v_warn_cnt
        FROM   dc_results
        WHERE  project_id = p_project_id;

        v_ok_cnt := v_total_all - v_dev_cnt - v_warn_cnt;

        -- =====================================================================
        -- E-Mail-Beschreibungstext
        -- =====================================================================
        v_body :=
            'Sehr geehrte Damen und Herren,' || CRLF ||
            CRLF ||
            'die automatische Dokumentenprüfung für das Projekt' || CRLF ||
            '„' || v_name || '"' || CRLF ||
            'wurde am ' || TO_CHAR(NVL(v_done, SYSDATE), 'DD.MM.YYYY') ||
            ' erfolgreich abgeschlossen.' || CRLF ||
            CRLF ||
            'Zusammenfassung der Prüfergebnisse:' || CRLF ||
            C_OK   || '  OK:              ' || v_ok_cnt    || CRLF ||
            C_WARN || '  Hinweise:        ' || v_warn_cnt  || CRLF ||
            C_NOK  || '  Abweichungen:    ' || v_dev_cnt   || CRLF ||
            '    Gesamt:         ' || v_total_all || ' Prüfpunkte' || CRLF ||
            CRLF ||
            'Den vollständigen Prüfbericht finden Sie als PDF im Anhang.' || CRLF ||
            CRLF ||
            'Direkt zur Prüfung in der Anwendung:' || CRLF ||
            c_app_link_base || p_project_id || CRLF ||
            CRLF ||
            'Mit freundlichen Grüßen' || CRLF ||
            'Ihr Dokumenten-Check-System' || CRLF ||
            'Frigosped Express GmbH';

        -- =====================================================================
        -- PDF bestimmen: Priorität p_pdf (von Java mitgeliefert) >
        -- convert_markdown HTTP-Callback > kein PDF (skip_pdf oder Fehler)
        -- =====================================================================
        IF p_pdf IS NOT NULL AND DBMS_LOB.GETLENGTH(p_pdf) > 0 THEN
            -- Java hat das PDF schon fertig mitgeschickt – direkt verwenden
            v_pdf := p_pdf;
        ELSIF NOT p_skip_pdf THEN
            BEGIN
                v_pdf := DC_BACKEND_PKG.convert_markdown(v_markdown, 'PDF');
            EXCEPTION WHEN OTHERS THEN
                DBMS_OUTPUT.PUT_LINE('PDF-Generierung fehlgeschlagen: ' || SQLERRM);
                v_pdf := NULL;
            END;
        END IF;

        IF v_pdf IS NOT NULL THEN
            -- Mail mit PDF-Anhang
            v_mail_id := APEX_MAIL.SEND(
                p_to   => v_email,
                p_from => c_mail_from,
                p_subj => 'Prüfbericht: ' || v_name,
                p_body => v_body
            );

            APEX_MAIL.ADD_ATTACHMENT(
                p_mail_id    => v_mail_id,
                p_attachment => v_pdf,
                p_filename   => 'Pruefbericht_' ||
                                REGEXP_REPLACE(v_name, '[^A-Za-z0-9_äöüÄÖÜß-]', '_') ||
                                '.pdf',
                p_mime_type  => 'application/pdf'
            );
        ELSE
            -- Fallback: Backend nicht erreichbar → Markdown direkt im Body
            APEX_MAIL.SEND(
                p_to   => v_email,
                p_from => c_mail_from,
                p_subj => 'Prüfbericht: ' || v_name,
                p_body => v_body || CRLF || CRLF ||
                          '--- Detaillierter Bericht ---' || CRLF || CRLF ||
                          v_markdown
            );
        END IF;

    EXCEPTION
        WHEN NO_DATA_FOUND THEN NULL;
    END send_report_email;

END DC_UTILS_PKG;
/


-- =============================================================================
-- Schnelltest (optional, auskommentiert)
-- =============================================================================
/*
BEGIN
    DC_UTILS_PKG.generate_report(p_project_id => 1);
    COMMIT;
    DBMS_OUTPUT.PUT_LINE('Bericht generiert.');
END;
/
*/
