-- 001_init.sql —— 空库基线迁移（完整业务库结构）。
--
-- 约定：
-- 1. 仅在空库首次迁移时执行本文件，建立全量基线结构。
-- 2. 后续结构变更不要改本文件，改为新增 002_xxx.sql / 003_xxx.sql 等渐进迁移。
-- 3. 新环境按文件名顺序执行：001_init → 后续增量；已有环境只应用尚未记录的增量。
-- 4. 已应用到任何共享/生产环境的迁移文件禁止修改内容。

CREATE EXTENSION IF NOT EXISTS pgcrypto;

-- 后台账号与审计日志。
CREATE TABLE IF NOT EXISTS admin_users (
  id BIGSERIAL PRIMARY KEY,
  username TEXT NOT NULL UNIQUE,
  password_hash TEXT NOT NULL,
  role TEXT NOT NULL,
  status TEXT NOT NULL,
  session_version INTEGER NOT NULL DEFAULT 1,
  created_at TIMESTAMPTZ NOT NULL,
  updated_at TIMESTAMPTZ NOT NULL
);

CREATE TABLE IF NOT EXISTS admin_audit_logs (
  id BIGSERIAL PRIMARY KEY,
  actor_user_id BIGINT,
  actor_username TEXT NOT NULL DEFAULT '',
  actor_role TEXT NOT NULL DEFAULT '',
  action TEXT NOT NULL,
  target_type TEXT NOT NULL,
  target_id TEXT NOT NULL,
  payload_json JSONB NOT NULL DEFAULT '{}'::jsonb,
  created_at TIMESTAMPTZ NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_created_at
  ON admin_audit_logs(created_at DESC);
CREATE INDEX IF NOT EXISTS idx_admin_audit_logs_target
  ON admin_audit_logs(target_type, target_id);

COMMENT ON TABLE admin_users IS '后台管理用户';
COMMENT ON TABLE admin_audit_logs IS '后台操作审计日志';
COMMENT ON COLUMN admin_audit_logs.payload_json IS '审计上下文快照，保留请求关键字段和变更内容';

-- 运行时配置中心。
CREATE TABLE IF NOT EXISTS app_config_entries (
  config_key TEXT PRIMARY KEY,
  config_json JSONB NOT NULL DEFAULT '{}'::jsonb,
  source_file_path TEXT NOT NULL DEFAULT '',
  migrated_from_file BOOLEAN NOT NULL DEFAULT FALSE,
  created_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMPTZ NOT NULL DEFAULT NOW()
);

CREATE INDEX IF NOT EXISTS idx_app_config_entries_updated_at
  ON app_config_entries(updated_at DESC);

COMMENT ON TABLE app_config_entries IS '运行时配置中心，保存从 JSON 文件迁移来的平台和系统配置';
COMMENT ON COLUMN app_config_entries.config_key IS '配置唯一键';
COMMENT ON COLUMN app_config_entries.config_json IS '配置内容 JSON';
COMMENT ON COLUMN app_config_entries.source_file_path IS '首次迁移来源文件路径，便于排查历史来源';
COMMENT ON COLUMN app_config_entries.migrated_from_file IS '是否由旧 JSON 文件迁移产生';

-- 履约档案与履约资源要求。
CREATE TABLE IF NOT EXISTS fulfillment_profiles (
  id BIGSERIAL PRIMARY KEY,
  profile_key TEXT NOT NULL UNIQUE,
  name TEXT NOT NULL,
  executor_key TEXT NOT NULL,
  requires_claim BOOLEAN NOT NULL DEFAULT FALSE,
  auto_dispatch BOOLEAN NOT NULL DEFAULT FALSE,
  inventory_strategy TEXT NOT NULL DEFAULT 'static_pool',
  config_json JSONB NOT NULL DEFAULT '{}'::jsonb,
  created_at TIMESTAMPTZ NOT NULL,
  updated_at TIMESTAMPTZ NOT NULL
);

CREATE TABLE IF NOT EXISTS fulfillment_profile_requirements (
  id BIGSERIAL PRIMARY KEY,
  profile_id BIGINT NOT NULL REFERENCES fulfillment_profiles(id) ON DELETE CASCADE,
  role_key TEXT NOT NULL,
  credential_type TEXT NOT NULL,
  quantity_per_unit INTEGER NOT NULL DEFAULT 1,
  is_required BOOLEAN NOT NULL DEFAULT TRUE,
  config_json JSONB NOT NULL DEFAULT '{}'::jsonb,
  created_at TIMESTAMPTZ NOT NULL,
  updated_at TIMESTAMPTZ NOT NULL,
  UNIQUE(profile_id, role_key)
);

COMMENT ON TABLE fulfillment_profiles IS '履约档案，定义不同履约执行器的基础能力';
COMMENT ON COLUMN fulfillment_profiles.config_json IS '履约执行器配置快照，适合存放低频变化的扩展配置';
COMMENT ON TABLE fulfillment_profile_requirements IS '履约档案需要的账号、角色或凭证要求';
COMMENT ON COLUMN fulfillment_profile_requirements.config_json IS '履约资源要求的扩展配置';

-- 订单主数据与订单项。
CREATE TABLE IF NOT EXISTS orders (
  id BIGSERIAL PRIMARY KEY,
  provider TEXT NOT NULL,
  platform TEXT NOT NULL,
  shop_id TEXT NOT NULL DEFAULT '',
  shop_name TEXT NOT NULL DEFAULT '',
  platform_order_id TEXT NOT NULL,
  order_status TEXT NOT NULL DEFAULT 'created',
  pay_status TEXT NOT NULL DEFAULT 'unpaid',
  buyer_id TEXT NOT NULL DEFAULT '',
  buyer_name TEXT NOT NULL DEFAULT '',
  receiver_contact TEXT NOT NULL DEFAULT '',
  total_amount INTEGER NOT NULL DEFAULT 0,
  currency TEXT NOT NULL DEFAULT 'CNY',
  raw_payload_json JSONB NOT NULL DEFAULT '{}'::jsonb,
  paid_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL,
  updated_at TIMESTAMPTZ NOT NULL,
  UNIQUE(provider, platform, shop_id, platform_order_id)
);

CREATE INDEX IF NOT EXISTS idx_orders_platform_order_lookup
  ON orders(provider, platform, platform_order_id, id DESC);
CREATE INDEX IF NOT EXISTS idx_orders_created_at_desc
  ON orders(created_at DESC);

CREATE TABLE IF NOT EXISTS order_items (
  id BIGSERIAL PRIMARY KEY,
  order_id BIGINT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
  sku_code TEXT NOT NULL,
  sku_name TEXT NOT NULL DEFAULT '',
  quantity INTEGER NOT NULL DEFAULT 1,
  spec_json JSONB NOT NULL DEFAULT '{}'::jsonb,
  item_snapshot_json JSONB NOT NULL DEFAULT '{}'::jsonb,
  created_at TIMESTAMPTZ NOT NULL,
  updated_at TIMESTAMPTZ NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_order_items_order_id
  ON order_items(order_id);
CREATE INDEX IF NOT EXISTS idx_order_items_sku_code
  ON order_items(sku_code);

COMMENT ON TABLE orders IS '外部平台订单主表';
COMMENT ON COLUMN orders.raw_payload_json IS '外部平台原始订单报文，便于追溯和兼容字段变化';
COMMENT ON TABLE order_items IS '订单商品明细';
COMMENT ON COLUMN order_items.spec_json IS '商品规格扩展字段';
COMMENT ON COLUMN order_items.item_snapshot_json IS '订单项履约规划时的商品、路由和平台配置快照';

-- 履约任务、领取令牌和任务事件。
CREATE TABLE IF NOT EXISTS fulfillment_tasks (
  id BIGSERIAL PRIMARY KEY,
  order_id BIGINT NOT NULL REFERENCES orders(id) ON DELETE CASCADE,
  order_item_id BIGINT NOT NULL REFERENCES order_items(id) ON DELETE CASCADE,
  unit_index INTEGER NOT NULL DEFAULT 1,
  task_no TEXT NOT NULL UNIQUE,
  provider TEXT NOT NULL,
  platform TEXT NOT NULL,
  shop_id TEXT NOT NULL DEFAULT '',
  shop_name TEXT NOT NULL DEFAULT '',
  platform_order_id TEXT NOT NULL,
  profile_id BIGINT NOT NULL REFERENCES fulfillment_profiles(id) ON DELETE RESTRICT,
  executor_key TEXT NOT NULL,
  task_status TEXT NOT NULL DEFAULT 'pending_payment',
  delivery_status TEXT NOT NULL DEFAULT 'pending',
  result_code TEXT NOT NULL DEFAULT '',
  result_message TEXT NOT NULL DEFAULT '',
  claim_token TEXT NOT NULL DEFAULT '',
  claim_expires_at TIMESTAMPTZ,
  automation_mode TEXT NOT NULL DEFAULT 'manual',
  requires_claim BOOLEAN NOT NULL DEFAULT FALSE,
  user_action_status TEXT NOT NULL DEFAULT 'not_required',
  attempt_count INTEGER NOT NULL DEFAULT 0,
  last_error TEXT NOT NULL DEFAULT '',
  context_json JSONB NOT NULL DEFAULT '{}'::jsonb,
  claimed_at TIMESTAMPTZ,
  role_confirmed_at TIMESTAMPTZ,
  redeemed_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL,
  updated_at TIMESTAMPTZ NOT NULL,
  UNIQUE(order_item_id, unit_index)
);

CREATE INDEX IF NOT EXISTS idx_fulfillment_tasks_status
  ON fulfillment_tasks(task_status, delivery_status);
CREATE INDEX IF NOT EXISTS idx_fulfillment_tasks_order
  ON fulfillment_tasks(order_id, order_item_id);
CREATE INDEX IF NOT EXISTS idx_fulfillment_tasks_claim_token
  ON fulfillment_tasks(claim_token);
CREATE INDEX IF NOT EXISTS idx_fulfillment_tasks_platform_order_lookup
  ON fulfillment_tasks(provider, platform, platform_order_id, id DESC);
CREATE INDEX IF NOT EXISTS idx_fulfillment_tasks_created_at_desc
  ON fulfillment_tasks(created_at DESC);

CREATE TABLE IF NOT EXISTS claim_tokens (
  id BIGSERIAL PRIMARY KEY,
  task_id BIGINT NOT NULL UNIQUE REFERENCES fulfillment_tasks(id) ON DELETE CASCADE,
  token TEXT NOT NULL UNIQUE,
  status TEXT NOT NULL DEFAULT 'active',
  expired_at TIMESTAMPTZ NOT NULL,
  used_at TIMESTAMPTZ,
  max_use_count INTEGER NOT NULL DEFAULT 1,
  used_count INTEGER NOT NULL DEFAULT 0,
  created_at TIMESTAMPTZ NOT NULL,
  updated_at TIMESTAMPTZ NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_claim_tokens_status
  ON claim_tokens(status, expired_at);

CREATE TABLE IF NOT EXISTS task_events (
  id BIGSERIAL PRIMARY KEY,
  task_id BIGINT NOT NULL REFERENCES fulfillment_tasks(id) ON DELETE CASCADE,
  event_type TEXT NOT NULL,
  payload_json JSONB NOT NULL DEFAULT '{}'::jsonb,
  created_at TIMESTAMPTZ NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_task_events_task_created_at
  ON task_events(task_id, created_at DESC);

COMMENT ON TABLE fulfillment_tasks IS '履约任务，一件商品按数量拆成独立履约单元';
COMMENT ON COLUMN fulfillment_tasks.context_json IS '任务执行上下文，保存履约过程中的平台返回、链接、角色和中间状态';
COMMENT ON TABLE claim_tokens IS '用户领取链接令牌';
COMMENT ON TABLE task_events IS '任务事件流水';
COMMENT ON COLUMN task_events.payload_json IS '事件 payload 快照';

-- 运行时上下文和快手云履约索引态。
CREATE TABLE IF NOT EXISTS task_runtime_contexts (
  id BIGSERIAL PRIMARY KEY,
  task_id BIGINT NOT NULL UNIQUE REFERENCES fulfillment_tasks(id) ON DELETE CASCADE,
  runtime_session_id TEXT NOT NULL DEFAULT '',
  login_type TEXT NOT NULL DEFAULT '',
  nickname TEXT NOT NULL DEFAULT '',
  role_id TEXT NOT NULL DEFAULT '',
  role_name TEXT NOT NULL DEFAULT '',
  area TEXT NOT NULL DEFAULT '',
  partition_name TEXT NOT NULL DEFAULT '',
  screenshot_path TEXT NOT NULL DEFAULT '',
  artifacts_json JSONB NOT NULL DEFAULT '{}'::jsonb,
  state_json JSONB NOT NULL DEFAULT '{}'::jsonb,
  created_at TIMESTAMPTZ NOT NULL,
  updated_at TIMESTAMPTZ NOT NULL
);

CREATE TABLE IF NOT EXISTS kuaishou_cloud_task_states (
  task_id BIGINT PRIMARY KEY REFERENCES fulfillment_tasks(id) ON DELETE CASCADE,
  ticket_status TEXT NOT NULL DEFAULT 'pending',
  ticket_code_masked TEXT NOT NULL DEFAULT '',
  bind_url TEXT NOT NULL DEFAULT '',
  vn_phone TEXT NOT NULL DEFAULT '',
  role_name TEXT NOT NULL DEFAULT '',
  role_id TEXT NOT NULL DEFAULT '',
  dispatch_status TEXT NOT NULL DEFAULT 'pending',
  consume_status TEXT NOT NULL DEFAULT 'pending',
  source_key TEXT NOT NULL DEFAULT '',
  updated_at TIMESTAMPTZ NOT NULL
);

CREATE INDEX IF NOT EXISTS idx_kuaishou_cloud_task_states_role_id
  ON kuaishou_cloud_task_states(role_id);
CREATE INDEX IF NOT EXISTS idx_kuaishou_cloud_task_states_source_key
  ON kuaishou_cloud_task_states(source_key);
CREATE INDEX IF NOT EXISTS idx_kuaishou_cloud_task_states_dispatch_status
  ON kuaishou_cloud_task_states(dispatch_status);
CREATE INDEX IF NOT EXISTS idx_kuaishou_cloud_task_states_consume_status
  ON kuaishou_cloud_task_states(consume_status);
CREATE INDEX IF NOT EXISTS idx_kuaishou_cloud_task_states_updated_at_desc
  ON kuaishou_cloud_task_states(updated_at DESC);

COMMENT ON TABLE task_runtime_contexts IS '任务运行时领取和截图上下文';
COMMENT ON COLUMN task_runtime_contexts.artifacts_json IS '运行时产物集合，如截图、日志或外部资源引用';
COMMENT ON COLUMN task_runtime_contexts.state_json IS '运行时状态快照';
COMMENT ON TABLE kuaishou_cloud_task_states IS '从任务上下文同步出来的快手云履约可查询状态';

-- 快手行业电子凭证。
CREATE TABLE IF NOT EXISTS kuaishou_industry_vouchers (
  id BIGSERIAL PRIMARY KEY,
  voucher_code TEXT NOT NULL UNIQUE,
  oid TEXT NOT NULL,
  order_id BIGINT REFERENCES orders(id) ON DELETE SET NULL,
  task_id BIGINT REFERENCES fulfillment_tasks(id) ON DELETE SET NULL,
  unit_index INTEGER NOT NULL,
  seller_id TEXT NOT NULL DEFAULT '',
  token TEXT NOT NULL DEFAULT '',
  status TEXT NOT NULL DEFAULT 'UNUSED',
  valid_start_time BIGINT NOT NULL DEFAULT 0,
  valid_end_time BIGINT NOT NULL DEFAULT 0,
  consume_serial_num TEXT NOT NULL DEFAULT '',
  consume_details_json JSONB NOT NULL DEFAULT '[]'::jsonb,
  consumed_at TIMESTAMPTZ,
  destroyed_at TIMESTAMPTZ,
  raw_payload_json JSONB NOT NULL DEFAULT '{}'::jsonb,
  send_callback_status TEXT NOT NULL DEFAULT 'success',
  send_callback_attempt_count INTEGER NOT NULL DEFAULT 0,
  send_callback_last_error TEXT NOT NULL DEFAULT '',
  send_callback_response_json JSONB NOT NULL DEFAULT '{}'::jsonb,
  send_callback_sent_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL,
  updated_at TIMESTAMPTZ NOT NULL,
  UNIQUE(oid, unit_index)
);

CREATE INDEX IF NOT EXISTS idx_kuaishou_industry_vouchers_oid
  ON kuaishou_industry_vouchers(oid, unit_index);
CREATE INDEX IF NOT EXISTS idx_kuaishou_industry_vouchers_order_id
  ON kuaishou_industry_vouchers(order_id);
CREATE INDEX IF NOT EXISTS idx_kuaishou_industry_vouchers_task_id
  ON kuaishou_industry_vouchers(task_id);
CREATE INDEX IF NOT EXISTS idx_kuaishou_industry_vouchers_status
  ON kuaishou_industry_vouchers(status);
CREATE INDEX IF NOT EXISTS idx_kuaishou_industry_vouchers_seller_id
  ON kuaishou_industry_vouchers(seller_id);
CREATE INDEX IF NOT EXISTS idx_kuaishou_industry_vouchers_send_callback_status
  ON kuaishou_industry_vouchers(send_callback_status);

COMMENT ON TABLE kuaishou_industry_vouchers IS '快手行业电子凭证码实例';
COMMENT ON COLUMN kuaishou_industry_vouchers.consume_details_json IS '快手核销详情列表';
COMMENT ON COLUMN kuaishou_industry_vouchers.raw_payload_json IS '快手行业电子凭证原始请求或回调报文';
COMMENT ON COLUMN kuaishou_industry_vouchers.send_callback_response_json IS '发码回调响应内容';

-- 短链。
CREATE TABLE IF NOT EXISTS short_links (
  id BIGSERIAL PRIMARY KEY,
  code TEXT NOT NULL UNIQUE,
  target_url TEXT NOT NULL,
  target_url_hash TEXT NOT NULL,
  source TEXT NOT NULL DEFAULT '',
  task_id BIGINT REFERENCES fulfillment_tasks(id) ON DELETE SET NULL,
  expires_at TIMESTAMPTZ,
  visit_count INTEGER NOT NULL DEFAULT 0,
  last_visited_at TIMESTAMPTZ,
  created_at TIMESTAMPTZ NOT NULL,
  updated_at TIMESTAMPTZ NOT NULL,
  UNIQUE(source, task_id, target_url_hash)
);

CREATE INDEX IF NOT EXISTS idx_short_links_code
  ON short_links(code);
CREATE INDEX IF NOT EXISTS idx_short_links_task_id
  ON short_links(task_id);
CREATE INDEX IF NOT EXISTS idx_short_links_expires_at
  ON short_links(expires_at);

COMMENT ON TABLE short_links IS '内部短链映射，用于隐藏较长领取链接或外部平台链接';
