-- Aslan Game King: dedicated consume-to-draw campaign, ranking and settlement.
--
-- Performance review (DDL only; this migration must not run ad-hoc against production):
-- 1. Wallet hot path inserts one ledger row by uq_activity_asset, then updates one overall PK row
--    and one daily PK row. All are O(log N); ranking never GROUP BYs the append-only ledger.
-- 2. Overall/daily Tycoon and Victorious rankings read at most 30/100 rows through dimension
--    indexes whose prefixes bind activity_id (and stat_date for daily rankings).
-- 3. Current campaign lookup uses idx_aslan_gk_activity_window. start_time is the range column;
--    end_time remains an index-covered residual filter, which is acceptable because concurrent
--    enabled campaigns per sys_origin are expected to be very small.
-- 4. Draw request and settlement retry paths use unique keys. The recovery scan uses
--    idx_aslan_gk_draw_recover (delivery_status, update_time, id), so the five-minute task does
--    not scan or filesort the full draw table as records accumulate.
-- 5. Settlement due scans bind settlement_status first and range on settlement_time through
--    idx_aslan_gk_activity_settle; end_time remains available for the bounded due-order scan.
--    Per-reward delivery writes only the small configured group and reads it by owner/status.
-- 6. No UPDATE/DELETE scans an unindexed predicate. The migration only creates empty tables and
--    idempotent menu rows; it does not backfill wallet history or lock existing business tables.
-- 7. Unlimited prize stock (-1) is never updated at draw time, avoiding a global hot-row lock;
--    only finite stock uses the activity/prize primary-key update.

SET NAMES utf8mb4;

CREATE TABLE IF NOT EXISTS aslan_game_king_activity (
  id BIGINT NOT NULL,
  activity_code VARCHAR(64) NOT NULL,
  activity_name VARCHAR(128) NOT NULL,
  activity_desc VARCHAR(1000) NULL,
  sys_origin VARCHAR(32) NOT NULL,
  time_zone VARCHAR(64) NOT NULL DEFAULT 'Asia/Riyadh',
  start_time DATETIME(3) NOT NULL,
  end_time DATETIME(3) NOT NULL,
  settlement_delay_minutes INT NOT NULL DEFAULT 30,
  settlement_time DATETIME(3) NOT NULL,
  coin_per_draw BIGINT NOT NULL,
  event_types VARCHAR(2000) NOT NULL,
  ranking_type VARCHAR(32) NOT NULL DEFAULT 'TYCOON',
  ranking_period VARCHAR(32) NOT NULL DEFAULT 'OVERALL',
  enabled TINYINT(1) NOT NULL DEFAULT 0,
  settlement_status VARCHAR(32) NOT NULL DEFAULT 'NOT_STARTED',
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  PRIMARY KEY (id),
  UNIQUE KEY uq_aslan_gk_activity_code (activity_code),
  KEY idx_aslan_gk_activity_window (sys_origin, enabled, start_time, end_time),
  KEY idx_aslan_gk_activity_settle (settlement_status, settlement_time, end_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Aslan Game King campaign';

CREATE TABLE IF NOT EXISTS aslan_game_king_prize (
  id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  prize_name VARCHAR(128) NOT NULL,
  prize_image VARCHAR(500) NULL,
  weight BIGINT NOT NULL,
  stock BIGINT NOT NULL DEFAULT -1 COMMENT '-1 unlimited, 0 depleted, positive remaining',
  resource_group_id BIGINT NOT NULL,
  enabled TINYINT(1) NOT NULL DEFAULT 1,
  sort_order TINYINT NOT NULL,
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  PRIMARY KEY (id),
  UNIQUE KEY uq_aslan_gk_prize_position (activity_id, sort_order),
  KEY idx_aslan_gk_prize_enabled (activity_id, enabled)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Seven configured prizes for Aslan Game King';

CREATE TABLE IF NOT EXISTS aslan_game_king_rank_reward (
  id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  start_rank SMALLINT NOT NULL,
  end_rank SMALLINT NOT NULL,
  resource_group_id BIGINT NOT NULL,
  reward_name VARCHAR(128) NULL,
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  PRIMARY KEY (id),
  UNIQUE KEY uq_aslan_gk_rank_range (activity_id, start_rank, end_rank),
  KEY idx_aslan_gk_rank_start (activity_id, start_rank)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Six exact end-settlement rank reward ranges';

CREATE TABLE IF NOT EXISTS aslan_game_king_wallet_ledger (
  id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  asset_record_id BIGINT NOT NULL,
  user_id BIGINT NOT NULL,
  sys_origin VARCHAR(32) NOT NULL,
  event_type VARCHAR(64) NOT NULL,
  event_id VARCHAR(128) NULL,
  receipt_type TINYINT NOT NULL COMMENT '0 income/winning, 1 expenditure/consumption',
  amount DECIMAL(24,2) NOT NULL,
  event_time DATETIME(3) NOT NULL,
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  PRIMARY KEY (id),
  UNIQUE KEY uq_aslan_gk_activity_asset (activity_id, asset_record_id),
  KEY idx_aslan_gk_ledger_user (activity_id, user_id, event_time)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Idempotent eligible game expenditure and winning ledger';

CREATE TABLE IF NOT EXISTS aslan_game_king_user (
  activity_id BIGINT NOT NULL,
  user_id BIGINT NOT NULL,
  total_consumed DECIMAL(24,2) NOT NULL DEFAULT 0,
  total_won DECIMAL(24,2) NOT NULL DEFAULT 0,
  used_chances BIGINT NOT NULL DEFAULT 0,
  consume_reached_time DATETIME(3) NULL,
  win_reached_time DATETIME(3) NULL,
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  PRIMARY KEY (activity_id, user_id),
  KEY idx_aslan_gk_user_tycoon (
    activity_id,
    total_consumed DESC,
    consume_reached_time ASC,
    user_id ASC
  ),
  KEY idx_aslan_gk_user_victorious (
    activity_id,
    total_won DESC,
    win_reached_time ASC,
    user_id ASC
  )
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Overall Tycoon/Victorious aggregates and used draw chances';

CREATE TABLE IF NOT EXISTS aslan_game_king_user_daily (
  activity_id BIGINT NOT NULL,
  stat_date DATE NOT NULL,
  user_id BIGINT NOT NULL,
  total_consumed DECIMAL(24,2) NOT NULL DEFAULT 0,
  total_won DECIMAL(24,2) NOT NULL DEFAULT 0,
  consume_reached_time DATETIME(3) NULL,
  win_reached_time DATETIME(3) NULL,
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  PRIMARY KEY (activity_id, stat_date, user_id),
  KEY idx_aslan_gk_daily_tycoon (
    activity_id, stat_date, total_consumed DESC, consume_reached_time ASC, user_id ASC
  ),
  KEY idx_aslan_gk_daily_victorious (
    activity_id, stat_date, total_won DESC, win_reached_time ASC, user_id ASC
  )
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Timezone-materialized daily Tycoon/Victorious aggregates';

CREATE TABLE IF NOT EXISTS aslan_game_king_draw_record (
  id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  user_id BIGINT NOT NULL,
  request_id VARCHAR(64) NOT NULL,
  prize_id BIGINT NOT NULL,
  prize_name VARCHAR(128) NOT NULL,
  prize_image VARCHAR(500) NULL,
  resource_group_id BIGINT NULL,
  delivery_status VARCHAR(32) NOT NULL,
  failure_reason VARCHAR(500) NULL,
  draw_time DATETIME(3) NOT NULL,
  deliver_time DATETIME(3) NULL,
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  PRIMARY KEY (id),
  UNIQUE KEY uq_aslan_gk_draw_request (activity_id, user_id, request_id),
  KEY idx_aslan_gk_draw_user (activity_id, user_id, id),
  KEY idx_aslan_gk_draw_manage (activity_id, delivery_status, id),
  KEY idx_aslan_gk_draw_recover (delivery_status, update_time, id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Idempotent Aslan Game King draws and delivery state';

CREATE TABLE IF NOT EXISTS aslan_game_king_settlement_record (
  id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  user_id BIGINT NOT NULL,
  rank_no SMALLINT NOT NULL,
  total_consumed DECIMAL(24,2) NOT NULL,
  rank_reward_id BIGINT NOT NULL,
  resource_group_id BIGINT NOT NULL,
  business_no VARCHAR(128) NOT NULL,
  delivery_status VARCHAR(32) NOT NULL,
  retry_count INT NOT NULL DEFAULT 0,
  failure_reason VARCHAR(500) NULL,
  deliver_time DATETIME(3) NULL,
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  PRIMARY KEY (id),
  UNIQUE KEY uq_aslan_gk_settlement_user (activity_id, user_id),
  UNIQUE KEY uq_aslan_gk_settlement_rank (activity_id, rank_no),
  UNIQUE KEY uq_aslan_gk_settlement_business (business_no),
  KEY idx_aslan_gk_settlement_status (activity_id, delivery_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Frozen top-30 settlement snapshot and retry state';

CREATE TABLE IF NOT EXISTS aslan_game_king_delivery_item (
  id BIGINT NOT NULL,
  owner_type VARCHAR(20) NOT NULL COMMENT 'DRAW or SETTLEMENT',
  owner_id BIGINT NOT NULL,
  activity_id BIGINT NOT NULL,
  user_id BIGINT NOT NULL,
  reward_config_id BIGINT NOT NULL,
  resource_group_id BIGINT NOT NULL,
  reward_type VARCHAR(64) NOT NULL,
  detail_type VARCHAR(128) NULL,
  content VARCHAR(2000) NULL,
  quantity INT NULL,
  sort_order INT NULL,
  remark VARCHAR(1000) NULL,
  delivery_status VARCHAR(32) NOT NULL,
  retry_count INT NOT NULL DEFAULT 0,
  failure_reason VARCHAR(500) NULL,
  deliver_time DATETIME(3) NULL,
  create_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3),
  update_time DATETIME(3) NOT NULL DEFAULT CURRENT_TIMESTAMP(3) ON UPDATE CURRENT_TIMESTAMP(3),
  PRIMARY KEY (id),
  UNIQUE KEY uq_aslan_gk_delivery_config (owner_type, owner_id, reward_config_id),
  KEY idx_aslan_gk_delivery_owner (owner_type, owner_id, delivery_status),
  KEY idx_aslan_gk_delivery_activity (activity_id, delivery_status, id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
  COMMENT='Per reward-config transactional delivery outbox';

-- The following menu/resource statements are idempotent and inherit only roles that already own
-- the Operations directory. They do not grant the campaign page to unrelated roles.
INSERT INTO sys_resource (id, resource_name, mapping, method, auth_type, perm, update_time)
VALUES
  ('activity:game-king:list', '游戏王活动列表', '/activity/game-king/list', 'GET', 3, 'activity:game-king:list', NOW()),
  ('activity:game-king:detail', '游戏王活动详情', '/activity/game-king/detail/**', 'GET', 3, 'activity:game-king:detail', NOW()),
  ('activity:game-king:save', '保存游戏王活动', '/activity/game-king/save', 'POST', 3, 'activity:game-king:save', NOW()),
  ('activity:game-king:enable', '启停游戏王活动', '/activity/game-king/enable/**', 'PUT', 3, 'activity:game-king:enable', NOW()),
  ('activity:game-king:prizes:get', '游戏王奖品列表', '/activity/game-king/prizes/**', 'GET', 3, 'activity:game-king:prizes:get', NOW()),
  ('activity:game-king:prizes:save', '保存游戏王奖品', '/activity/game-king/prizes/**', 'PUT', 3, 'activity:game-king:prizes:save', NOW()),
  ('activity:game-king:rank:get', '游戏王排名奖励', '/activity/game-king/rank-rewards/**', 'GET', 3, 'activity:game-king:rank:get', NOW()),
  ('activity:game-king:rank:save', '保存游戏王排名奖励', '/activity/game-king/rank-rewards/**', 'PUT', 3, 'activity:game-king:rank:save', NOW()),
  ('activity:game-king:draw:get', '游戏王抽奖记录', '/activity/game-king/draw-records/**', 'GET', 3, 'activity:game-king:draw:get', NOW()),
  ('activity:game-king:draw:retry', '重试游戏王抽奖奖励', '/activity/game-king/draw/retry/**', 'POST', 3, 'activity:game-king:draw:retry', NOW()),
  ('activity:game-king:delivery:resolve', '核账处理游戏王未知奖励', '/activity/game-king/delivery-item/resolve/**', 'POST', 3, 'activity:game-king:delivery:resolve', NOW()),
  ('activity:game-king:settlement:get', '游戏王结算记录', '/activity/game-king/settlement-records/**', 'GET', 3, 'activity:game-king:settlement:get', NOW()),
  ('activity:game-king:settlement:run', '执行游戏王结算', '/activity/game-king/settlement/**', 'POST', 3, 'activity:game-king:settlement:run', NOW()),
  ('activity:game-king:backfill', '补算游戏王消费', '/activity/game-king/backfill/**', 'POST', 3, 'activity:game-king:backfill', NOW())
ON DUPLICATE KEY UPDATE
  resource_name = VALUES(resource_name),
  mapping = VALUES(mapping),
  method = VALUES(method),
  auth_type = VALUES(auth_type),
  perm = VALUES(perm),
  update_time = NOW();

INSERT INTO sys_menu (
  parent_id, menu_name, path, router, menu_type, icon, alias, sort, status,
  create_user, update_user, create_time, update_time
)
SELECT
  parent.id,
  '游戏王活动',
  'activity/game-king/index',
  'activity/game-king',
  2,
  'trophy',
  'ActivityGameKing',
  88,
  0,
  '0',
  '0',
  NOW(),
  NOW()
FROM sys_menu parent
LEFT JOIN sys_menu existing ON existing.alias = 'ActivityGameKing'
WHERE (parent.alias = 'OperationManager' OR parent.menu_name = '运营管理')
  AND parent.menu_type = 1
  AND existing.id IS NULL
LIMIT 1;

UPDATE sys_menu campaign_menu
JOIN (
  SELECT id
  FROM sys_menu
  WHERE (alias = 'OperationManager' OR menu_name = '运营管理')
    AND menu_type = 1
  LIMIT 1
) parent ON 1 = 1
SET
  campaign_menu.parent_id = parent.id,
  campaign_menu.menu_name = '游戏王活动',
  campaign_menu.path = 'activity/game-king/index',
  campaign_menu.router = 'activity/game-king',
  campaign_menu.menu_type = 2,
  campaign_menu.icon = 'trophy',
  campaign_menu.sort = 88,
  campaign_menu.status = 0,
  campaign_menu.update_user = '0',
  campaign_menu.update_time = NOW()
WHERE campaign_menu.alias = 'ActivityGameKing';

INSERT INTO sys_menu_resource (menu_id, resource_id)
SELECT menu.id, resource.id
FROM sys_menu menu
JOIN sys_resource resource ON resource.id LIKE 'activity:game-king:%'
LEFT JOIN sys_menu_resource existing
  ON existing.menu_id = menu.id AND existing.resource_id = resource.id
WHERE menu.alias = 'ActivityGameKing'
  AND existing.id IS NULL;

INSERT INTO sys_role_menu (role_id, menu_id)
SELECT DISTINCT source_role.role_id, campaign_menu.id
FROM sys_role_menu source_role
JOIN sys_menu source_menu ON source_menu.id = source_role.menu_id
JOIN sys_menu campaign_menu ON campaign_menu.alias = 'ActivityGameKing'
LEFT JOIN sys_role_menu existing
  ON existing.role_id = source_role.role_id AND existing.menu_id = campaign_menu.id
WHERE (source_menu.alias = 'OperationManager' OR source_menu.menu_name = '运营管理')
  AND source_menu.menu_type = 1
  AND existing.id IS NULL;
