package com.zioinfo.kintex.publicsite.subscription;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;
import java.util.Map;

/**
 * 관심 행사 구독(F-C1) 매퍼.
 * <p>★ Map 반환 @Select 는 camelCase 별칭에 쌍따옴표({@code AS "x"})를 사용한다(PG lower-fold 방지).
 * <p>★ MyBatis 함정 회피: raw {@code <}/{@code <=} 미사용 — 시각 비교는 {@code now() >= sale_start} 로 표현.
 * <p>원문 email 은 발송 경로(스케줄러)로만 반환 — 서비스가 응답/로그에 노출하지 않는다.
 */
@Mapper
public interface EventSubscriptionMapper {

    /** 대상 행사 존재 여부(공개 구독 검증). */
    @Select("SELECT count(*) FROM event WHERE id = #{eventId}")
    int eventExists(@Param("eventId") String eventId);

    /** 행사 종료 여부 — 종료된 행사는 구독 불가(오픈 예정/진행 대상만). end_date 없으면 진행중 간주. */
    @Select("""
            SELECT CASE WHEN end_date IS NOT NULL AND end_date < current_date
                        THEN 1 ELSE 0 END
            FROM event WHERE id = #{eventId}
            """)
    Integer isEnded(@Param("eventId") String eventId);

    /** 동일 행사+이메일 기존 구독 조회(대소문자 무시). */
    @Select("""
            SELECT id, status, unsub_token AS "unsubToken",
                   to_char(notified_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "notifiedAt"
            FROM event_subscription
            WHERE tenant_id = 'KINTEX' AND event_id = #{eventId} AND lower(email) = lower(#{email})
            """)
    Map<String, Object> findByEventEmail(@Param("eventId") String eventId,
                                         @Param("email") String email);

    /** 동일 이메일 최근 window(초) 내 구독 건수 — 단순 rate 제어. */
    @Select("""
            SELECT count(*)::int FROM event_subscription
            WHERE lower(email) = lower(#{email})
              AND created_at > now() - make_interval(secs => #{windowSeconds})
            """)
    int countRecentByEmail(@Param("email") String email, @Param("windowSeconds") int windowSeconds);

    /** 신규 구독 저장. */
    @Insert("""
            INSERT INTO event_subscription
                (tenant_id, id, event_id, email, email_masked, unsub_token, status)
            VALUES ('KINTEX', #{id}, #{eventId}, #{email}, #{emailMasked}, #{unsubToken}, 'active')
            """)
    int insert(@Param("id") String id,
               @Param("eventId") String eventId,
               @Param("email") String email,
               @Param("emailMasked") String emailMasked,
               @Param("unsubToken") String unsubToken);

    /** 수신거부되었던 구독 재활성화(토큰 유지). 티켓 오픈이 아직이면 재알림 대상이 되도록 notified_at 은 유지. */
    @Update("""
            UPDATE event_subscription SET status = 'active'
            WHERE tenant_id = 'KINTEX' AND id = #{id} AND status <> 'active'
            """)
    int reactivate(@Param("id") String id);

    /** 원클릭 수신거부(멱등). 활성 건만 전이. */
    @Update("""
            UPDATE event_subscription SET status = 'unsubscribed'
            WHERE unsub_token = #{token} AND status = 'active'
            """)
    int unsubscribeByToken(@Param("token") String token);

    /** 토큰 존재 여부(이미 처리된 건 포함) — 멱등 성공/실패 구분용. */
    @Select("SELECT count(*) FROM event_subscription WHERE unsub_token = #{token}")
    int tokenExists(@Param("token") String token);

    /**
     * 티켓 판매가 개시된(오픈) 행사의 미통지 활성 구독 — 오픈 감지 스케줄러 대상.
     * 오픈 판정: 해당 행사에 status='active', total_qty>0, {@code now() >= sale_start} 인 티켓 상품이 1건 이상.
     * email 원문은 발송 경로 한정(서비스가 노출 금지).
     */
    @Select("""
            SELECT s.id, s.event_id AS "eventId", s.email, s.unsub_token AS "unsubToken",
                   e.name AS "eventName"
            FROM event_subscription s
            JOIN event e ON e.id = s.event_id
            WHERE s.tenant_id = 'KINTEX'
              AND s.status = 'active'
              AND s.notified_at IS NULL
              AND EXISTS (
                    SELECT 1 FROM ticket_product tp
                    WHERE tp.event_id = s.event_id
                      AND tp.status = 'active'
                      AND tp.total_qty > 0
                      AND tp.sale_start IS NOT NULL
                      AND now() >= tp.sale_start
              )
            ORDER BY s.created_at
            LIMIT #{limit}
            """)
    List<Map<String, Object>> findPendingTicketOpen(@Param("limit") int limit);

    /** 통지 완료 마킹(조건부 — 이미 통지된 건은 0). 중복 발송 방지. */
    @Update("""
            UPDATE event_subscription SET notified_at = now()
            WHERE tenant_id = 'KINTEX' AND id = #{id} AND notified_at IS NULL
            """)
    int markNotified(@Param("id") String id);
}
