-- Generate one reusable, question-specific Few-shot pattern per customer QA
-- benchmark without promoting the benchmark answer itself.  Game identity is
-- deliberately not inferred here: sg_game_query_plan owns that through OCI
-- GenAI chat + the current game catalog.

CREATE OR REPLACE FUNCTION sg_qa_genai_generalize_pattern(
  p_question   IN CLOB,
  p_answer_sql IN CLOB,
  p_target_type IN VARCHAR2
) RETURN CLOB AUTHID DEFINER
IS
  v_prompt CLOB;
  v_result CLOB;
BEGIN
  v_prompt :=
    'Create one reusable, question-specific Few-shot SQL pattern from the source example. '
    || 'This is training guidance, never an answer key. Return exactly these tagged sections and nothing else: '
    || '[[PATTERN_QUESTION]], [[STRUCTURAL_SQL_PATTERN]], [[OBJECT_ROLE]], [[TARGET_TYPE]], '
    || '[[APPLICABILITY]], [[END]]. '
    || 'Preserve only the query intent and structural operations such as aggregation, joins, '
    || 'grouping, ordering, date semantics, and filters. Replace every game name, alias, game ID, '
    || 'schema name, physical object name, column name, literal date, literal number, user ID, '
    || 'currency amount, and expected output with semantic placeholders such as <GAME_SCOPE>, '
    || '<LOGICAL_FACT>, <LOGICAL_DIMENSION>, <METRIC>, <AS_OF_DATE>, <FILTER>, and <GROUPING>. '
    || 'In STRUCTURAL_SQL_PATTERN, every non-SQL identifier must be an angle-bracket placeholder: '
    || 'do not retain any source column, alias, table, schema, literal, code, or business value. '
    || 'Do not include executable SQL. Do not include a game name or a customer answer. '
    || 'The current game scope is supplied separately at runtime by a database OCI GenAI chat '
    || 'resolver, therefore never choose or imply a game. The TARGET_TYPE section must be one of NONE, '
    || 'SINGLE, MULTI, ALL, ANY and must describe applicability, not a game identity. '
    || 'Source target type from the current resolver: ' || NVL(p_target_type, 'ANY') || CHR(10)
    || 'Source question:' || CHR(10) || DBMS_LOB.SUBSTR(p_question, 4000, 1) || CHR(10)
    || 'Source SQL (structure only; do not copy identifiers or values):' || CHR(10)
    || DBMS_LOB.SUBSTR(p_answer_sql, 12000, 1);
  v_result := DBMS_CLOUD_AI.GENERATE(
    prompt => v_prompt,
    profile_name => 'SGMP_POC_OCI_GPT54MINI',
    action => 'chat'
  );
  RETURN v_result;
END;
/

CREATE OR REPLACE FUNCTION sg_qa_genai_validate_pattern(
  p_pattern_json IN CLOB
) RETURN CLOB AUTHID DEFINER
IS
  v_prompt CLOB;
  v_result CLOB;
BEGIN
  v_prompt :=
    'Inspect only concrete-answer leakage in this reusable Few-shot pattern. Return exactly '
    || '[[CONCRETE_LEAKAGE]] YES or NO, then [[REASON]] and a short reason, then [[END]]. '
    || 'Return YES only when a customer answer, concrete game identity, physical schema/table/column '
    || 'identifier, literal date, literal business result, or executable SQL against a real object remains. '
    || 'Return NO when all such references are semantic angle-bracket placeholders. A pseudo-SQL pattern '
    || 'using SELECT/FROM/JOIN/GROUP BY, generic game-scope checks, EXISTS, UNION, or equality with '
    || 'angle-bracket placeholders is not concrete leakage and must return NO. Do not judge usefulness or '
    || 'completeness; classify leakage only. '
    || 'Candidate:' || CHR(10) || DBMS_LOB.SUBSTR(p_pattern_json, 16000, 1);
  v_result := DBMS_CLOUD_AI.GENERATE(
    prompt => v_prompt,
    profile_name => 'SGMP_POC_OCI_GPT54MINI',
    action => 'chat'
  );
  RETURN v_result;
END;
/

CREATE OR REPLACE FUNCTION sg_qa_generate_generalized_patterns
  RETURN NUMBER AUTHID DEFINER
IS
  PRAGMA AUTONOMOUS_TRANSACTION;
  v_plan_raw       CLOB;
  v_plan            JSON_OBJECT_T;
  v_target_type     VARCHAR2(16);
  v_pattern_raw     CLOB;
  v_validation_raw  CLOB;
  v_status          VARCHAR2(16);
  v_validation_note CLOB;
  v_question        CLOB;
  v_sql_pattern     CLOB;
  v_answer_text     CLOB;
  v_object_role     VARCHAR2(64);
  v_embedding_input CLOB;
  v_embedding       VECTOR;
  v_count           NUMBER := 0;

  FUNCTION parse_json_result(p_value CLOB) RETURN JSON_OBJECT_T IS
    v_text CLOB := TRIM(p_value);
  BEGIN
    IF DBMS_LOB.SUBSTR(v_text, 7, 1) = '```json' THEN
      v_text := REGEXP_REPLACE(v_text, '^```json[[:space:]]*', '');
      v_text := REGEXP_REPLACE(v_text, '[[:space:]]*```[[:space:]]*$', '');
    ELSIF DBMS_LOB.SUBSTR(v_text, 3, 1) = '```' THEN
      v_text := REGEXP_REPLACE(v_text, '^```[[:space:]]*', '');
      v_text := REGEXP_REPLACE(v_text, '[[:space:]]*```[[:space:]]*$', '');
    END IF;
    RETURN JSON_OBJECT_T.parse(v_text);
  END;

  FUNCTION section_value(
    p_raw IN CLOB, p_start_tag IN VARCHAR2, p_end_tag IN VARCHAR2
  ) RETURN CLOB IS
    v_start PLS_INTEGER;
    v_end PLS_INTEGER;
  BEGIN
    v_start := DBMS_LOB.INSTR(p_raw, p_start_tag, 1, 1);
    IF v_start = 0 THEN
      RAISE_APPLICATION_ERROR(-20071, 'OCI GenAI response is missing ' || p_start_tag);
    END IF;
    v_start := v_start + LENGTH(p_start_tag);
    v_end := DBMS_LOB.INSTR(p_raw, p_end_tag, v_start, 1);
    IF v_end = 0 OR v_end <= v_start THEN
      RAISE_APPLICATION_ERROR(-20072, 'OCI GenAI response is missing ' || p_end_tag);
    END IF;
    RETURN TRIM(DBMS_LOB.SUBSTR(p_raw, LEAST(v_end - v_start, 32767), v_start));
  END;

  PROCEDURE upsert_pattern(
    p_case_id IN VARCHAR2,
    p_status IN VARCHAR2,
    p_note IN CLOB
  ) IS
  BEGIN
    UPDATE sg_qa_vector_example
       SET question = v_question,
           answer_sql = v_sql_pattern,
           answer_text = v_answer_text,
           embedding_input = v_embedding_input,
           embedding = v_embedding,
           embedding_model = 'cohere.embed-v4.0',
           reference_status = p_status,
           reference_kind = 'SQL_PATTERN',
           target_type = v_target_type,
           object_role = v_object_role,
           inspection_status = CASE WHEN p_status = 'APPROVED' THEN 'GENAI_VERIFIED' ELSE 'GENAI_REJECTED' END,
           inspection_note = p_note,
           verified_at = SYSTIMESTAMP,
           verified_by = 'SGMP_POC_OCI_GENAI_PATTERN'
     WHERE source_type = 'GENERALIZED_QUESTION_PATTERN'
       AND source_case_id = 'PAT-' || p_case_id;

    IF SQL%ROWCOUNT = 0 THEN
      INSERT INTO sg_qa_vector_example (
        question, answer_sql, answer_text, embedding_input, embedding, embedding_model,
        reference_status, reference_kind, target_type, object_role, inspection_status,
        inspection_note, verified_at, verified_by, source_case_id, source_type
      ) VALUES (
        v_question, v_sql_pattern, v_answer_text, v_embedding_input, v_embedding, 'cohere.embed-v4.0',
        p_status, 'SQL_PATTERN', v_target_type, v_object_role,
        CASE WHEN p_status = 'APPROVED' THEN 'GENAI_VERIFIED' ELSE 'GENAI_REJECTED' END,
        p_note, SYSTIMESTAMP, 'SGMP_POC_OCI_GENAI_PATTERN',
        'PAT-' || p_case_id, 'GENERALIZED_QUESTION_PATTERN'
      );
    END IF;
  END;
BEGIN
  FOR source_row IN (
    SELECT source.source_case_id, source.question, source.answer_sql
      FROM sg_qa_vector_example source
     WHERE source.source_type = 'CUSTOMER_QA_BENCHMARK'
       AND NOT EXISTS (
         SELECT 1
           FROM sg_qa_vector_example pattern
          WHERE pattern.source_type = 'GENERALIZED_QUESTION_PATTERN'
            AND pattern.source_case_id = 'PAT-' || source.source_case_id
            AND pattern.reference_status = 'APPROVED'
       )
     ORDER BY source_case_id
  ) LOOP
    BEGIN
      -- The target category comes from the existing OCI GenAI game resolver;
      -- no alias, prefix, table, or name is transformed in this migration.
      v_plan_raw := sg_game_query_plan(source_row.question, 5);
      v_plan := parse_json_result(v_plan_raw);
      v_target_type := UPPER(NVL(v_plan.get_string('targetType'), 'ANY'));
      IF v_target_type NOT IN ('NONE', 'SINGLE', 'MULTI', 'ALL') THEN
        v_target_type := 'ANY';
      END IF;

      v_pattern_raw := sg_qa_genai_generalize_pattern(
        source_row.question, source_row.answer_sql, v_target_type
      );
      v_question := section_value(v_pattern_raw, '[[PATTERN_QUESTION]]', '[[STRUCTURAL_SQL_PATTERN]]');
      v_sql_pattern := section_value(v_pattern_raw, '[[STRUCTURAL_SQL_PATTERN]]', '[[OBJECT_ROLE]]');
      v_object_role := SUBSTR(section_value(v_pattern_raw, '[[OBJECT_ROLE]]', '[[TARGET_TYPE]]'), 1, 64);
      IF section_value(v_pattern_raw, '[[TARGET_TYPE]]', '[[APPLICABILITY]]')
          IN ('NONE', 'SINGLE', 'MULTI', 'ALL', 'ANY') THEN
        v_target_type := section_value(v_pattern_raw, '[[TARGET_TYPE]]', '[[APPLICABILITY]]');
      END IF;
      v_answer_text := TO_CLOB('Generalized, question-specific structural pattern. '
        || 'Current game scope must be supplied only by sg_game_query_plan. Applicability: ')
        || section_value(v_pattern_raw, '[[APPLICABILITY]]', '[[END]]');
      v_embedding_input := TO_CLOB('Question-specific generalized Few-shot pattern:' || CHR(10))
        || v_question || CHR(10) || 'Logical role: ' || v_object_role || CHR(10)
        || 'Structural SQL pattern:' || CHR(10) || v_sql_pattern;
      v_embedding := DBMS_VECTOR.UTL_TO_EMBEDDING(
        v_embedding_input, JSON(sg_qa_vector_params('search_document'))
      );
      v_validation_raw := sg_qa_genai_validate_pattern(v_pattern_raw);
      v_status := CASE
        WHEN REGEXP_SUBSTR(
          UPPER(section_value(v_validation_raw, '[[CONCRETE_LEAKAGE]]', '[[REASON]]')),
          '[A-Z]+'
        ) = 'NO'
          THEN 'APPROVE'
        ELSE 'REJECT'
      END;
      v_validation_note := section_value(v_validation_raw, '[[REASON]]', '[[END]]');

      IF v_status = 'APPROVE' THEN
        upsert_pattern(source_row.source_case_id, 'APPROVED',
          'ADB OCI GenAI generated and independently validated a generalized pattern. '
          || 'The original customer QA remains evaluation-only. ' || v_validation_note);
        v_count := v_count + 1;
      ELSE
        upsert_pattern(source_row.source_case_id, 'DRAFT',
          'ADB OCI GenAI rejected the generalized pattern: ' || v_validation_note);
      END IF;
    EXCEPTION
      WHEN OTHERS THEN
        -- Persist an auditable non-runtime draft and continue with the other
        -- customer questions; one malformed LLM response must not block all 47.
        v_question := source_row.question;
        v_sql_pattern := TO_CLOB('<PATTERN_GENERATION_FAILED>');
        v_answer_text := TO_CLOB('No runtime Few-shot pattern: OCI GenAI generalization failed.');
        v_object_role := 'UNSPECIFIED';
        v_target_type := 'ANY';
        v_embedding_input := TO_CLOB('Failed generalized pattern: ') || source_row.question;
        v_embedding := DBMS_VECTOR.UTL_TO_EMBEDDING(
          v_embedding_input, JSON(sg_qa_vector_params('search_document'))
        );
        upsert_pattern(source_row.source_case_id, 'DRAFT',
          'OCI GenAI pattern generation error: ' || SQLERRM);
    END;
  END LOOP;
  COMMIT;
  RETURN v_count;
EXCEPTION
  WHEN OTHERS THEN
    ROLLBACK;
    RAISE;
END;
/

-- Re-run only the independent OCI Chat safety review after its policy changes.
-- It never reads a customer benchmark and never changes the generated pattern.
CREATE OR REPLACE FUNCTION sg_qa_revalidate_generalized_patterns
  RETURN NUMBER AUTHID DEFINER
IS
  PRAGMA AUTONOMOUS_TRANSACTION;
  v_raw CLOB;
  v_status VARCHAR2(16);
  v_reason CLOB;
  v_start PLS_INTEGER;
  v_end PLS_INTEGER;
  v_count NUMBER := 0;

  FUNCTION section_value(
    p_raw IN CLOB, p_start_tag IN VARCHAR2, p_end_tag IN VARCHAR2
  ) RETURN CLOB IS
    v_from PLS_INTEGER;
    v_to PLS_INTEGER;
  BEGIN
    v_from := DBMS_LOB.INSTR(p_raw, p_start_tag, 1, 1);
    IF v_from = 0 THEN RAISE_APPLICATION_ERROR(-20073, 'Missing ' || p_start_tag); END IF;
    v_from := v_from + LENGTH(p_start_tag);
    v_to := DBMS_LOB.INSTR(p_raw, p_end_tag, v_from, 1);
    IF v_to = 0 OR v_to <= v_from THEN RAISE_APPLICATION_ERROR(-20074, 'Missing ' || p_end_tag); END IF;
    RETURN TRIM(DBMS_LOB.SUBSTR(p_raw, LEAST(v_to - v_from, 32767), v_from));
  END;
BEGIN
  FOR item IN (
    SELECT example_id, question, answer_sql, answer_text
      FROM sg_qa_vector_example
     WHERE source_type = 'GENERALIZED_QUESTION_PATTERN'
     ORDER BY source_case_id
  ) LOOP
    BEGIN
      v_raw := sg_qa_genai_validate_pattern(
        TO_CLOB('[[PATTERN_QUESTION]]') || item.question
        || TO_CLOB(CHR(10) || '[[STRUCTURAL_SQL_PATTERN]]') || item.answer_sql
        || TO_CLOB(CHR(10) || '[[APPLICABILITY]]') || item.answer_text || CHR(10) || '[[END]]'
      );
      v_status := CASE
        WHEN REGEXP_SUBSTR(
          UPPER(section_value(v_raw, '[[CONCRETE_LEAKAGE]]', '[[REASON]]')),
          '[A-Z]+'
        ) = 'NO'
          THEN 'APPROVE'
        ELSE 'REJECT'
      END;
      v_reason := section_value(v_raw, '[[REASON]]', '[[END]]');
      UPDATE sg_qa_vector_example
         SET reference_status = CASE WHEN v_status = 'APPROVE' THEN 'APPROVED' ELSE 'DRAFT' END,
             inspection_status = CASE WHEN v_status = 'APPROVE' THEN 'GENAI_VERIFIED' ELSE 'GENAI_REJECTED' END,
             inspection_note = 'ADB OCI GenAI independent revalidation: ' || v_reason,
             verified_at = SYSTIMESTAMP,
             verified_by = 'SGMP_POC_OCI_GENAI_PATTERN'
       WHERE example_id = item.example_id;
      IF v_status = 'APPROVE' THEN v_count := v_count + 1; END IF;
    EXCEPTION
      WHEN OTHERS THEN
        v_reason := TO_CLOB('OCI GenAI revalidation error: ' || SQLERRM);
        UPDATE sg_qa_vector_example
           SET reference_status = 'DRAFT',
               inspection_status = 'GENAI_REJECTED',
               inspection_note = v_reason,
               verified_at = SYSTIMESTAMP,
               verified_by = 'SGMP_POC_OCI_GENAI_PATTERN'
         WHERE example_id = item.example_id;
    END;
  END LOOP;
  COMMIT;
  RETURN v_count;
EXCEPTION
  WHEN OTHERS THEN
    ROLLBACK;
    RAISE;
END;
/

-- Production retrieval accepts only independently generalized patterns or
-- policy templates. Customer QA benchmarks remain evaluation-only forever.
CREATE OR REPLACE FUNCTION sg_qa_vector_search(
  p_question    IN CLOB,
  p_top_k       IN PLS_INTEGER DEFAULT 3,
  p_target_type IN VARCHAR2 DEFAULT 'ANY'
) RETURN SYS_REFCURSOR AUTHID DEFINER
IS
  v_query_vector VECTOR;
  v_results SYS_REFCURSOR;
  v_target_type VARCHAR2(16) := UPPER(TRIM(NVL(p_target_type, 'ANY')));
BEGIN
  IF p_question IS NULL THEN RAISE_APPLICATION_ERROR(-20003, 'question is required.'); END IF;
  IF p_top_k IS NULL OR p_top_k < 1 OR p_top_k > 20 THEN
    RAISE_APPLICATION_ERROR(-20004, 'top_k must be between 1 and 20.');
  END IF;
  IF v_target_type NOT IN ('NONE', 'SINGLE', 'MULTI', 'ALL', 'ANY') THEN
    RAISE_APPLICATION_ERROR(-20005, 'invalid target type.');
  END IF;
  v_query_vector := DBMS_VECTOR.UTL_TO_EMBEDDING(
    p_question, JSON(sg_qa_vector_params('search_query'))
  );
  OPEN v_results FOR
    SELECT example_id, question, answer_sql, answer_text, embedding_model,
           reference_kind, target_type, object_role, source_case_id, source_type,
           vector_distance(embedding, v_query_vector, COSINE) AS cosine_distance
      FROM sg_qa_vector_example
     WHERE reference_status = 'APPROVED'
       AND source_type IN ('GENERALIZED_QUESTION_PATTERN', 'POLICY_TEMPLATE')
       AND (target_type = 'ANY' OR v_target_type = 'ANY' OR target_type = v_target_type)
     ORDER BY vector_distance(embedding, v_query_vector, COSINE), example_id
     FETCH FIRST p_top_k ROWS ONLY;
  RETURN v_results;
END;
/
