-- =============================================================================
-- 镭射卡重构 v3 — 业务表迁移（Phase 1）
-- 文档：docs/specs/2026-05-25-laser-card-refactor-design.md §6.5–6.11
-- 创建日期：2026-05-27
--
-- 说明：
--   1. 本期新增 laser_card_templates / laser_card_instances / laser_card_operation_logs
--   2. 不对 assets / asset_registry 做 ALTER（品类+工艺走 assets.tags 约定，见 §6.3.1）
--   3. 依赖：public.assets、public.mint_orders、public.materials 已存在
-- 执行：psql -U <user> -d <db> -f migrate_laser_card_v3_tables.sql
-- 备注：§4 含全部 COMMENT ON（表/字段/索引/约束），可重复执行；已建表无备注时可单独跑 migrate_laser_card_v3_comments_only.sql
-- =============================================================================

BEGIN;

-- -----------------------------------------------------------------------------
-- 1. laser_card_templates
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.laser_card_templates (
    id                  BIGSERIAL PRIMARY KEY,
    template_code       VARCHAR(64)  NOT NULL,
    name                VARCHAR(100) NOT NULL,
    description         TEXT,
    status              VARCHAR(20)  NOT NULL DEFAULT 'published',
    version             INT          NOT NULL DEFAULT 1,
    thumbnail_oss_key   VARCHAR(255),
    backdrop_options    JSONB        NOT NULL DEFAULT '[]'::jsonb,
    render_config       JSONB        NOT NULL DEFAULT '{}'::jsonb,
    engine_min_version  VARCHAR(32)  NOT NULL DEFAULT 'compositor-1.0.0',
    sort_order          INT          NOT NULL DEFAULT 0,
    star_id             BIGINT       NOT NULL DEFAULT 0,
    created_by          BIGINT       NOT NULL DEFAULT 0,
    created_at          BIGINT       NOT NULL,
    updated_at          BIGINT       NOT NULL,
    deleted_at          BIGINT
);

CREATE UNIQUE INDEX IF NOT EXISTS uk_lct_code_version
    ON public.laser_card_templates (template_code, version)
    WHERE deleted_at IS NULL;

CREATE INDEX IF NOT EXISTS idx_lct_status_star
    ON public.laser_card_templates (status, star_id)
    WHERE deleted_at IS NULL;

-- -----------------------------------------------------------------------------
-- 2. laser_card_instances
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.laser_card_instances (
    id                      BIGSERIAL PRIMARY KEY,
    instance_no             VARCHAR(32)  NOT NULL,
    instance_ulid           VARCHAR(40)  NOT NULL,
    template_id             BIGINT       NOT NULL,
    template_code           VARCHAR(64)  NOT NULL,
    template_version        INT          NOT NULL,
    owner_user_id           BIGINT       NOT NULL,
    star_id                 BIGINT       NOT NULL,
    status                  VARCHAR(20)  NOT NULL DEFAULT 'rendered',
    client_request_id       VARCHAR(64),
    render_config           JSONB,
    materials_snapshot      JSONB        NOT NULL DEFAULT '[]'::jsonb,
    composite_oss_key       VARCHAR(255),
    composite_material_id   BIGINT,
    asset_id                BIGINT,
    mint_order_id           VARCHAR(100),
    idempotency_key         VARCHAR(64),
    version                 INT          NOT NULL DEFAULT 1,
    created_at              BIGINT       NOT NULL,
    updated_at              BIGINT       NOT NULL,
    deleted_at              BIGINT,

    CONSTRAINT fk_lci_template
        FOREIGN KEY (template_id) REFERENCES public.laser_card_templates (id),
    CONSTRAINT fk_lci_asset
        FOREIGN KEY (asset_id) REFERENCES public.assets (id) ON DELETE SET NULL,
    CONSTRAINT fk_lci_mint_order
        FOREIGN KEY (mint_order_id) REFERENCES public.mint_orders (order_id) ON DELETE SET NULL,
    CONSTRAINT fk_lci_composite_material
        FOREIGN KEY (composite_material_id) REFERENCES public.materials (id) ON DELETE SET NULL,
    CONSTRAINT chk_lci_status
        CHECK (status IN ('rendered', 'minting', 'minted'))
);

CREATE UNIQUE INDEX IF NOT EXISTS uk_lci_instance_no
    ON public.laser_card_instances (instance_no);

CREATE UNIQUE INDEX IF NOT EXISTS uk_lci_instance_ulid
    ON public.laser_card_instances (instance_ulid);

CREATE UNIQUE INDEX IF NOT EXISTS uk_lci_user_client_req
    ON public.laser_card_instances (owner_user_id, client_request_id)
    WHERE deleted_at IS NULL AND client_request_id IS NOT NULL;

CREATE INDEX IF NOT EXISTS idx_lci_owner_status
    ON public.laser_card_instances (owner_user_id, status)
    WHERE deleted_at IS NULL;

CREATE INDEX IF NOT EXISTS idx_lci_asset
    ON public.laser_card_instances (asset_id)
    WHERE asset_id IS NOT NULL AND deleted_at IS NULL;

CREATE INDEX IF NOT EXISTS idx_lci_star_created
    ON public.laser_card_instances (star_id, created_at DESC)
    WHERE deleted_at IS NULL;

-- -----------------------------------------------------------------------------
-- 3. laser_card_operation_logs
-- -----------------------------------------------------------------------------
CREATE TABLE IF NOT EXISTS public.laser_card_operation_logs (
    id                  BIGSERIAL PRIMARY KEY,
    instance_id         BIGINT       NOT NULL,
    instance_no         VARCHAR(32)  NOT NULL,
    operator_user_id    BIGINT       NOT NULL,
    action              VARCHAR(50)  NOT NULL,
    status_before       VARCHAR(20),
    status_after        VARCHAR(20),
    request_id          VARCHAR(64),
    payload_json        JSONB,
    result_json         JSONB,
    ip_address          VARCHAR(45),
    user_agent          VARCHAR(255),
    latency_ms          INT,
    err_code            VARCHAR(32),
    created_at          BIGINT       NOT NULL,

    CONSTRAINT fk_lclog_instance
        FOREIGN KEY (instance_id) REFERENCES public.laser_card_instances (id) ON DELETE CASCADE
);

CREATE INDEX IF NOT EXISTS idx_lclog_instance_time
    ON public.laser_card_operation_logs (instance_id, created_at DESC);

CREATE INDEX IF NOT EXISTS idx_lclog_operator_time
    ON public.laser_card_operation_logs (operator_user_id, created_at DESC);

CREATE INDEX IF NOT EXISTS idx_lclog_action_time
    ON public.laser_card_operation_logs (action, created_at DESC);

-- -----------------------------------------------------------------------------
-- 4. 表 / 字段 / 索引 / 约束备注（COMMENT，可重复执行）
-- -----------------------------------------------------------------------------
\ir migrate_laser_card_v3_comments.sql

-- -----------------------------------------------------------------------------
-- 5. 种子数据：5 套预设（与 frontend/utils/laser-card/laserPresets.js 对齐）
-- -----------------------------------------------------------------------------
INSERT INTO public.laser_card_templates (
    template_code,
    name,
    status,
    version,
    backdrop_options,
    render_config,
    engine_min_version,
    sort_order,
    star_id,
    created_by,
    created_at,
    updated_at
)
SELECT
    v.template_code,
    v.name,
    'published',
    1,
    v.backdrop_options::jsonb,
    v.render_config::jsonb,
    'compositor-1.0.0',
    v.sort_order,
    0,
    0,
    (EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint,
    (EXTRACT(EPOCH FROM clock_timestamp()) * 1000)::bigint
FROM (
    VALUES
        (
            'dream',
            '梦幻',
            0,
            '[{"id":"liquidBlue","label":"液态蓝","oss_key":"static/laser-bg/laser-bg-1.png"}]',
            '{"style":"dream","angle":36,"beam":"prism","backdrop":"liquidBlue","laserStrength":78,"diffractionDensity":68,"marbleFlow":52,"beamIntensity":92,"grainDensity":82}'
        ),
        (
            'classic',
            '经典',
            1,
            '[{"id":"liquidLavender","label":"液态紫","oss_key":"static/laser-bg/laser-bg-2.png"}]',
            '{"style":"classic","angle":24,"beam":"aurora","backdrop":"liquidLavender","laserStrength":85,"diffractionDensity":74,"marbleFlow":38,"beamIntensity":96,"grainDensity":76}'
        ),
        (
            'holoFull',
            '全息',
            2,
            '[{"id":"liquidPearl","label":"液态珍珠","oss_key":"static/laser-bg/laser-bg-3.png"}]',
            '{"style":"holoFull","angle":58,"beam":"neon","backdrop":"liquidPearl","laserStrength":90,"diffractionDensity":80,"marbleFlow":58,"beamIntensity":100,"grainDensity":88}'
        ),
        (
            'ice',
            '冰蓝',
            3,
            '[{"id":"liquidBlue","label":"液态蓝","oss_key":"static/laser-bg/laser-bg-1.png"}]',
            '{"style":"ice","angle":46,"beam":"white","backdrop":"liquidBlue","laserStrength":72,"diffractionDensity":60,"marbleFlow":44,"beamIntensity":88,"grainDensity":74}'
        ),
        (
            'sunset',
            '晚霞',
            4,
            '[{"id":"liquidLavender","label":"液态紫","oss_key":"static/laser-bg/laser-bg-2.png"}]',
            '{"style":"sunset","angle":30,"beam":"sunset","backdrop":"liquidLavender","laserStrength":82,"diffractionDensity":66,"marbleFlow":50,"beamIntensity":94,"grainDensity":84}'
        )
) AS v (template_code, name, sort_order, backdrop_options, render_config)
WHERE NOT EXISTS (
    SELECT 1
    FROM public.laser_card_templates t
    WHERE t.template_code = v.template_code
      AND t.version = 1
      AND t.deleted_at IS NULL
);

COMMIT;
