package com.alttd.altitudeweb.database.litebans;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;
import org.jetbrains.annotations.NotNull;

import java.util.List;

public interface NameHistoryMapper {

    int PAGE_SIZE = 10;

    /**
     * Retrieves a list of all types of recent punishment history records filtered based on the given parameters sorted
     * in descending time order. This result does NOT contain kicks history
     *
     * @param partialName a partial or complete name of the user to filter the records, case-insensitive
     * @param nameColumn  the column name in the database indicating the name to use for filtering
     * @param limit       the maximum number of records to fetch
     * @param offset      the starting offset position of the result set
     *
     * @return a list of {@link HistoryRecord} objects that match the specified filters, ordered by time in descending
     * order
     */
    @Results({
            @Result(property = "id", column = "id"),
            @Result(property = "uuid", column = "uuid"),
            @Result(property = "punishedName", column = "punished_name"),
            @Result(property = "reason", column = "reason"),
            @Result(property = "bannedByUuid", column = "banned_by_uuid"),
            @Result(property = "bannedByName", column = "banned_by_name"),
            @Result(property = "removedByName", column = "removed_by_name"),
            @Result(property = "time", column = "time"),
            @Result(property = "until", column = "until"),
            @Result(property = "removedByReason", column = "removed_by_reason"),
            @Result(property = "type", column = "type")
    })
    @Select("""
            SELECT id,
                   all_punishments.uuid,
                   user_lookup.name AS punished_name,
                   reason,
                   banned_by_uuid,
                   banned_by_name,
                   removed_by_name,
                   time,
                   until,
                   removed_by_reason,
                   type
            FROM all_punishments
                     INNER JOIN user_lookup
                                ON user_lookup.uuid = all_punishments.uuid
            WHERE ${name_column} LIKE #{partialName}
            ORDER BY time DESC
            LIMIT #{limit} OFFSET #{offset}
            """)
    List<HistoryRecord> getRecentAllHistoryForName(@Param("partialName") String partialName,
                                                   @Param("name_column") String nameColumn,
                                                   @Param("limit") int limit,
                                                   @Param("offset") int offset);

    /**
     * Retrieves a list of all types of recent punishment history records sorted
     * in descending time order. This result does NOT contain kicks history
     *
     * @param limit       the maximum number of records to fetch
     * @param offset      the starting offset position of the result set
     *
     * @return a list of {@link HistoryRecord} objects that match the specified filters, ordered by time in descending
     * order
     */
    @Results({
            @Result(property = "id", column = "id"),
            @Result(property = "uuid", column = "uuid"),
            @Result(property = "punishedName", column = "punished_name"),
            @Result(property = "reason", column = "reason"),
            @Result(property = "bannedByUuid", column = "banned_by_uuid"),
            @Result(property = "bannedByName", column = "banned_by_name"),
            @Result(property = "removedByName", column = "removed_by_name"),
            @Result(property = "time", column = "time"),
            @Result(property = "until", column = "until"),
            @Result(property = "removedByReason", column = "removed_by_reason"),
            @Result(property = "type", column = "type")
    })
    @Select("""
            SELECT punishment.id,
                   punishment.uuid,
                   user_lookup.name AS punished_name,
                   punishment.reason,
                   punishment.banned_by_uuid,
                   punishment.banned_by_name,
                   punishment.removed_by_name,
                   punishment.time,
                   punishment.until,
                   punishment.removed_by_reason,
                   punishment.type
            FROM (SELECT uuid,
                         id,
                         reason,
                         banned_by_uuid,
                         banned_by_name,
                         removed_by_name,
                         time,
                         until,
                         removed_by_reason,
                         type
                  FROM all_punishments
                  ORDER BY time DESC
                  LIMIT #{limit} OFFSET #{offset}) AS punishment
                     INNER JOIN user_lookup
                                ON user_lookup.uuid = punishment.uuid;
            """)
    List<HistoryRecord> getRecentAllHistory(@Param("limit") int limit,
                                            @Param("offset") int offset);

    /**
     * Retrieves a list of a specific type of recent punishment history records filtered based on the provided
     * parameters. The records are sorted in descending order by time and limited to a specified range.
     *
     * @param tableName   the name of the database table to query the history records from
     * @param partialName a partial or complete name of the user to filter the records, case-insensitive
     * @param nameColumn  the column name in the database indicating the name to use for filtering
     * @param limit       the maximum number of records to fetch
     * @param offset      the starting offset position of the result set
     *
     * @return a list of {@link HistoryRecord} objects that match the specified filters, sorted by time in descending
     * order
     */
    @Results({
            @Result(property = "id", column = "id"),
            @Result(property = "uuid", column = "uuid"),
            @Result(property = "punishedName", column = "punished_name"),
            @Result(property = "reason", column = "reason"),
            @Result(property = "bannedByUuid", column = "banned_by_uuid"),
            @Result(property = "bannedByName", column = "banned_by_name"),
            @Result(property = "removedByName", column = "removed_by_name"),
            @Result(property = "time", column = "time"),
            @Result(property = "until", column = "until"),
            @Result(property = "removedByReason", column = "removed_by_reason")
    })
    @Select("""
            SELECT punishment.id,
                   punishment.uuid,
                   user_lookup.name AS punished_name,
                   punishment.reason,
                   punishment.banned_by_uuid,
                   punishment.banned_by_name,
                   punishment.removed_by_name,
                   punishment.time,
                   punishment.until,
                   punishment.removed_by_reason
            FROM ${table_name} AS punishment
                     INNER JOIN user_lookup
                                ON user_lookup.uuid = punishment.uuid
            WHERE ${name_column} LIKE #{partialName}
            ORDER BY time DESC
            LIMIT #{limit} OFFSET #{offset}
            """)
    List<HistoryRecord> getRecentHistoryForName(@Param("table_name") String tableName,
                                                @Param("partialName") String partialName,
                                                @Param("name_column") String nameColumn,
                                                @Param("limit") int limit,
                                                @Param("offset") int offset);

    /**
     * Retrieves a list of a specific type of recent punishment history records filtered based on the provided
     * parameters. The records are sorted in descending order by time and limited to a specified range.
     *
     * @param tableName   the name of the database table to query the history records from
     * @param limit       the maximum number of records to fetch
     * @param offset      the starting offset position of the result set
     *
     * @return a list of {@link HistoryRecord} objects that match the specified filters, sorted by time in descending
     * order
     */
    @Results({
            @Result(property = "id", column = "id"),
            @Result(property = "uuid", column = "uuid"),
            @Result(property = "punishedName", column = "punished_name"),
            @Result(property = "reason", column = "reason"),
            @Result(property = "bannedByUuid", column = "banned_by_uuid"),
            @Result(property = "bannedByName", column = "banned_by_name"),
            @Result(property = "removedByName", column = "removed_by_name"),
            @Result(property = "time", column = "time"),
            @Result(property = "until", column = "until"),
            @Result(property = "removedByReason", column = "removed_by_reason")
    })
    @Select("""
            SELECT punishment.id,
                   punishment.uuid,
                   user_lookup.name AS punished_name,
                   punishment.reason,
                   punishment.banned_by_uuid,
                   punishment.banned_by_name,
                   punishment.removed_by_name,
                   punishment.time,
                   punishment.until,
                   punishment.removed_by_reason
            FROM (
                     SELECT id,
                            uuid,
                            reason,
                            banned_by_uuid,
                            banned_by_name,
                            removed_by_name,
                            time,
                            until,
                            removed_by_reason
                     FROM ${table_name} AS punishment
                     ORDER BY time DESC
                     LIMIT #{limit} OFFSET #{offset}
                 ) AS punishment
                     INNER JOIN user_lookup ON user_lookup.uuid = punishment.uuid
            """)
    List<HistoryRecord> getRecentHistory(@Param("table_name") String tableName,
                                        @Param("limit") int limit,
                                        @Param("offset") int offset);

    default List<HistoryRecord> getRecent(HistoryType historyType, UserType userType, String partialName,
                                          int page) {
        return switch (historyType) {
            case ALL -> getRecentAll(userType, partialName, page);
            case BAN -> getRecentBans(userType, partialName, page);
            case MUTE -> getRecentMutes(userType, partialName, page);
            case KICK -> getRecentKicks(userType, partialName, page);
            case WARN -> getRecentWarns(userType, partialName, page);
        };
    }

    private List<HistoryRecord> getRecent(String tableName, UserType userType, String partialName,
                                          int page) {
        int offset = page * PAGE_SIZE;
        int limit = PAGE_SIZE;
        if (partialName.isEmpty()) {
            return getRecentHistory(tableName, limit, offset);
        } else {
            final String searchName = partialName.toLowerCase().replace("_", "\\_") + "%";
            return switch (userType) {
                case PLAYER -> getRecentHistoryForName(tableName, searchName, "name",
                                                       limit, offset);
                case STAFF -> getRecentHistoryForName(tableName, searchName, "banned_by_name",
                                                      limit, offset);
            };
        }
    }

    private List<HistoryRecord> getRecentAll(UserType userType, String partialName, int page) {
        int offset = page * PAGE_SIZE;
        int limit = PAGE_SIZE;
        if (partialName.isEmpty()) {
            return getRecentAllHistory(limit, offset);
        } else {
            final String searchName = partialName.toLowerCase().replace("_", "\\_") + "%";
            return switch (userType) {
                case PLAYER -> getRecentAllHistoryForName(searchName, "name",
                                                          limit, offset);
                case STAFF -> getRecentAllHistoryForName(searchName, "banned_by_name",
                                                         limit, offset);
            };
        }
    }

    private @NotNull List<HistoryRecord> getRecentBans(UserType userType, String partialName, int page) {
        return addType(getRecent("litebans_bans", userType, partialName, page), "ban");
    }

    private @NotNull List<HistoryRecord> getRecentKicks(UserType userType, String partialName, int page) {
        return addType(getRecent("litebans_kicks", userType, partialName, page), "kick");
    }

    private @NotNull List<HistoryRecord> getRecentMutes(UserType userType, String partialName, int page) {
        return addType(getRecent("litebans_mutes", userType, partialName, page), "mute");
    }

    private @NotNull List<HistoryRecord> getRecentWarns(UserType userType, String partialName, int page) {
        return addType(getRecent("litebans_warnings", userType, partialName, page), "warn");
    }

    private @NotNull List<HistoryRecord> addType(@NotNull List<HistoryRecord> historyRecords, String type) {
        historyRecords.forEach(historyRecord -> historyRecord.setType(type));
        return historyRecords;
    }

}
