-- =====================================================================================
-- 第三方快递轨迹（平台侧）表结构升级 (PostgreSQL + Supabase)
-- 用途:
--   - 引入第三方承运方运单与轨迹事件的统一入库模型
--   - 支撑商家端/用户端/平台后台的同源轨迹展示
-- 说明:
--   - 仅创建 platform 侧三张表：platform_express_waybills / platform_express_tracking_events / platform_express_event_raw
--   - 不包含 mock_* 测试表
--   - 不涉及自营骑手表（ml_delivery_*）
-- =====================================================================================

BEGIN;

-- 依赖扩展（项目主库已使用 uuid-ossp；这里重复声明是安全的）
CREATE EXTENSION IF NOT EXISTS "uuid-ossp";
CREATE EXTENSION IF NOT EXISTS "btree_gin";

-- updated_at 维护函数：若主库已存在则不重复创建
DO $do$
BEGIN
  IF to_regprocedure('public.update_updated_at_column()') IS NULL THEN
    CREATE OR REPLACE FUNCTION public.update_updated_at_column()
    RETURNS TRIGGER
    LANGUAGE plpgsql
    AS $func$
    BEGIN
      NEW.updated_at = NOW();
      RETURN NEW;
    END;
    $func$;
  END IF;
END $do$;

-- =====================================================================
-- A. 平台侧（platform）：统一轨迹模型入库与查询
-- =====================================================================

-- 运单主表：按 (carrier, tracking_no) 唯一
CREATE TABLE IF NOT EXISTS public.platform_express_waybills (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),

  -- 关联订单（推荐关联主键；order_no 作为展示/兜底）
  order_id UUID NULL REFERENCES public.ml_orders(id) ON DELETE SET NULL,
  order_no VARCHAR(64) NULL,

  carrier VARCHAR(32) NOT NULL,                 -- YUNDA/YTO/ZTO/STO/KDN...
  tracking_no VARCHAR(64) NOT NULL,
  source VARCHAR(16) NOT NULL DEFAULT 'mock',   -- mock/carrier/aggregator

  current_status_code VARCHAR(32) NOT NULL DEFAULT 'SHIPPED',
  current_status_text TEXT NULL,

  eta TIMESTAMP WITH TIME ZONE NULL,
  last_synced_at TIMESTAMP WITH TIME ZONE NULL,

  created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
  updated_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),

  CONSTRAINT uk_platform_express_waybill UNIQUE (carrier, tracking_no)
);

-- updated_at 触发器
DO $$
BEGIN
  IF NOT EXISTS (
    SELECT 1 FROM pg_trigger
    WHERE tgname = 'trigger_platform_express_waybills_updated_at'
  ) THEN
    CREATE TRIGGER trigger_platform_express_waybills_updated_at
    BEFORE UPDATE ON public.platform_express_waybills
    FOR EACH ROW EXECUTE FUNCTION public.update_updated_at_column();
  END IF;
END $$;

-- 索引（按订单查运单是主查询路径）
CREATE INDEX IF NOT EXISTS idx_platform_express_waybills_order_id
  ON public.platform_express_waybills(order_id);
CREATE INDEX IF NOT EXISTS idx_platform_express_waybills_order_no
  ON public.platform_express_waybills(order_no);
CREATE INDEX IF NOT EXISTS idx_platform_express_waybills_tracking_no
  ON public.platform_express_waybills(tracking_no);
CREATE INDEX IF NOT EXISTS idx_platform_express_waybills_status
  ON public.platform_express_waybills(current_status_code);


-- 轨迹事件表：前端时间线/告警/统计的主数据来源
CREATE TABLE IF NOT EXISTS public.platform_express_tracking_events (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),

  waybill_id UUID NOT NULL REFERENCES public.platform_express_waybills(id) ON DELETE CASCADE,
  carrier VARCHAR(32) NOT NULL,
  tracking_no VARCHAR(64) NOT NULL,

  -- 第三方事件唯一标识（可能缺失）
  event_id VARCHAR(128) NULL,

  event_time TIMESTAMP WITH TIME ZONE NOT NULL,
  event_code VARCHAR(64) NOT NULL,
  event_text TEXT NOT NULL,

  -- 平台统一状态
  status_code VARCHAR(32) NOT NULL,

  node_name VARCHAR(128) NULL,
  location TEXT NULL,
  description TEXT NULL,

  evidence_urls JSONB NOT NULL DEFAULT '[]'::jsonb,

  -- 原始回文（用于审计/排障）
  raw_payload JSONB NULL,

  -- 接收侧信息
  received_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
  source VARCHAR(16) NOT NULL DEFAULT 'webhook', -- webhook/poll/manual

  -- 幂等去重键：优先 event_id；缺失时用 tracking_no+event_code+event_time(+可选字段) 构造
  dedupe_key VARCHAR(256) NOT NULL,

  created_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),

  CONSTRAINT uk_platform_express_event_dedupe UNIQUE (waybill_id, dedupe_key)
);

CREATE INDEX IF NOT EXISTS idx_platform_express_events_waybill_time
  ON public.platform_express_tracking_events(waybill_id, event_time);
CREATE INDEX IF NOT EXISTS idx_platform_express_events_tracking_time
  ON public.platform_express_tracking_events(tracking_no, event_time);
CREATE INDEX IF NOT EXISTS idx_platform_express_events_status
  ON public.platform_express_tracking_events(status_code);
CREATE INDEX IF NOT EXISTS idx_platform_express_events_received_at
  ON public.platform_express_tracking_events(received_at);
CREATE INDEX IF NOT EXISTS gin_platform_express_events_raw_payload
  ON public.platform_express_tracking_events USING gin (raw_payload);


-- 原始接收表：记录每一次 webhook/轮询原文、验签结果与解析错误
CREATE TABLE IF NOT EXISTS public.platform_express_event_raw (
  id UUID PRIMARY KEY DEFAULT uuid_generate_v4(),

  received_at TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW(),
  source VARCHAR(16) NOT NULL DEFAULT 'webhook',

  -- 请求侧标识
  client_id VARCHAR(64) NULL,
  carrier VARCHAR(32) NULL,
  tracking_no VARCHAR(64) NULL,

  -- 安全审计
  signature_valid BOOLEAN NULL,
  signature TEXT NULL,
  ts_header TEXT NULL,

  request_id VARCHAR(64) NULL,
  remote_ip INET NULL,
  headers JSONB NULL,

  body JSONB NULL,
  parse_error TEXT NULL,

  -- 去重辅助（可选）：用于识别完全重复的原始请求
  dedupe_key VARCHAR(256) NULL
);

CREATE INDEX IF NOT EXISTS idx_platform_express_raw_received_at
  ON public.platform_express_event_raw(received_at);
CREATE INDEX IF NOT EXISTS idx_platform_express_raw_tracking_no
  ON public.platform_express_event_raw(tracking_no);
CREATE INDEX IF NOT EXISTS idx_platform_express_raw_signature_valid
  ON public.platform_express_event_raw(signature_valid);
CREATE INDEX IF NOT EXISTS gin_platform_express_raw_body
  ON public.platform_express_event_raw USING gin (body);

COMMIT;
