package com.zioinfo.kintex.cms;

import org.apache.ibatis.annotations.*;

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

/**
 * M17 CMS 매퍼 — 콘텐츠 + 번역. ★Map 반환 @Select 별칭은 반드시 쌍따옴표(예: {@code AS "eventId"}).
 * 게시 상태(draft→review→approved→published)·예약 게시(scheduled_at)·번역 상태(none/ai/reviewed).
 */
@Mapper
public interface CmsMapper {

    // ── 콘텐츠 ───────────────────────────────────────────────────────────────
    @Select("""
            <script>
            SELECT id, event_id AS "eventId", content_type AS "contentType", title, body, status, lang,
                   to_char(scheduled_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "scheduledAt",
                   signage, mailing, author_name AS "authorName",
                   to_char(published_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "publishedAt",
                   to_char(created_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "createdAt",
                   to_char(updated_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "updatedAt"
            FROM cms_content
            <where>
              <if test="eventId != null and eventId != ''">AND event_id = #{eventId}</if>
              <if test="status != null and status != ''">AND status = #{status}</if>
              <if test="type != null and type != ''">AND content_type = #{type}</if>
              <if test="keyword != null and keyword != ''">AND title ILIKE '%' || #{keyword} || '%'</if>
            </where>
            ORDER BY updated_at DESC, id DESC
            LIMIT #{size} OFFSET #{offset}
            </script>
            """)
    List<Map<String, Object>> findContents(Map<String, Object> q);

    @Select("""
            <script>
            SELECT count(*) FROM cms_content
            <where>
              <if test="eventId != null and eventId != ''">AND event_id = #{eventId}</if>
              <if test="status != null and status != ''">AND status = #{status}</if>
              <if test="type != null and type != ''">AND content_type = #{type}</if>
              <if test="keyword != null and keyword != ''">AND title ILIKE '%' || #{keyword} || '%'</if>
            </where>
            </script>
            """)
    long countContents(Map<String, Object> q);

    @Select("""
            SELECT id, event_id AS "eventId", content_type AS "contentType", title, body, status, lang,
                   to_char(scheduled_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "scheduledAt",
                   signage, mailing, author_name AS "authorName",
                   to_char(published_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "publishedAt",
                   to_char(created_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "createdAt",
                   to_char(updated_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "updatedAt"
            FROM cms_content WHERE id = #{id}
            """)
    Map<String, Object> findContentById(@Param("id") String id);

    @Insert("""
            INSERT INTO cms_content (id, event_id, content_type, title, body, status, lang, author_id, author_name)
            VALUES (#{id}, #{eventId}, COALESCE(#{contentType},'PAGE'), #{title}, #{body},
                    'draft', COALESCE(#{lang},'ko'), #{authorId}, #{authorName})
            """)
    int insertContent(Map<String, Object> p);

    /** 상태 전이(전이 검증은 서비스). published 전이 시 published_at=now(). */
    @Update("""
            UPDATE cms_content
               SET status = #{status},
                   published_at = CASE WHEN #{status} = 'published' THEN now() ELSE published_at END,
                   updated_at = now()
             WHERE id = #{id}
            """)
    int updateStatus(@Param("id") String id, @Param("status") String status);

    /** 본문/제목/예약·플래그 수정(sanitize 는 서비스). scheduledAt 은 ISO 문자열 또는 null. */
    @Update("""
            UPDATE cms_content
               SET title = #{title},
                   body = #{body},
                   scheduled_at = CAST(#{scheduledAt} AS timestamptz),
                   signage = COALESCE(#{signage}, signage),
                   mailing = COALESCE(#{mailing}, mailing),
                   updated_at = now()
             WHERE id = #{id}
            """)
    int updateContent(Map<String, Object> p);

    /** 롤백 — 특정 버전의 제목/본문/상태로 복원(published 는 published_at 유지). */
    @Update("""
            UPDATE cms_content
               SET title = #{title},
                   body = #{body},
                   status = #{status},
                   updated_at = now()
             WHERE id = #{id}
            """)
    int restoreContent(Map<String, Object> p);

    // ── 버전 이력 ────────────────────────────────────────────────────────────
    @Select("SELECT COALESCE(MAX(version_no),0) FROM cms_content_version WHERE content_id = #{contentId}")
    int maxVersionNo(@Param("contentId") String contentId);

    @Insert("""
            INSERT INTO cms_content_version (id, content_id, version_no, title, body, status, author_name, note)
            VALUES (#{id}, #{contentId}, #{versionNo}, #{title}, #{body}, #{status}, #{authorName}, #{note})
            """)
    int insertVersion(Map<String, Object> p);

    @Select("""
            SELECT id, content_id AS "contentId", version_no AS "versionNo", title, body, status,
                   author_name AS "authorName", note,
                   to_char(created_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "createdAt"
            FROM cms_content_version
            WHERE content_id = #{contentId}
            ORDER BY version_no DESC
            """)
    List<Map<String, Object>> findVersions(@Param("contentId") String contentId);

    @Select("""
            SELECT id, content_id AS "contentId", version_no AS "versionNo", title, body, status,
                   author_name AS "authorName", note,
                   to_char(created_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "createdAt"
            FROM cms_content_version
            WHERE content_id = #{contentId} AND version_no = #{versionNo}
            """)
    Map<String, Object> findVersion(@Param("contentId") String contentId, @Param("versionNo") int versionNo);

    // ── 예약 게시(도달분 자동 게시 — 승인 상태만, @Scheduled 폴러/스윕이 사용) ─────
    /**
     * 예약 시각 도달·승인(approved) 상태인 예약 콘텐츠의 id/event 목록(스윕 상한).
     * ★raw '&lt;' 미사용 — 부팅 크래시 회피 위해 {@code now() >= scheduled_at} 로 표현한다.
     */
    @Select("""
            SELECT id, event_id AS "eventId"
            FROM cms_content
            WHERE scheduled_at IS NOT NULL
              AND now() >= scheduled_at
              AND status = 'approved'
            ORDER BY scheduled_at
            LIMIT #{limit}
            """)
    List<Map<String, Object>> findDueScheduled(@Param("limit") int limit);

    /**
     * 예약 게시 단건 전이(멱등·이중 전이 방지) — approved→published 조건 UPDATE.
     * 반환=영향 행 수(0=이미 게시됨/전이 불가). WHERE 상태 조건으로 동시/재실행 시 이중 전이를 차단한다.
     */
    @Update("""
            UPDATE cms_content
               SET status = 'published', published_at = now(), updated_at = now()
             WHERE id = #{id}
               AND status = 'approved'
               AND scheduled_at IS NOT NULL
               AND now() >= scheduled_at
            """)
    int publishScheduledDue(@Param("id") String id);

    // ── 미디어 라이브러리 ──────────────────────────────────────────────────────
    @Insert("""
            INSERT INTO cms_media (id, event_id, file_name, url, content_type, size_bytes, alt_text, uploader_name)
            VALUES (#{id}, #{eventId}, #{fileName}, #{url}, #{contentType}, #{sizeBytes}, #{altText}, #{uploaderName})
            """)
    int insertMedia(Map<String, Object> p);

    @Select("""
            <script>
            SELECT id, event_id AS "eventId", file_name AS "fileName", url, content_type AS "contentType",
                   size_bytes AS "sizeBytes", alt_text AS "altText", uploader_name AS "uploaderName",
                   to_char(created_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "createdAt"
            FROM cms_media
            <where>
              <if test="eventId != null and eventId != ''">AND event_id = #{eventId}</if>
            </where>
            ORDER BY created_at DESC
            LIMIT #{limit}
            </script>
            """)
    List<Map<String, Object>> findMedia(Map<String, Object> q);

    // ── 공개 조회(게시 상태만) ─────────────────────────────────────────────────
    @Select("""
            <script>
            SELECT id, event_id AS "eventId", content_type AS "contentType", title, body, status, lang,
                   to_char(published_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "publishedAt",
                   to_char(updated_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "updatedAt"
            FROM cms_content
            WHERE status = 'published' AND content_type = #{type}
            <if test="eventId != null and eventId != ''">AND event_id = #{eventId}</if>
            ORDER BY published_at DESC NULLS LAST, updated_at DESC
            LIMIT #{limit}
            </script>
            """)
    List<Map<String, Object>> findPublishedByType(Map<String, Object> q);

    // ── 번역 ─────────────────────────────────────────────────────────────────
    @Select("""
            SELECT content_id AS "contentId", lang, title, body, trans_status AS "transStatus",
                   to_char(updated_at AT TIME ZONE 'UTC','YYYY-MM-DD"T"HH24:MI:SS"Z"') AS "updatedAt"
            FROM cms_translation
            WHERE content_id = #{contentId}
            ORDER BY lang
            """)
    List<Map<String, Object>> findTranslations(@Param("contentId") String contentId);

    /** 언어별 upsert(멱등) — (content_id, lang) 유니크 충돌 시 갱신. */
    @Insert("""
            INSERT INTO cms_translation (id, content_id, lang, title, body, trans_status, updated_at)
            VALUES (#{id}, #{contentId}, #{lang}, #{title}, #{body},
                    COALESCE(#{transStatus},'none'), now())
            ON CONFLICT (content_id, lang) DO UPDATE
               SET title = EXCLUDED.title,
                   body = EXCLUDED.body,
                   trans_status = EXCLUDED.trans_status,
                   updated_at = now()
            """)
    int upsertTranslation(Map<String, Object> p);
}
