/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2025 SteamWar.de-Serverteam
 *
 *   This program is free software: you can redistribute it and/or modify
 *   it under the terms of the GNU Affero General Public License as published by
 *   the Free Software Foundation, either version 3 of the License, or
 *   (at your option) any later version.
 *
 *   This program is distributed in the hope that it will be useful,
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *   GNU Affero General Public License for more details.
 *
 *   You should have received a copy of the GNU Affero General Public License
 *   along with this program.  If not, see <https://www.gnu.org/licenses/>.
 */

package de.steamwar.sql;

import de.steamwar.sql.internal.*;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.sql.Timestamp;
import java.time.Instant;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.function.Consumer;
import java.util.stream.Collectors;

@AllArgsConstructor
public class Punishment {

    static {
        SqlTypeMapper.nameEnumMapper(PunishmentType.class);
    }

    public static final Timestamp PERMA_TIME = Timestamp.from(Instant.ofEpochSecond(946674800));

    private static final Table<Punishment> table = new Table<>(Punishment.class, "Punishments");
    private static final SelectStatement<Punishment> getPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE PunishmentId IN (SELECT MAX(PunishmentId) FROM Punishments WHERE UserId = ? GROUP BY Type)");
    private static final SelectStatement<Punishment> getPunishment = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? AND Type = ? ORDER BY PunishmentId DESC LIMIT 1");
    private static final SelectStatement<Punishment> getAllPunishments = new SelectStatement<>(table, "SELECT * FROM Punishments WHERE UserId = ? ORDER BY `PunishmentId` DESC");
    private static final Statement insert = table.insertFields(true, "UserId", "Punisher", "Type", "EndTime", "Perma", "Reason");

    public static Punishment getPunishmentOfPlayer(int user, PunishmentType type) {
        return getPunishment.select(user, type);
    }

    public static Map<PunishmentType, Punishment> getPunishmentsOfPlayer(int user) {
        return getPunishments.listSelect(user).stream().collect(Collectors.toMap(Punishment::getType, punishment -> punishment));
    }

    public static List<Punishment> getAllPunishmentsOfPlayer(int user) {
        return getAllPunishments.listSelect(user);
    }

    public static boolean isPunished(SteamwarUser user, PunishmentType type, Consumer<Punishment> callback) {
        Punishment punishment = Punishment.getPunishmentOfPlayer(user.getId(), type);
        if(punishment == null || !punishment.isCurrent()) {
            return false;
        } else {
            callback.accept(punishment);
            return true;
        }
    }

    public static Punishment createPunishment(int user, int executor, PunishmentType type, String reason, Timestamp endTime, boolean perma) {
        if(perma && !endTime.equals(PERMA_TIME)) {
            throw new IllegalArgumentException("Permanent punishments must have an end time of `Punishment.PERMA_TIME`");
        }
        int punishmentId = insert.insertGetKey(user, executor, type.name(), endTime, perma, reason);
        return new Punishment(punishmentId, user, executor, type, Timestamp.from(Instant.now()), endTime, perma, reason);
    }

    @Field(keys = {Table.PRIMARY}, autoincrement = true)
    private final int punishmentId;
    @Field
    @Getter
    private final int userId;
    @Field
    @Getter
    private final int punisher;
    @Field
    @Getter
    private final PunishmentType type;
    @Field
    @Getter
    private final Timestamp startTime;
    @Field
    @Getter
    private final Timestamp endTime;
    @Field
    @Getter
    private final boolean perma;
    @Field
    @Getter
    private final String reason;

    @Deprecated // Not multiling, misleading title
    public String getBantime(Timestamp endTime, boolean perma) {
        if (perma) {
            return "permanent";
        } else {
            return endTime.toLocalDateTime().format(DateTimeFormatter.ofPattern("dd.MM.yyyy HH:mm"));
        }
    }

    public boolean isCurrent() {
        return isPerma() || getEndTime().after(new Date());
    }

    @AllArgsConstructor
    @RequiredArgsConstructor
    @Getter
    public enum PunishmentType {
        Ban(false, "BAN_TEAM", "BAN_PERMA", "BAN_UNTIL", "UNBAN_ERROR", "UNBAN"),
        Mute( false, "MUTE_TEAM", "MUTE_PERMA", "MUTE_UNTIL", "UNMUTE_ERROR", "UNMUTE"),
        NoSchemReceiving(true, "NOSCHEMRECEIVING_TEAM", "NOSCHEMRECEIVING_PERMA", "NOSCHEMRECEIVING_UNTIL", "UNNOSCHEMRECEIVING_ERROR", "UNNOSCHEMRECEIVING"),
        NoSchemSharing(true, "NOSCHEMSHARING_TEAM", "NOSCHEMSHARING_PERMA", "NOSCHEMSHARING_UNTIL", "UNNOSCHEMSHARING_ERROR", "UNNOSCHEMSHARING"),
        NoSchemSubmitting(false, "NOSCHEMSUBMITTING_TEAM", "NOSCHEMSUBMITTING_PERMA", "NOSCHEMSUBMITTING_UNTIL", "UNNOSCHEMSUBMITTING_ERROR", "UNNOSCHEMSUBMITTING"),
        NoDevServer(true, "NODEVSERVER_TEAM", "NODEVSERVER_PERMA", "NODEVSERVER_UNTIL", "UNNODEVSERVER_ERROR", "UNNODEVSERVER"),
        NoFightServer(true, "NOFIGHTSERVER_TEAM", "NOFIGHTSERVER_PERMA", "NOFIGHTSERVER_UNTIL", "UNNOFIGHTSERVER_ERROR", "UNNOFIGHTSERVER"),
        NoTeamServer(true, "NOTEAMSERVER_TEAM", "NOTEAMSERVER_PERMA", "NOTEAMSERVER_UNTIL", "UNNOTEAMSERVER_ERROR", "UNNOTEAMSERVER"),
        Note(false, "NOTE_TEAM", null, null, null, null, true);

        private final boolean needsAdmin;
        private final String teamMessage;
        private final String playerMessagePerma;
        private final String playerMessageUntil;
        private final String usageNotPunished;
        private final String unpunishmentMessage;
        private boolean multi = false;
    }
}
