-- V44: 소프트 참조·핫 조회 경로 성능 인덱스 (멱등 · 순수 인덱스 · 스키마 불변)
-- 배경: 데이터 표준이 물리 FK 최소화 → PostgreSQL 은 FK/소프트참조 컬럼에 인덱스를 자동 생성하지 않는다.
--       조인/필터/정렬에 실제로 쓰이는 소프트 참조(*_id)·상태(공통코드값)·기간 컬럼 중, 기존 인덱스·
--       PK((tenant_id,id))·UNIQUE 제약 자동 인덱스로 커버되지 않는 누락분만 실사용 쿼리 근거로 추가한다.
-- 원칙: 전부 CREATE INDEX IF NOT EXISTS(멱등) · 단일 트랜잭션 · 순증 · 중복/PK커버 조합 제외.
-- 소유: 성능 인덱스 마이그레이션의 단일 소유 파일(DA 감사 트랙은 별도 인덱스 파일을 만들지 않음).
-- 신규 테이블/컬럼 없음 — V43 다음 순번.

-- ─────────────────────────────────────────────────────────────────────────────
-- 1) hall_assignment(event_id, is_primary) — 최고 빈도 핫패스
--    근거: 이벤트 목록의 "대표 홀" LATERAL 서브쿼리 —
--          DashboardMapper.listEvents / EventCatalogMapper.list / HomeMapper /
--          MarketingMapper / OpsMapper.myEvents 의
--          "SELECT hall_id FROM hall_assignment ha WHERE ha.event_id = e.id ORDER BY is_primary DESC LIMIT 1",
--          PublicSiteMapper "LEFT JOIN hall_assignment ha ON ha.event_id = e.id AND ha.is_primary = true",
--          HallAssignMapper "WHERE ha.event_id = #{eventId} ORDER BY ha.is_primary DESC".
--    기존 idx_hall_assignment_hall_period 는 (hall_id, occupy_start, occupy_end) 로 event_id 미커버.
CREATE INDEX IF NOT EXISTS idx_hall_assignment_event
    ON hall_assignment (event_id, is_primary DESC);

-- ─────────────────────────────────────────────────────────────────────────────
-- 2) event_member(company_id, event_id) — 업체 스코프 조인
--    근거: AuctionMapper 초대 입찰자 조회
--          "JOIN event_member em ON em.company_id = ai.company_id AND em.event_id = (SELECT event_id FROM auction ...)",
--          OpsMapper.summary awardedBooths "EXISTS(SELECT 1 FROM event_member m WHERE m.company_id = b.company_id ...)".
--    기존 idx_event_member_user(user_id) · UNIQUE(event_id,user_id,role_code) 는 company_id 선두 미커버.
CREATE INDEX IF NOT EXISTS idx_event_member_company
    ON event_member (company_id, event_id);

-- ─────────────────────────────────────────────────────────────────────────────
-- 3) bid(company_id) — 계약사(입찰업체) 내 입찰 조회
--    근거: AuctionMapper.contractorMyBids "FROM bid b JOIN auction a ON a.id = b.auction_id WHERE b.company_id = #{companyId} ORDER BY a.deadline".
--    기존 idx_bid_auction(auction_id) · UNIQUE(auction_id,company_id) 는 company_id 선두 미커버.
CREATE INDEX IF NOT EXISTS idx_bid_company
    ON bid (company_id);

-- ─────────────────────────────────────────────────────────────────────────────
-- 4) tax_invoice(invoice_id) — 정산 세금계산서 역참조
--    근거: SettlementMapper.getTaxInvoiceByInvoice "FROM tax_invoice WHERE invoice_id = #{invoiceId}".
--    기존 인덱스는 (event_id) 만 존재 → invoice_id(소프트 FK) 미커버.
CREATE INDEX IF NOT EXISTS idx_tax_invoice_invoice
    ON tax_invoice (invoice_id);

-- ─────────────────────────────────────────────────────────────────────────────
-- 5) message(sender_id, created_at) — 쪽지 보낸함
--    근거: MessageMapper.listSent "FROM message m WHERE m.sender_id = #{userId} ORDER BY m.created_at DESC LIMIT/OFFSET".
--    message 테이블엔 인덱스 없음(수신함은 message_recipient(recipient_id,read_at) 로 커버).
CREATE INDEX IF NOT EXISTS idx_message_sender
    ON message (sender_id, created_at DESC);

-- ─────────────────────────────────────────────────────────────────────────────
-- 6) cms_content 공개 게시 목록 — 상태(공통코드값) 필터 + 타입 + 게시일 정렬
--    근거: CmsMapper.listPublished(공개 홍보사이트)
--          "WHERE status = 'published' AND content_type = #{type} ORDER BY published_at DESC NULLS LAST".
--    기존 idx_cms_content_status(status) 는 content_type/published_at 정렬 미커버. 게시분만 부분 인덱스로 경량화.
CREATE INDEX IF NOT EXISTS idx_cms_content_published
    ON cms_content (content_type, published_at DESC)
    WHERE status = 'published';

-- ─────────────────────────────────────────────────────────────────────────────
-- 7) visitor_registration(event_id, checkin_state) — 체크인 상태(공통코드값) 필터
--    근거: VisitorMapper.checkinHourly
--          "WHERE event_id = #{eventId} AND checkin_state = 'done' AND checkin_at IS NOT NULL ORDER BY to_char(checkin_at,'HH24')".
--    대용량(관람객 등록) 테이블에서 (event_id) 만으로는 상태 서브셋 필터 비용 큼. 기존 (event_id,registered_at) 와 별개.
CREATE INDEX IF NOT EXISTS idx_visitor_reg_checkin_state
    ON visitor_registration (event_id, checkin_state);

-- ─────────────────────────────────────────────────────────────────────────────
-- 8) lead 팔로업 스윕 — hot 리드(score>=80) 전역 조회(이벤트 무관 배치)
--    근거: FollowupMapper.findUnprocessedHotLeads
--          "FROM lead l LEFT JOIN edm_followup_log f ... WHERE l.score >= 80 AND f.id IS NULL ORDER BY l.score DESC, l.id".
--    기존 idx_lead_score 는 (event_id, score DESC) — event_id 선두라 전역 score 범위스캔에 부적합 → 부분 인덱스.
--    (조인측 edm_followup_log 는 UNIQUE(event_id,lead_id) 자동 인덱스로 이미 커버.)
CREATE INDEX IF NOT EXISTS idx_lead_hot_followup
    ON lead (score DESC)
    WHERE score >= 80;
