-- =============================================================================
-- ORDS REST Services: Dokumenten-Check
-- Modul:     frigosped.dc
-- Base-Path: /api/dc/
-- =============================================================================
-- Voraussetzungen:
--   - Oracle Database 23c mit installiertem ORDS (>= 23.x)
--   - Script wird als Schema-Owner ausgeführt (hat ORDS_METADATA-Rolle)
--   - Datenbankschema bereits angelegt via "Datenmodell DocumentCheck.sql"
--
-- Deployment:
--   sqlplus user/pass@db @"Scripts/ORDS REST Services.sql"
--
-- Das Skript ist idempotent: bestehende Modul-Definition wird gelöscht und
-- neu erstellt. Bestehende Daten bleiben unberührt.
--
-- Endpunkt-Übersicht:
--   1.  GET  /api/dc/catalogs/                      Alle Fragenkataloge
--   2.  GET  /api/dc/catalogs/:id/questions/        Fragen eines Katalogs (flach)
--   3.  GET  /api/dc/projects/:id                   Projektdetail
--   4.  POST /api/dc/projects/:id/start             → IN_PROGRESS
--   5.  PUT  /api/dc/projects/:id/progress          Fortschritt aktualisieren
--   6.  POST /api/dc/projects/:id/complete          → COMPLETED
--   7.  GET  /api/dc/projects/:id/documents/        Dokument-Liste (kein BLOB)
--   8.  GET  /api/dc/documents/:id/file             BLOB-Download
--   9.  PUT  /api/dc/documents/:id/texts            OCR-Text + Übersetzung schreiben
--  10.  POST /api/dc/projects/:id/results           Prüfergebnis einfügen
--  11.  DELETE /api/dc/projects/:id/results         Alle Ergebnisse löschen
-- =============================================================================

-- =============================================================================
-- BLOCK 1: Modul, Templates und Handler
-- =============================================================================
BEGIN

  -- -------------------------------------------------------------------------
  -- Idempotenz: Modul löschen falls vorhanden
  -- -------------------------------------------------------------------------
  BEGIN
    ORDS.DELETE_MODULE(p_module_name => 'frigosped.dc');
  EXCEPTION
    WHEN OTHERS THEN NULL;
  END;

  -- -------------------------------------------------------------------------
  -- Modul definieren
  -- -------------------------------------------------------------------------
  ORDS.DEFINE_MODULE(
    p_module_name    => 'frigosped.dc',
    p_base_path      => '/api/dc/',
    p_items_per_page => 0,          -- Pagination deaktiviert (Quarkus steuert selbst)
    p_status         => 'PUBLISHED',
    p_comments       => 'Dokumenten-Check Backend-API fuer den Quarkus-Verarbeitungsserver'
  );


  -- ===========================================================================
  -- TEMPLATE 1: catalogs/
  -- ===========================================================================
  ORDS.DEFINE_TEMPLATE(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'catalogs/',
    p_priority    => 0,
    p_etag_type   => 'HASH',
    p_comments    => 'Alle Fragenkataloge mit Dokumenttyp-Name'
  );

  -- 1. GET /api/dc/catalogs/
  ORDS.DEFINE_HANDLER(
    p_module_name    => 'frigosped.dc',
    p_pattern        => 'catalogs/',
    p_method         => 'GET',
    p_source_type    => ORDS.SOURCE_TYPE_COLLECTION_FEED,
    p_items_per_page => 0,
    p_comments       => 'Gibt alle Kataloge mit Dokumenttyp-Name zurueck',
    p_source         => q'[
      SELECT
        c.id,
        c.name,
        c.description,
        c.document_type_id,
        dt.name   AS document_type_name,
        c.row_version,
        c.created,
        c.updated
      FROM   dc_question_catalogs  c
      JOIN   dc_document_types     dt ON dt.id = c.document_type_id
      ORDER  BY c.name
    ]'
  );


  -- ===========================================================================
  -- TEMPLATE 2: catalogs/:catalog_id/questions/
  -- ===========================================================================
  ORDS.DEFINE_TEMPLATE(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'catalogs/:catalog_id/questions/',
    p_priority    => 0,
    p_etag_type   => 'HASH',
    p_comments    => 'Alle Fragen eines Katalogs (flach mit Kategorie-Info)'
  );

  -- 2. GET /api/dc/catalogs/:catalog_id/questions/
  ORDS.DEFINE_HANDLER(
    p_module_name    => 'frigosped.dc',
    p_pattern        => 'catalogs/:catalog_id/questions/',
    p_method         => 'GET',
    p_source_type    => ORDS.SOURCE_TYPE_COLLECTION_FEED,
    p_items_per_page => 0,
    p_comments       => 'Flacher Join: Kategorie + Frage-Spalten, nach Kategorie/ID sortiert',
    p_source         => q'[
      SELECT
        q.id                  AS question_id,
        cat.id                AS category_id,
        cat.name              AS category_name,
        cat.description       AS category_description,
        q.question_text,
        q.evaluation_type,
        q.threshold,
        q.result_handling,
        q.example_0_percent,
        q.example_100_percent,
        q.row_version
      FROM   dc_questions           q
      JOIN   dc_question_categories cat ON cat.id = q.category_id
      WHERE  cat.catalog_id = :catalog_id
      ORDER  BY cat.name, q.id
    ]'
  );


  -- ===========================================================================
  -- TEMPLATE 3: projects/:project_id  (kein Trailing-Slash = Einzel-Ressource)
  -- ===========================================================================
  ORDS.DEFINE_TEMPLATE(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'projects/:project_id',
    p_priority    => 0,
    p_etag_type   => 'HASH',
    p_comments    => 'Einzelnes Projekt'
  );

  -- 3. GET /api/dc/projects/:project_id
  ORDS.DEFINE_HANDLER(
    p_module_name    => 'frigosped.dc',
    p_pattern        => 'projects/:project_id',
    p_method         => 'GET',
    p_source_type    => ORDS.SOURCE_TYPE_COLLECTION_ITEM,
    p_items_per_page => 1,
    p_comments       => 'Projektdetail als einzelnes JSON-Objekt; 404 wenn nicht gefunden',
    p_source         => q'[
      SELECT
        p.id,
        p.name,
        p.description,
        p.catalog_id,
        p.created_by_user,
        p.status,
        p.progress,
        p.completed_at,
        p.notification_email,
        p.row_version,
        p.created,
        p.updated
      FROM   dc_projects p
      WHERE  p.id = :project_id
    ]'
  );


  -- ===========================================================================
  -- TEMPLATE 4: projects/:project_id/start
  -- ===========================================================================
  ORDS.DEFINE_TEMPLATE(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'projects/:project_id/start',
    p_priority    => 0,
    p_comments    => 'Projekt-Status auf IN_PROGRESS setzen'
  );

  -- 4. POST /api/dc/projects/:project_id/start
  ORDS.DEFINE_HANDLER(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'projects/:project_id/start',
    p_method      => 'POST',
    p_source_type => ORDS.SOURCE_TYPE_PLSQL,
    p_comments    => '200 OK; 409 wenn Status nicht PENDING; 404 wenn Projekt fehlt',
    p_source      => q'[
      DECLARE
        v_status  dc_projects.status%TYPE;
      BEGIN
        SELECT status
        INTO   v_status
        FROM   dc_projects
        WHERE  id = :project_id
        FOR UPDATE NOWAIT;

        IF v_status != 'PENDING' THEN
          :status_code := 409;
          HTP.P('{"error":"Projekt ist nicht im Status PENDING",'
             || '"current_status":"' || v_status || '"}');
          RETURN;
        END IF;

        UPDATE dc_projects
        SET    status   = 'IN_PROGRESS',
               progress = 0
        WHERE  id = :project_id;

        :status_code := 200;
        HTP.P('{"project_id":' || :project_id
           || ',"status":"IN_PROGRESS"}');

      EXCEPTION
        WHEN NO_DATA_FOUND THEN
          :status_code := 404;
          HTP.P('{"error":"Projekt nicht gefunden","project_id":' || :project_id || '}');
        WHEN OTHERS THEN
          ROLLBACK;
          :status_code := 500;
          HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
      END;
    ]'
  );


  -- ===========================================================================
  -- TEMPLATE 5: projects/:project_id/progress
  -- ===========================================================================
  ORDS.DEFINE_TEMPLATE(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'projects/:project_id/progress',
    p_priority    => 0,
    p_comments    => 'Verarbeitungsfortschritt aktualisieren'
  );

  -- 5. PUT /api/dc/projects/:project_id/progress
  --    Body: {"progress": 45}
  ORDS.DEFINE_HANDLER(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'projects/:project_id/progress',
    p_method      => 'PUT',
    p_source_type => ORDS.SOURCE_TYPE_PLSQL,
    p_comments    => 'Setzt progress (0-100); 400 bei ungueltigem Wert',
    p_source      => q'[
      DECLARE
        v_progress  NUMBER;
        v_body      CLOB;
      BEGIN
        v_body     := :body_text;
        v_progress := TO_NUMBER(JSON_VALUE(v_body, '$.progress'));

        IF v_progress IS NULL OR v_progress < 0 OR v_progress > 100 THEN
          :status_code := 400;
          HTP.P('{"error":"progress muss eine Zahl zwischen 0 und 100 sein"}');
          RETURN;
        END IF;

        UPDATE dc_projects
        SET    progress = v_progress
        WHERE  id       = :project_id;

        IF SQL%ROWCOUNT = 0 THEN
          :status_code := 404;
          HTP.P('{"error":"Projekt nicht gefunden","project_id":' || :project_id || '}');
          RETURN;
        END IF;

        :status_code := 200;
        HTP.P('{"project_id":' || :project_id
           || ',"progress":' || v_progress || '}');

      EXCEPTION
        WHEN VALUE_ERROR THEN
          :status_code := 400;
          HTP.P('{"error":"progress muss eine gueltige Zahl sein"}');
        WHEN OTHERS THEN
          ROLLBACK;
          :status_code := 500;
          HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
      END;
    ]'
  );


  -- ===========================================================================
  -- TEMPLATE 6: projects/:project_id/complete
  -- ===========================================================================
  ORDS.DEFINE_TEMPLATE(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'projects/:project_id/complete',
    p_priority    => 0,
    p_comments    => 'Projekt auf COMPLETED setzen'
  );

  -- 6. POST /api/dc/projects/:project_id/complete
  ORDS.DEFINE_HANDLER(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'projects/:project_id/complete',
    p_method      => 'POST',
    p_source_type => ORDS.SOURCE_TYPE_PLSQL,
    p_comments    => 'Setzt status=COMPLETED, completed_at=SYSDATE, progress=100',
    p_source      => q'[
      DECLARE
        v_status dc_projects.status%TYPE;
        v_now    DATE := SYSDATE;
      BEGIN
        SELECT status
        INTO   v_status
        FROM   dc_projects
        WHERE  id = :project_id
        FOR UPDATE NOWAIT;

        IF v_status = 'COMPLETED' THEN
          :status_code := 409;
          HTP.P('{"error":"Projekt ist bereits COMPLETED","project_id":' || :project_id || '}');
          RETURN;
        END IF;

        UPDATE dc_projects
        SET    status       = 'COMPLETED',
               completed_at = v_now,
               progress     = 100
        WHERE  id = :project_id;

        :status_code := 200;
        HTP.P('{"project_id":' || :project_id
           || ',"status":"COMPLETED"'
           || ',"completed_at":"' || TO_CHAR(v_now, 'YYYY-MM-DD"T"HH24:MI:SS') || '"'
           || '}');

      EXCEPTION
        WHEN NO_DATA_FOUND THEN
          :status_code := 404;
          HTP.P('{"error":"Projekt nicht gefunden","project_id":' || :project_id || '}');
        WHEN OTHERS THEN
          ROLLBACK;
          :status_code := 500;
          HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
      END;
    ]'
  );


  -- ===========================================================================
  -- TEMPLATE 7: projects/:project_id/documents/
  -- ===========================================================================
  ORDS.DEFINE_TEMPLATE(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'projects/:project_id/documents/',
    p_priority    => 0,
    p_etag_type   => 'HASH',
    p_comments    => 'Dokument-Liste eines Projekts (ohne BLOB-Payload)'
  );

  -- 7. GET /api/dc/projects/:project_id/documents/
  ORDS.DEFINE_HANDLER(
    p_module_name    => 'frigosped.dc',
    p_pattern        => 'projects/:project_id/documents/',
    p_method         => 'GET',
    p_source_type    => ORDS.SOURCE_TYPE_COLLECTION_FEED,
    p_items_per_page => 0,
    p_comments       => 'Metadaten aller Dokumente; has_original_text/has_translated_text als 0/1',
    p_source         => q'[
      SELECT
        d.id,
        d.filename,
        d.mime_type,
        d.document_type_id,
        d.catalog_id,
        d.uploaded_at,
        CASE WHEN d.original_text   IS NOT NULL THEN 1 ELSE 0 END AS has_original_text,
        CASE WHEN d.translated_text IS NOT NULL THEN 1 ELSE 0 END AS has_translated_text,
        d.row_version,
        d.created,
        d.updated
      FROM   dc_project_documents d
      WHERE  d.project_id = :project_id
      ORDER  BY d.uploaded_at
    ]'
  );


  -- ===========================================================================
  -- TEMPLATE 8: documents/:doc_id/file
  -- ===========================================================================
  ORDS.DEFINE_TEMPLATE(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'documents/:doc_id/file',
    p_priority    => 0,
    p_comments    => 'BLOB-Download: original_file mit korrektem Content-Type'
  );

  -- 8. GET /api/dc/documents/:doc_id/file
  --    SOURCE_TYPE_MEDIA: Pflicht-Aliase: content, content_type, filename
  ORDS.DEFINE_HANDLER(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'documents/:doc_id/file',
    p_method      => 'GET',
    p_source_type => ORDS.SOURCE_TYPE_MEDIA,
    p_comments    => 'Streamt original_file BLOB; ORDS setzt Content-Type aus mime_type-Spalte',
    p_source      => q'[
      SELECT
        d.original_file   AS content,
        d.mime_type       AS content_type,
        d.filename        AS filename,
        d.updated         AS last_modified
      FROM   dc_project_documents d
      WHERE  d.id = :doc_id
    ]'
  );


  -- ===========================================================================
  -- TEMPLATE 9: documents/:doc_id/texts
  -- ===========================================================================
  ORDS.DEFINE_TEMPLATE(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'documents/:doc_id/texts',
    p_priority    => 0,
    p_comments    => 'OCR-Ergebnis und Uebersetzung zurueckschreiben'
  );

  -- 9. PUT /api/dc/documents/:doc_id/texts
  --    Body: {"original_text": "## ...", "translated_text": "## ..."}
  --    Beide Felder sind optional: NULL-Wert ueberschreibt nicht (nur wenn im Body vorhanden)
  ORDS.DEFINE_HANDLER(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'documents/:doc_id/texts',
    p_method      => 'PUT',
    p_source_type => ORDS.SOURCE_TYPE_PLSQL,
    p_comments    => 'Schreibt original_text und/oder translated_text als CLOB',
    p_source      => q'[
      DECLARE
        v_body           CLOB;
        v_original_text  CLOB;
        v_translated     CLOB;
        v_rows           INTEGER;
      BEGIN
        v_body          := :body_text;
        -- RETURNING CLOB erlaubt Werte groesser als 32767 Bytes (wichtig fuer OCR-Output)
        v_original_text := JSON_VALUE(v_body, '$.original_text'   RETURNING CLOB);
        v_translated    := JSON_VALUE(v_body, '$.translated_text' RETURNING CLOB);

        UPDATE dc_project_documents
        SET    original_text   = CASE
                                   WHEN JSON_EXISTS(v_body, '$.original_text')
                                   THEN v_original_text
                                   ELSE original_text
                                 END,
               translated_text = CASE
                                   WHEN JSON_EXISTS(v_body, '$.translated_text')
                                   THEN v_translated
                                   ELSE translated_text
                                 END
        WHERE  id = :doc_id;

        v_rows := SQL%ROWCOUNT;
        IF v_rows = 0 THEN
          :status_code := 404;
          HTP.P('{"error":"Dokument nicht gefunden","doc_id":' || :doc_id || '}');
          RETURN;
        END IF;

        :status_code := 200;
        HTP.P('{"doc_id":' || :doc_id || ',"updated":true}');

      EXCEPTION
        WHEN OTHERS THEN
          ROLLBACK;
          :status_code := 500;
          HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
      END;
    ]'
  );


  -- ===========================================================================
  -- TEMPLATE 10+11: projects/:project_id/results
  -- (POST = Einfuegen, DELETE = Alle loeschen)
  -- ===========================================================================
  ORDS.DEFINE_TEMPLATE(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'projects/:project_id/results',
    p_priority    => 0,
    p_comments    => 'Prüfergebnisse: POST einfuegen, DELETE alle loeschen'
  );

  -- 10. POST /api/dc/projects/:project_id/results
  --     Body: {
  --       "question_id": 5,
  --       "doc_id": 101,
  --       "answer": "Ja, §305 BGB ist erfuellt.",
  --       "score": 85,
  --       "result_type": "OK",
  --       "deviation": 0,
  --       "warning": 0,
  --       "the_comment": "Klare Einbeziehungsklausel vorhanden"
  --     }
  ORDS.DEFINE_HANDLER(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'projects/:project_id/results',
    p_method      => 'POST',
    p_source_type => ORDS.SOURCE_TYPE_PLSQL,
    p_comments    => '201 Created mit Location-Header; 400 bei fehlendem question_id/doc_id/result_type',
    p_source      => q'[
      DECLARE
        v_body        CLOB;
        v_question_id NUMBER;
        v_doc_id      NUMBER;
        v_answer      CLOB;
        v_score       NUMBER;
        v_result_type VARCHAR2(20);
        v_deviation   NUMBER;
        v_warning     NUMBER;
        v_comment     CLOB;
        v_new_id      NUMBER;
      BEGIN
        v_body        := :body_text;
        v_question_id := TO_NUMBER(JSON_VALUE(v_body, '$.question_id'));
        v_doc_id      := TO_NUMBER(JSON_VALUE(v_body, '$.doc_id'));
        v_answer      := JSON_VALUE(v_body, '$.answer'       RETURNING CLOB);
        v_score       := TO_NUMBER(JSON_VALUE(v_body, '$.score'));
        v_result_type := JSON_VALUE(v_body, '$.result_type');
        v_deviation   := NVL(TO_NUMBER(JSON_VALUE(v_body, '$.deviation')), 0);
        v_warning     := NVL(TO_NUMBER(JSON_VALUE(v_body, '$.warning')),   0);
        v_comment     := JSON_VALUE(v_body, '$.the_comment'  RETURNING CLOB);

        -- Pflichtfelder prüfen
        IF v_question_id IS NULL OR v_doc_id IS NULL THEN
          :status_code := 400;
          HTP.P('{"error":"question_id und doc_id sind Pflichtfelder"}');
          RETURN;
        END IF;

        IF v_result_type NOT IN ('OK', 'UNKLAR', 'NOK') THEN
          :status_code := 400;
          HTP.P('{"error":"result_type muss OK, UNKLAR oder NOK sein",'
             || '"received":"' || NVL(v_result_type, 'NULL') || '"}');
          RETURN;
        END IF;

        INSERT INTO dc_results (
          project_id,
          question_id,
          doc_id,
          answer,
          score,
          result_type,
          deviation,
          warning,
          the_comment,
          evaluated_at
        ) VALUES (
          :project_id,
          v_question_id,
          v_doc_id,
          v_answer,
          v_score,
          v_result_type,
          v_deviation,
          v_warning,
          v_comment,
          SYSDATE
        )
        RETURNING id INTO v_new_id;

        -- Location-Header gemaess REST-Konvention
        ORDS.SET_RESPONSE_HEADER(
          'Location',
          '/api/dc/projects/' || :project_id || '/results/' || v_new_id
        );

        :status_code := 201;
        HTP.P('{"id":'          || v_new_id
           || ',"project_id":'  || :project_id
           || ',"question_id":' || v_question_id
           || ',"doc_id":'      || v_doc_id
           || ',"result_type":"' || v_result_type || '"'
           || ',"score":'       || NVL(TO_CHAR(v_score), 'null')
           || '}');

      EXCEPTION
        WHEN OTHERS THEN
          ROLLBACK;
          :status_code := 500;
          HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
      END;
    ]'
  );

  -- 11. DELETE /api/dc/projects/:project_id/results
  ORDS.DEFINE_HANDLER(
    p_module_name => 'frigosped.dc',
    p_pattern     => 'projects/:project_id/results',
    p_method      => 'DELETE',
    p_source_type => ORDS.SOURCE_TYPE_PLSQL,
    p_comments    => 'Loescht alle Ergebnisse eines Projekts (fuer Re-Processing)',
    p_source      => q'[
      DECLARE
        v_deleted INTEGER;
      BEGIN
        DELETE FROM dc_results
        WHERE  project_id = :project_id;

        v_deleted := SQL%ROWCOUNT;

        :status_code := 200;
        HTP.P('{"project_id":' || :project_id
           || ',"deleted_count":' || v_deleted || '}');

      EXCEPTION
        WHEN OTHERS THEN
          ROLLBACK;
          :status_code := 500;
          HTP.P('{"error":"' || REPLACE(SQLERRM, '"', '''') || '"}');
      END;
    ]'
  );

  COMMIT;
END;
/


-- =============================================================================
-- BLOCK 2: Auth / Privilege (fuer Produktion)
-- =============================================================================
-- In der Entwicklung kann dieser Block auskommentiert bleiben – das Modul
-- ist dann ohne Auth-Pruefung erreichbar (STATUS = PUBLISHED genuegt).
--
-- Fuer Produktion einkommentieren und danach den OAuth2-Client anlegen:
--
--   SELECT client_id, client_secret
--   FROM   user_ords_clients
--   WHERE  name = 'quarkus_dc_backend';
--
-- Den client_id/client_secret in die Quarkus application.properties eintragen.
-- =============================================================================
/*
BEGIN
  -- Rolle anlegen (falls noch nicht vorhanden)
  BEGIN
    ORDS.CREATE_ROLE(p_role_name => 'dc_api_user');
  EXCEPTION
    WHEN OTHERS THEN NULL;
  END;

  -- Privilege loeschen falls vorhanden
  BEGIN
    ORDS.DROP_PRIVILEGE(p_name => 'dc.api.privilege');
  EXCEPTION
    WHEN OTHERS THEN NULL;
  END;

  -- Privilege definieren: mappt Rolle auf alle /api/dc/-Endpunkte
  ORDS.DEFINE_PRIVILEGE(
    p_privilege_name => 'dc.api.privilege',
    p_roles          => ORDS_TYPES.T_ORDS_VARCHARS('dc_api_user'),
    p_patterns       => ORDS_TYPES.T_ORDS_VARCHARS('/api/dc/*'),
    p_module_name    => 'frigosped.dc',
    p_label          => 'Dokumenten-Check API',
    p_description    => 'Zugriff auf alle /api/dc/-Endpunkte',
    p_comments       => 'Wird dem OAuth2-Client quarkus_dc_backend zugewiesen'
  );

  COMMIT;
END;
/

-- OAuth2-Client fuer den Quarkus-Server (einmalig ausfuehren)
BEGIN
  OAUTH.CREATE_CLIENT(
    p_name            => 'quarkus_dc_backend',
    p_grant_type      => 'CLIENT_CREDENTIALS',
    p_owner           => 'Frigosped',
    p_description     => 'Quarkus-Verarbeitungsserver Service-Account',
    p_redirect_uri    => NULL,
    p_support_email   => 'admin@frigosped.de',
    p_privilege_names => 'dc.api.privilege'
  );
  COMMIT;
END;
/
*/


-- =============================================================================
-- Test-Aufrufe (curl)
-- =============================================================================
-- Ersetze <BASE> und <SCHEMA> mit deinen ORDS-Werten, z.B.:
--   BASE="https://apex.example.com/ords/FRIGOSPED_APP"
--
-- Ohne Auth (Entwicklung):
--
--   # 1. Alle Kataloge
--   curl -sS "$BASE/api/dc/catalogs/" | jq .
--
--   # 2. Fragen fuer Katalog 1
--   curl -sS "$BASE/api/dc/catalogs/1/questions/" | jq .
--
--   # 3. Projektdetail
--   curl -sS "$BASE/api/dc/projects/42" | jq .
--
--   # 4. Projekt starten
--   curl -sS -X POST "$BASE/api/dc/projects/42/start" | jq .
--
--   # 5. Fortschritt setzen
--   curl -sS -X PUT -H "Content-Type: application/json" \
--        -d '{"progress":35}' "$BASE/api/dc/projects/42/progress" | jq .
--
--   # 6. Projekt abschliessen
--   curl -sS -X POST "$BASE/api/dc/projects/42/complete" | jq .
--
--   # 7. Dokumente auflisten
--   curl -sS "$BASE/api/dc/projects/42/documents/" | jq .
--
--   # 8. Datei herunterladen
--   curl -sS -o original.pdf "$BASE/api/dc/documents/101/file"
--
--   # 9. OCR-Text + Uebersetzung zurueckschreiben
--   curl -sS -X PUT -H "Content-Type: application/json" \
--        -d '{"original_text":"## Vertrag\n\nAbsatz 1...",
--             "translated_text":"## Vertrag (DE)\n\nAbsatz 1..."}' \
--        "$BASE/api/dc/documents/101/texts" | jq .
--
--   # 10. Ergebnis einfuegen
--   curl -sS -X POST -H "Content-Type: application/json" \
--        -d '{"question_id":5,"doc_id":101,"answer":"Ja, §305 erfuellt.",
--             "score":85,"result_type":"OK","deviation":0,"warning":0,
--             "the_comment":"Klare Einbeziehungsklausel vorhanden"}' \
--        "$BASE/api/dc/projects/42/results" | jq .
--
--   # 11. Alle Ergebnisse loeschen (Re-Processing)
--   curl -sS -X DELETE "$BASE/api/dc/projects/42/results" | jq .
-- =============================================================================
