-- Petstore Phase 0: persist immutable business events for funnels and timelines.
-- Target: MySQL. Run after 20260801_create_store_customer.sql.
-- One-time migration; back up the database before execution.

CREATE TABLE t_business_event (
    id BIGINT NOT NULL AUTO_INCREMENT,
    event_id VARCHAR(36) NOT NULL COMMENT '随机事件 ID',
    event_type VARCHAR(64) NOT NULL COMMENT '过去时业务事件类型',
    event_version INT NOT NULL DEFAULT 1,
    store_id BIGINT NOT NULL,
    store_customer_id BIGINT NULL COMMENT '门店客户稳定关系，可空',
    aggregate_type VARCHAR(32) NOT NULL,
    aggregate_id BIGINT NOT NULL,
    actor_user_id BIGINT NULL,
    actor_role VARCHAR(16) NULL,
    source VARCHAR(32) NOT NULL,
    occurred_at DATETIME(3) NOT NULL,
    metadata_json TEXT NOT NULL COMMENT '仅低敏白名单维度 JSON',
    idempotency_key VARCHAR(128) NULL,
    create_time DATETIME(3) NOT NULL,
    PRIMARY KEY (id),
    UNIQUE KEY uk_business_event_event_id (event_id),
    UNIQUE KEY uk_business_event_idempotency (idempotency_key),
    KEY idx_be_store_type_time (store_id, event_type, occurred_at),
    KEY idx_be_aggregate_time (aggregate_type, aggregate_id, occurred_at),
    KEY idx_be_store_customer_time (store_customer_id, occurred_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='不可变业务事件';

-- 历史预约只回填可确定的“预约已创建”，不猜测无法还原的开始服务时间。
INSERT INTO t_business_event (
    event_id, event_type, event_version, store_id, store_customer_id,
    aggregate_type, aggregate_id, actor_user_id, actor_role, source,
    occurred_at, metadata_json, idempotency_key, create_time
)
SELECT
    UUID(),
    'appointment_created',
    1,
    a.store_id,
    sc.id,
    'appointment',
    a.id,
    a.created_by_user_id,
    actor.role,
    CASE
        WHEN a.created_by_user_id IS NULL THEN 'migration'
        WHEN a.created_by_user_id = a.customer_user_id THEN 'customer'
        ELSE 'admin'
    END,
    COALESCE(a.create_time, a.appointment_time, NOW()),
    CASE
        WHEN a.created_by_user_id IS NULL
            THEN '{"bookingOrigin":"migration"}'
        WHEN a.created_by_user_id = a.customer_user_id
            THEN '{"bookingOrigin":"customer"}'
        ELSE '{"bookingOrigin":"admin"}'
    END,
    CONCAT('appointment_created:', a.id),
    NOW()
FROM t_appointment a
LEFT JOIN t_store_customer sc
    ON sc.store_id = a.store_id
   AND sc.customer_user_id = a.customer_user_id
   AND sc.deleted = 0
LEFT JOIN t_user actor
    ON actor.id = a.created_by_user_id
   AND actor.deleted = 0
WHERE a.deleted = 0
  AND a.store_id IS NOT NULL;

-- 历史报告提交是可靠事实；不保存 report_token 或其 hash。
INSERT INTO t_business_event (
    event_id, event_type, event_version, store_id, store_customer_id,
    aggregate_type, aggregate_id, actor_user_id, actor_role, source,
    occurred_at, metadata_json, idempotency_key, create_time
)
SELECT
    UUID(),
    'report_submitted',
    1,
    r.store_id,
    sc.id,
    'report',
    r.id,
    r.author_staff_id,
    actor.role,
    CASE WHEN r.author_staff_id IS NULL THEN 'migration' ELSE 'admin' END,
    COALESCE(r.create_time, NOW()),
    '{}',
    CONCAT('report_submitted:', r.id),
    NOW()
FROM t_report r
LEFT JOIN t_store_customer sc
    ON sc.store_id = r.store_id
   AND sc.customer_user_id = r.customer_user_id
   AND sc.deleted = 0
LEFT JOIN t_user actor
    ON actor.id = r.author_staff_id
   AND actor.deleted = 0
WHERE r.deleted = 0
  AND r.store_id IS NOT NULL;

-- 有报告的完成时间可由报告创建时间可靠近似；无报告的历史 done 不做猜测。
INSERT INTO t_business_event (
    event_id, event_type, event_version, store_id, store_customer_id,
    aggregate_type, aggregate_id, actor_user_id, actor_role, source,
    occurred_at, metadata_json, idempotency_key, create_time
)
SELECT
    UUID(),
    'service_completed',
    1,
    r.store_id,
    sc.id,
    'appointment',
    r.appointment_id,
    r.author_staff_id,
    actor.role,
    CASE WHEN r.author_staff_id IS NULL THEN 'migration' ELSE 'admin' END,
    COALESCE(r.create_time, NOW()),
    '{}',
    CONCAT('service_completed:', r.appointment_id),
    NOW()
FROM t_report r
LEFT JOIN t_store_customer sc
    ON sc.store_id = r.store_id
   AND sc.customer_user_id = r.customer_user_id
   AND sc.deleted = 0
LEFT JOIN t_user actor
    ON actor.id = r.author_staff_id
   AND actor.deleted = 0
WHERE r.deleted = 0
  AND r.store_id IS NOT NULL
  AND r.appointment_id IS NOT NULL;

-- 历史留资每条只形成一个 lead_submitted 事实；不复制手机号、IP、openid 或退订 token。
INSERT INTO t_business_event (
    event_id, event_type, event_version, store_id, store_customer_id,
    aggregate_type, aggregate_id, actor_user_id, actor_role, source,
    occurred_at, metadata_json, idempotency_key, create_time
)
SELECT
    UUID(),
    'lead_submitted',
    1,
    l.store_id,
    sc.id,
    'report_lead',
    l.id,
    NULL,
    NULL,
    'public_report',
    COALESCE(l.consent_at, l.create_time, l.update_time, NOW()),
    '{"repeatSubmit":false}',
    CONCAT('lead_submitted:', l.id),
    NOW()
FROM t_report_lead l
LEFT JOIN t_store_customer sc
    ON sc.store_id = l.store_id
   AND sc.phone = l.phone
   AND sc.deleted = 0
WHERE l.store_id IS NOT NULL;

-- Verification queries. All four counts should be 0 before application release.
SELECT COUNT(*) AS appointments_without_created_event
FROM t_appointment a
LEFT JOIN t_business_event e
    ON e.idempotency_key = CONCAT('appointment_created:', a.id)
WHERE a.deleted = 0
  AND a.store_id IS NOT NULL
  AND e.id IS NULL;

SELECT COUNT(*) AS reports_without_submitted_event
FROM t_report r
LEFT JOIN t_business_event e
    ON e.idempotency_key = CONCAT('report_submitted:', r.id)
WHERE r.deleted = 0
  AND r.store_id IS NOT NULL
  AND e.id IS NULL;

SELECT COUNT(*) AS leads_without_submitted_event
FROM t_report_lead l
LEFT JOIN t_business_event e
    ON e.idempotency_key = CONCAT('lead_submitted:', l.id)
WHERE l.store_id IS NOT NULL
  AND e.id IS NULL;

SELECT COUNT(*) AS events_without_store
FROM t_business_event
WHERE store_id IS NULL;
