package com.pandoli365.bibimbap.mapper;

import com.pandoli365.bibimbap.data.GameData;
import com.pandoli365.bibimbap.data.SearchCriteria;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Options;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

import java.util.List;

@Mapper
public interface GamesMapper {

    @Select("""
            SELECT
                g.id,
                g.user_id AS userId,
                g.name,
                u.display_name AS creator,
                g.creator_note AS creatorNote,
                g.git_url AS gitUrl,
                g.webgl_path AS webglPath,
                g.thumbnail_url AS thumbnailUrl,
                g.like_count AS likeCount,
                g.is_visible AS visible,
                g.sort_order AS sortOrder,
                g.created_at AS createdAt,
                g.updated_at AS updatedAt
            FROM games g
            JOIN users u ON u.id = g.user_id
            WHERE g.id = #{id}
              AND g.is_delete IS NOT TRUE
              AND u.is_delete IS NOT TRUE
            """)
    GameData getGame(long id);

    @Select("""
            SELECT
                g.id,
                g.user_id AS userId,
                g.name,
                u.display_name AS creator,
                g.creator_note AS creatorNote,
                g.git_url AS gitUrl,
                g.webgl_path AS webglPath,
                g.thumbnail_url AS thumbnailUrl,
                g.like_count AS likeCount,
                g.is_visible AS visible,
                g.sort_order AS sortOrder,
                g.created_at AS createdAt,
                g.updated_at AS updatedAt
            FROM games g
            JOIN users u ON u.id = g.user_id
            WHERE g.is_visible IS NOT FALSE
              AND g.is_delete IS NOT TRUE
              AND u.is_delete IS NOT TRUE
            ORDER BY g.sort_order ASC, g.created_at DESC, g.id DESC
            """)
    List<GameData> getVisibleGames();

    @Select("""
            SELECT
                g.id,
                g.user_id AS userId,
                g.name,
                u.display_name AS creator,
                g.creator_note AS creatorNote,
                g.git_url AS gitUrl,
                g.webgl_path AS webglPath,
                g.thumbnail_url AS thumbnailUrl,
                g.like_count AS likeCount,
                g.is_visible AS visible,
                g.sort_order AS sortOrder,
                g.created_at AS createdAt,
                g.updated_at AS updatedAt
            FROM games g
            JOIN users u ON u.id = g.user_id
            WHERE g.is_visible IS NOT FALSE
              AND g.is_delete IS NOT TRUE
              AND u.is_delete IS NOT TRUE
              AND (
                  g.name ILIKE CONCAT('%', #{query}, '%')
                  OR u.display_name ILIKE CONCAT('%', #{query}, '%')
                  OR g.creator_note ILIKE CONCAT('%', #{query}, '%')
              )
            ORDER BY g.sort_order ASC, g.created_at DESC, g.id DESC
            """)
    List<GameData> searchVisibleGames(@Param("query") String query);

    @Select("""
            SELECT
                g.id,
                g.user_id AS userId,
                g.name,
                u.display_name AS creator,
                g.creator_note AS creatorNote,
                g.git_url AS gitUrl,
                g.webgl_path AS webglPath,
                g.thumbnail_url AS thumbnailUrl,
                g.like_count AS likeCount,
                g.is_visible AS visible,
                g.sort_order AS sortOrder,
                g.created_at AS createdAt,
                g.updated_at AS updatedAt
            FROM games g
            JOIN users u ON u.id = g.user_id
            WHERE g.user_id = #{userId}
              AND g.is_delete IS NOT TRUE
              AND u.is_delete IS NOT TRUE
            ORDER BY g.updated_at DESC, g.created_at DESC, g.id DESC
            """)
    List<GameData> getGamesByUserId(@Param("userId") long userId);

    @Insert("""
            INSERT INTO games (
                user_id,
                name,
                creator_note,
                git_url,
                webgl_path,
                thumbnail_url,
                is_visible,
                sort_order
            ) VALUES (
                #{userId},
                #{name},
                #{creatorNote},
                #{gitUrl},
                #{webglPath},
                #{thumbnailUrl},
                #{visible},
                #{sortOrder}
            )
            """)
    @Options(useGeneratedKeys = true, keyProperty = "id", keyColumn = "id")
    int addGame(GameData game);

    @Select("""
            SELECT COALESCE(MAX(sort_order), 0) + 1
            FROM games
            WHERE is_delete IS NOT TRUE
            """)
    int nextSortOrder();

    @Update("""
            UPDATE games
            SET
                user_id = #{userId},
                name = #{name},
                creator_note = #{creatorNote},
                git_url = #{gitUrl},
                webgl_path = #{webglPath},
                thumbnail_url = #{thumbnailUrl},
                is_visible = #{visible},
                sort_order = #{sortOrder},
                updated_at = now()
            WHERE id = #{id}
              AND is_delete IS NOT TRUE
            """)
    int updateGame(GameData game);

    @Update("""
            UPDATE game_comments
            SET
                is_delete = true,
                deleted_at = COALESCE(deleted_at, now())
            WHERE game_id = #{gameId}
              AND is_delete IS NOT TRUE
            """)
    int softDeleteGameComments(@Param("gameId") long gameId);

    @Update("""
            UPDATE game_reviews
            SET
                is_delete = true,
                deleted_at = COALESCE(deleted_at, now())
            WHERE game_id = #{gameId}
              AND is_delete IS NOT TRUE
            """)
    int softDeleteGameReviews(@Param("gameId") long gameId);

    @Delete("""
            DELETE FROM game_likes
            WHERE game_id = #{gameId}
            """)
    int deleteGameLikes(@Param("gameId") long gameId);

    @Update("""
            UPDATE games
            SET
                is_delete = true,
                updated_at = now()
            WHERE id = #{id}
              AND is_delete IS NOT TRUE
            """)
    int softDeleteGame(@Param("id") long id);

    @Select("""
            <script>
            SELECT g.id, g.user_id AS userId, g.name, u.display_name AS creator,
                   g.creator_note AS creatorNote, g.git_url AS gitUrl, g.webgl_path AS webglPath,
                   g.thumbnail_url AS thumbnailUrl, g.like_count AS likeCount, g.is_visible AS visible,
                   g.sort_order AS sortOrder, g.created_at AS createdAt, g.updated_at AS updatedAt,
                   g.view_count AS viewCount,
                   grs.avg_rating  AS "avgRating",
                   grs.review_count AS "reviewCount"
            FROM games g
            JOIN users u ON u.id = g.user_id
            LEFT JOIN game_review_stats grs ON grs.game_id = g.id
            <if test="jamSlug != null">
              JOIN jam_entries je ON je.game_id = g.id AND je.is_delete IS NOT TRUE
              JOIN jams j ON j.id = je.jam_id AND j.slug = #{jamSlug}
            </if>
            WHERE g.is_visible IS NOT FALSE
              AND g.is_delete IS NOT TRUE
              AND u.is_delete IS NOT TRUE
              <if test="keyword != null">
                AND ( g.name ILIKE ('%' || #{keyword} || '%')
                   OR u.display_name ILIKE ('%' || #{keyword} || '%')
                   OR g.creator_note ILIKE ('%' || #{keyword} || '%') )
              </if>
              <if test="creator != null">
                AND u.display_name ILIKE ('%' || #{creator} || '%')
              </if>
              <if test="tags != null and tags.size() > 0 and 'and'.equals(tagMode)">
                AND g.id IN (
                  SELECT gt.game_id FROM game_tags gt
                  JOIN tags t ON t.id = gt.tag_id AND t.is_active = TRUE
                  WHERE t.slug IN <foreach item="s" collection="tags" open="(" separator="," close=")">#{s}</foreach>
                  GROUP BY gt.game_id
                  HAVING COUNT(DISTINCT t.slug) = #{tagCount}
                )
              </if>
              <if test="tags != null and tags.size() > 0 and 'or'.equals(tagMode)">
                AND g.id IN (
                  SELECT gt.game_id FROM game_tags gt
                  JOIN tags t ON t.id = gt.tag_id AND t.is_active = TRUE
                  WHERE t.slug IN <foreach item="s" collection="tags" open="(" separator="," close=")">#{s}</foreach>
                )
              </if>
            ORDER BY
            <choose>
              <when test="'likes'.equals(sort)"> g.like_count DESC, g.id DESC </when>
              <when test="'views'.equals(sort)"> g.view_count DESC, g.id DESC </when>
              <when test="'reviews'.equals(sort)"> grs.review_count DESC NULLS LAST, g.id DESC </when>
              <when test="'rating'.equals(sort)"> grs.avg_rating DESC NULLS LAST, g.id DESC </when>
              <when test="'relevance'.equals(sort) and tags != null and tags.size() > 0">
                ( SELECT COUNT(*) FROM game_tags gt2
                  JOIN tags t2 ON t2.id = gt2.tag_id AND t2.is_active = TRUE
                  WHERE gt2.game_id = g.id
                    AND t2.slug IN <foreach item="s" collection="tags" open="(" separator="," close=")">#{s}</foreach>
                ) DESC, g.created_at DESC, g.id DESC
              </when>
              <otherwise> g.sort_order ASC, g.created_at DESC, g.id DESC </otherwise>
            </choose>
            </script>
            """)
    List<GameData> searchGamesAdvanced(SearchCriteria c);

    @Select("""
            <script>
            SELECT
                g.id,
                g.user_id AS userId,
                g.name,
                u.display_name AS creator,
                g.creator_note AS creatorNote,
                g.git_url AS gitUrl,
                g.webgl_path AS webglPath,
                g.thumbnail_url AS thumbnailUrl,
                g.like_count AS likeCount,
                g.is_visible AS visible,
                g.sort_order AS sortOrder,
                g.created_at AS createdAt,
                g.updated_at AS updatedAt
            FROM games g
            JOIN users u ON u.id = g.user_id
            WHERE g.is_visible IS NOT FALSE
              AND g.is_delete IS NOT TRUE
              AND u.is_delete IS NOT TRUE
            <if test="cursorSortOrder != null and cursorCreatedAt != null and cursorId != null">
              AND (
                  g.sort_order &gt; #{cursorSortOrder}
                  OR (g.sort_order = #{cursorSortOrder} AND g.created_at &lt; #{cursorCreatedAt})
                  OR (g.sort_order = #{cursorSortOrder} AND g.created_at = #{cursorCreatedAt} AND g.id &lt; #{cursorId})
              )
            </if>
            ORDER BY g.sort_order ASC, g.created_at DESC, g.id DESC
            LIMIT #{limit}
            </script>
            """)
    List<GameData> listVisibleKeyset(@Param("cursorSortOrder") Integer cursorSortOrder,
                                     @Param("cursorCreatedAt") java.time.OffsetDateTime cursorCreatedAt,
                                     @Param("cursorId") Long cursorId,
                                     @Param("limit") int limit);

    @Select("""
            <script>
            SELECT
                g.id,
                g.user_id AS userId,
                g.name,
                u.display_name AS creator,
                g.creator_note AS creatorNote,
                g.git_url AS gitUrl,
                g.webgl_path AS webglPath,
                g.thumbnail_url AS thumbnailUrl,
                g.like_count AS likeCount,
                g.is_visible AS visible,
                g.sort_order AS sortOrder,
                g.created_at AS createdAt,
                g.updated_at AS updatedAt
            FROM games g
            JOIN users u ON u.id = g.user_id
            WHERE g.is_visible IS NOT FALSE
              AND g.is_delete IS NOT TRUE
              AND u.is_delete IS NOT TRUE
              AND (
                  g.name ILIKE CONCAT('%', #{query}, '%')
                  OR u.display_name ILIKE CONCAT('%', #{query}, '%')
                  OR g.creator_note ILIKE CONCAT('%', #{query}, '%')
              )
            <if test="cursorSortOrder != null and cursorCreatedAt != null and cursorId != null">
              AND (
                  g.sort_order &gt; #{cursorSortOrder}
                  OR (g.sort_order = #{cursorSortOrder} AND g.created_at &lt; #{cursorCreatedAt})
                  OR (g.sort_order = #{cursorSortOrder} AND g.created_at = #{cursorCreatedAt} AND g.id &lt; #{cursorId})
              )
            </if>
            ORDER BY g.sort_order ASC, g.created_at DESC, g.id DESC
            LIMIT #{limit}
            </script>
            """)
    List<GameData> searchVisibleKeyset(@Param("query") String query,
                                       @Param("cursorSortOrder") Integer cursorSortOrder,
                                       @Param("cursorCreatedAt") java.time.OffsetDateTime cursorCreatedAt,
                                       @Param("cursorId") Long cursorId,
                                       @Param("limit") int limit);
}
