-- DB-backed game query scope contract for MCP orchestration.
--
-- This script deliberately keeps game facts in database rows, not in application
-- code or Select AI instructions.  The view combines the active game alias source
-- with an operator-maintained registry for known games that currently have no
-- approved query object.  A zero-row business result is still queryable; only the
-- absence of an approved object makes a game scope unavailable.

DECLARE
  v_count PLS_INTEGER;
BEGIN
  SELECT COUNT(*) INTO v_count
    FROM user_tables
   WHERE table_name = 'SG_GAME_SCOPE_REGISTRY';

  IF v_count = 0 THEN
    EXECUTE IMMEDIATE q'[
      CREATE TABLE sg_game_scope_registry (
        game_key       VARCHAR2(128) NOT NULL,
        display_name   VARCHAR2(200) NOT NULL,
        game_alias     VARCHAR2(200) NOT NULL,
        game_prefix    VARCHAR2(30),
        active_yn      VARCHAR2(1) DEFAULT 'Y' NOT NULL,
        alias_priority NUMBER(10) DEFAULT 100 NOT NULL,
        source_type    VARCHAR2(30) DEFAULT 'OPERATOR' NOT NULL,
        work_dtm       TIMESTAMP(6) DEFAULT SYSTIMESTAMP NOT NULL,
        CONSTRAINT sg_game_scope_registry_pk PRIMARY KEY (game_key, game_alias),
        CONSTRAINT sg_game_scope_registry_active_ck CHECK (active_yn IN ('Y', 'N'))
      )]';
  END IF;
END;
/

-- Seed only database facts needed to recognise currently unavailable games in
-- the customer QA catalogue.  Customer game-master synchronization can replace
-- these rows without an application deployment.
MERGE INTO sg_game_scope_registry t
USING (
  SELECT 'LORDNINE' AS game_key,
         utl_i18n.raw_to_char(
           utl_encode.base64_decode(utl_raw.cast_to_raw('66Gc65Oc64KY7J24')),
           'AL32UTF8'
         ) AS display_name,
         utl_i18n.raw_to_char(
           utl_encode.base64_decode(utl_raw.cast_to_raw('66Gc65Oc64KY7J24')),
           'AL32UTF8'
         ) AS game_alias,
         CAST(NULL AS VARCHAR2(30)) AS game_prefix,
         100 AS alias_priority
    FROM dual
  UNION ALL
  SELECT 'BUBBLYZ', 'Bubblyz', 'Bubblyz', CAST(NULL AS VARCHAR2(30)), 100 FROM dual
) s
ON (t.game_key = s.game_key AND t.game_alias = s.game_alias)
WHEN MATCHED THEN UPDATE SET
  t.display_name = s.display_name,
  t.game_prefix = s.game_prefix,
  t.active_yn = 'Y',
  t.alias_priority = s.alias_priority,
  t.source_type = 'OPERATOR',
  t.work_dtm = SYSTIMESTAMP
WHEN NOT MATCHED THEN INSERT (
  game_key, display_name, game_alias, game_prefix, active_yn, alias_priority, source_type
) VALUES (
  s.game_key, s.display_name, s.game_alias, s.game_prefix, 'Y', s.alias_priority, 'OPERATOR'
);
/

CREATE OR REPLACE VIEW sg_game_query_scope_v AS
WITH existing_query_objects AS (
  -- USER_OBJECTS is the authoritative current-schema inventory.  The profile
  -- object list alone is not enough because a stale entry must not make a game
  -- executable after its table or view has been removed or invalidated.
  SELECT object_name
    FROM user_objects
   WHERE object_type IN ('TABLE', 'VIEW')
     AND status = 'VALID'
),
profile_names AS (
  SELECT DISTINCT profile_name
    FROM user_cloud_ai_profile_attributes
   WHERE attribute_name = 'object_list'
),
profile_objects AS (
  SELECT DISTINCT p.profile_name, o.object_name
    FROM user_cloud_ai_profile_attributes p,
         JSON_TABLE(
           p.attribute_value,
           '$[*]' COLUMNS (object_name VARCHAR2(128) PATH '$.name')
         ) o
    INNER JOIN existing_query_objects e
      ON e.object_name = o.object_name
   WHERE p.attribute_name = 'object_list'
),
source_alias AS (
  SELECT game_id AS game_key,
         game_nm AS display_name,
         game_alias_nm AS game_alias,
         game_prefix,
         use_yn AS active_yn,
         NVL(sort_order, 100) AS alias_priority,
         'GAME_ALIAS' AS source_type
    FROM comn_game_alias_bas
),
all_alias AS (
  SELECT game_key, display_name, game_alias, game_prefix, active_yn, alias_priority, source_type
    FROM source_alias
  UNION ALL
  SELECT r.game_key, r.display_name, r.game_alias, r.game_prefix,
         r.active_yn, r.alias_priority, r.source_type
    FROM sg_game_scope_registry r
   WHERE NOT EXISTS (
     SELECT 1
       FROM source_alias a
      WHERE a.game_key = r.game_key
        AND a.game_alias = r.game_alias
   )
),
scope_object AS (
  SELECT n.profile_name,
         a.game_key,
         a.game_alias,
         COUNT(p.object_name) AS approved_object_count
    FROM profile_names n
    CROSS JOIN all_alias a
    LEFT JOIN profile_objects p
     ON p.profile_name = n.profile_name
     AND a.game_prefix IS NOT NULL
     AND SUBSTR(p.object_name, 1, LENGTH(a.game_prefix) + 1) = a.game_prefix || '_'
   GROUP BY n.profile_name, a.game_key, a.game_alias
)
SELECT o.profile_name,
       a.game_key,
       a.display_name,
       a.game_alias,
       a.game_prefix,
       a.active_yn,
       NVL(o.approved_object_count, 0) AS approved_object_count,
       CASE
         WHEN a.active_yn <> 'Y' THEN 'N'
         WHEN NVL(o.approved_object_count, 0) > 0 THEN 'Y'
         ELSE 'N'
       END AS query_allowed_yn,
       CASE
         WHEN a.active_yn <> 'Y' THEN 'GAME_INACTIVE'
         WHEN NVL(o.approved_object_count, 0) > 0 THEN 'APPROVED_OBJECT_AVAILABLE'
         ELSE 'OBJECT_LIST_NOT_AVAILABLE'
       END AS reason_code,
       a.alias_priority,
       a.source_type,
       TO_CHAR(MAX(a.alias_priority) OVER (PARTITION BY a.game_key), 'FM999999990') AS scope_version
  FROM all_alias a
  LEFT JOIN scope_object o
    ON o.game_key = a.game_key
   AND o.game_alias = a.game_alias;
/

COMMENT ON TABLE sg_game_scope_registry IS
  'Operator-managed game aliases retained for scope resolution when a game has no approved query object.';
COMMENT ON COLUMN sg_game_scope_registry.game_key IS
  'Stable game identifier used only by the DB-backed scope contract.';
COMMENT ON COLUMN sg_game_scope_registry.game_alias IS
  'Question text alias matched by the resolver before any SQL worker is called.';
COMMENT ON COLUMN sg_game_scope_registry.game_prefix IS
  'Optional data-object prefix. The scope view derives approved object availability from it.';
COMMENT ON COLUMN sg_game_scope_registry.active_yn IS
  'Whether the game is eligible for scope resolution; inactive games are never executable.';
COMMENT ON COLUMN sg_game_scope_registry.alias_priority IS
  'Database-defined ordering used to resolve overlapping aliases without application constants.';
COMMENT ON COLUMN sg_game_query_scope_v.profile_name IS
  'Select AI profile whose current approved object list was used for this scope decision.';
COMMENT ON COLUMN sg_game_query_scope_v.query_allowed_yn IS
  'Y only when the active game has at least one current Select AI approved and valid prefix-specific table or view.';
COMMENT ON COLUMN sg_game_query_scope_v.reason_code IS
  'Database-derived explanation for scope availability returned to the MCP agent.';
