-- ============================================================================
-- V50 — 입장권(티켓) 도메인: 상품 · 주문 · 발권 (M10 관람객 · M9 정산)
-- ============================================================================
-- 공개(비로그인) 입장권 예매/조회 백엔드. GAP G-02 해소.
-- 표준: tenant_id = 'KINTEX'(대문자), 복합 PK 선두. FK 최소화(컬럼만, 제약 없음).
-- 재고 차감은 주문 시점 트랜잭션 — 조건부 UPDATE(sold_qty += qty WHERE total-sold >= qty)로 oversell 방지.
-- PII 최소화(§0-3): 주문에는 구매자 원문을 저장하지 않는다.
--   buyer_name_masked(홍*동)·buyer_contact_masked(010-****-1234)·buyer_email_masked(jo***@ex***.com) 만 저장,
--   조회 대조는 buyer_contact_hash = sha256(digits(contact)) 만으로 수행(원문 미보관).
-- ============================================================================

-- ── 1) 티켓 상품 ─────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS ticket_product (
    tenant_id     varchar(32)  NOT NULL DEFAULT 'KINTEX',
    id            varchar(64)  NOT NULL,
    event_id      varchar(64)  NOT NULL,
    code          varchar(48)  NOT NULL,
    kind          varchar(24)  NOT NULL DEFAULT 'GENERAL',  -- GENERAL/STUDENT/GROUP/VIP/BUYER
    name          varchar(160) NOT NULL,
    description   varchar(400),
    price         numeric(12,0) NOT NULL DEFAULT 0,
    currency      varchar(8)   NOT NULL DEFAULT 'KRW',
    sale_start    timestamptz,
    sale_end      timestamptz,
    total_qty     integer      NOT NULL DEFAULT 0,
    sold_qty      integer      NOT NULL DEFAULT 0,
    max_per_order integer      NOT NULL DEFAULT 10,
    status        varchar(16)  NOT NULL DEFAULT 'active',   -- active/closed
    sort_order    integer      NOT NULL DEFAULT 0,
    created_at    timestamptz  NOT NULL DEFAULT now(),
    CONSTRAINT pk_ticket_product PRIMARY KEY (tenant_id, id),
    CONSTRAINT ck_ticket_product_sold CHECK (sold_qty >= 0 AND sold_qty <= total_qty)
);
CREATE INDEX IF NOT EXISTS ix_ticket_product_event ON ticket_product (tenant_id, event_id, status, sort_order);

-- ── 2) 티켓 주문(예매) ───────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS ticket_order (
    tenant_id           varchar(32)  NOT NULL DEFAULT 'KINTEX',
    id                  varchar(64)  NOT NULL,
    order_no            varchar(40)  NOT NULL,
    event_id            varchar(64)  NOT NULL,
    product_id          varchar(64)  NOT NULL,
    product_name        varchar(160) NOT NULL,          -- 주문 시점 스냅샷
    qty                 integer      NOT NULL,
    unit_price          numeric(12,0) NOT NULL,
    total_amount        numeric(12,0) NOT NULL,
    status              varchar(16)  NOT NULL DEFAULT 'PENDING',  -- PENDING/PAID/CANCELLED
    pay_method          varchar(24),                    -- card/easy/bank/free
    pay_approval_no     varchar(64),
    buyer_name_masked   varchar(80),
    buyer_contact_masked varchar(40),
    buyer_email_masked  varchar(160),
    buyer_contact_hash  varchar(64),                    -- sha256(digits(contact)) hex
    ordered_at          timestamptz  NOT NULL DEFAULT now(),
    paid_at             timestamptz,
    cancelled_at        timestamptz,
    CONSTRAINT pk_ticket_order PRIMARY KEY (tenant_id, id),
    CONSTRAINT uq_ticket_order_no UNIQUE (order_no),
    CONSTRAINT ck_ticket_order_qty CHECK (qty > 0)
);
CREATE INDEX IF NOT EXISTS ix_ticket_order_lookup ON ticket_order (order_no, buyer_contact_hash);
CREATE INDEX IF NOT EXISTS ix_ticket_order_event ON ticket_order (tenant_id, event_id, status);

-- ── 3) 발권(주문 하위 개별 티켓) ─────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS ticket_issue (
    tenant_id   varchar(32)  NOT NULL DEFAULT 'KINTEX',
    id          varchar(64)  NOT NULL,
    order_id    varchar(64)  NOT NULL,
    ticket_code varchar(48)  NOT NULL,
    seq         integer      NOT NULL,
    used        boolean      NOT NULL DEFAULT false,
    used_at     timestamptz,
    issued_at   timestamptz  NOT NULL DEFAULT now(),
    CONSTRAINT pk_ticket_issue PRIMARY KEY (tenant_id, id),
    CONSTRAINT uq_ticket_issue_code UNIQUE (ticket_code)
);
CREATE INDEX IF NOT EXISTS ix_ticket_issue_order ON ticket_issue (tenant_id, order_id, seq);

-- ============================================================================
-- 시드 — 데모 해소 행사 e-2026-live 를 축으로 폐루프(빈 화면 방지). 멱등(ON CONFLICT DO NOTHING).
-- ============================================================================

-- 상품: e-2026-live 3종(일반/학생/단체) + e-2026-smf 2종 + bd-e-001 1종
INSERT INTO ticket_product
    (tenant_id, id, event_id, code, kind, name, description, price, sale_start, sale_end,
     total_qty, sold_qty, max_per_order, status, sort_order) VALUES
  ('KINTEX','tp-live-gen','e-2026-live','LIVE-GEN','GENERAL','일반권',
     '일반 관람객 및 개인 참가자 · 전 전시장 입장',            15000, now()-interval '30 day', now()+interval '40 day',
     5000, 1242, 10, 'active', 0),
  ('KINTEX','tp-live-stu','e-2026-live','LIVE-STU','STUDENT','학생/군인 할인권',
     '학생·군인 대상 할인(현장 신분 확인)',                     8000, now()-interval '30 day', now()+interval '40 day',
     2000, 321, 6, 'active', 1),
  ('KINTEX','tp-live-grp','e-2026-live','LIVE-GRP','GROUP','단체권 (10매 이상)',
     '기업·기관 단체 관람 전용',                                9000, now()-interval '30 day', now()+interval '40 day',
     3000, 160, 100, 'active', 2),
  ('KINTEX','tp-smf-gen','e-2026-smf','SMF-GEN','GENERAL','일반권',
     '스마트팩토리 코리아 일반 입장권',                        12000, now()-interval '10 day', now()+interval '60 day',
     4000, 512, 10, 'active', 0),
  ('KINTEX','tp-smf-vip','e-2026-smf','SMF-VIP','VIP','VIP 올패스',
     '전 세션 + VIP 라운지 이용',                               50000, now()-interval '10 day', now()+interval '60 day',
     200, 47, 4, 'active', 1),
  ('KINTEX','tp-bd001-gen','bd-e-001','BD001-GEN','GENERAL','일반권',
     '기본 입장권',                                             10000, now()-interval '5 day', now()+interval '90 day',
     1500, 88, 10, 'active', 0)
ON CONFLICT (tenant_id, id) DO NOTHING;

-- 데모 주문(PAID) — PII 원문 미보관. 조회 대조 hash = sha256(연락처 숫자만).
--   조회 테스트: 예매번호 + 연락처(하이픈 무관)로 lookup.
--   KTX-2026-500001 / 010-1234-5678,  KTX-2026-500002 / 010-2222-3333,  KTX-2026-500003 / 010-9999-0000
INSERT INTO ticket_order
    (tenant_id, id, order_no, event_id, product_id, product_name, qty, unit_price, total_amount,
     status, pay_method, pay_approval_no, buyer_name_masked, buyer_contact_masked, buyer_email_masked,
     buyer_contact_hash, ordered_at, paid_at) VALUES
  ('KINTEX','to-live-500001','KTX-2026-500001','e-2026-live','tp-live-gen','일반권',
     2, 15000, 30000, 'PAID', 'card', 'MOCK-DEMO-500001',
     '김*준','010-****-5678','mi***@example.com',
     encode(sha256(convert_to('01012345678','UTF8')),'hex'), now()-interval '3 day', now()-interval '3 day'),
  ('KINTEX','to-live-500002','KTX-2026-500002','e-2026-live','tp-live-stu','학생/군인 할인권',
     1, 8000, 8000, 'PAID', 'easy', 'MOCK-DEMO-500002',
     '이*연','010-****-3333','se***@example.com',
     encode(sha256(convert_to('01022223333','UTF8')),'hex'), now()-interval '2 day', now()-interval '2 day'),
  ('KINTEX','to-live-500003','KTX-2026-500003','e-2026-live','tp-live-grp','단체권 (10매 이상)',
     10, 9000, 90000, 'PAID', 'bank', 'MOCK-DEMO-500003',
     '박*윤','010-****-0000','do***@example.com',
     encode(sha256(convert_to('01099990000','UTF8')),'hex'), now()-interval '1 day', now()-interval '1 day')
ON CONFLICT (tenant_id, id) DO NOTHING;

-- 발권 — 주문 수량만큼 생성(generate_series, 멱등). ticket_code 유니크.
INSERT INTO ticket_issue (tenant_id, id, order_id, ticket_code, seq)
SELECT 'KINTEX',
       'ti-500001-' || lpad(g::text, 2, '0'),
       'to-live-500001',
       'TK-500001-' || lpad(g::text, 2, '0'),
       g
FROM generate_series(1, 2) g
ON CONFLICT (tenant_id, id) DO NOTHING;

INSERT INTO ticket_issue (tenant_id, id, order_id, ticket_code, seq)
SELECT 'KINTEX',
       'ti-500002-' || lpad(g::text, 2, '0'),
       'to-live-500002',
       'TK-500002-' || lpad(g::text, 2, '0'),
       g
FROM generate_series(1, 1) g
ON CONFLICT (tenant_id, id) DO NOTHING;

INSERT INTO ticket_issue (tenant_id, id, order_id, ticket_code, seq)
SELECT 'KINTEX',
       'ti-500003-' || lpad(g::text, 2, '0'),
       'to-live-500003',
       'TK-500003-' || lpad(g::text, 2, '0'),
       g
FROM generate_series(1, 10) g
ON CONFLICT (tenant_id, id) DO NOTHING;
