/*
 *   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.fightsystem.utils;

import de.steamwar.fightsystem.ArenaMode;
import de.steamwar.fightsystem.Config;
import de.steamwar.fightsystem.FightSystem;
import de.steamwar.fightsystem.countdown.Countdown;
import de.steamwar.fightsystem.fight.Fight;
import de.steamwar.fightsystem.fight.FightPlayer;
import de.steamwar.fightsystem.fight.FightTeam;
import de.steamwar.fightsystem.record.FileRecorder;
import de.steamwar.fightsystem.record.FileSource;
import de.steamwar.fightsystem.states.FightState;
import de.steamwar.fightsystem.states.OneShotStateDependent;
import de.steamwar.fightsystem.winconditions.Wincondition;
import de.steamwar.linkage.Linked;
import de.steamwar.sql.EventFight;
import de.steamwar.sql.EventRelation;
import de.steamwar.sql.SchematicNode;
import de.steamwar.sql.SteamwarUser;
import org.bukkit.Bukkit;

import java.nio.file.Files;
import java.sql.Timestamp;
import java.time.Instant;
import java.util.logging.Level;

import static de.steamwar.sql.Fight.create;
import static de.steamwar.sql.Fight.markReplayAvailable;

@Linked
public class FightStatistics {

    private Timestamp starttime = Timestamp.from(Instant.now());

    public FightStatistics() {
        new OneShotStateDependent(ArenaMode.SeriousFight, FightState.Running, this::enable);
        new OneShotStateDependent(ArenaMode.Event, FightState.Spectate, this::setEventResult);
        new OneShotStateDependent(ArenaMode.SeriousFight, FightState.Spectate, () -> Bukkit.getScheduler().runTask(FightSystem.getPlugin(), this::disable));
    }

    private void enable() {
        starttime = Timestamp.from(Instant.now());
    }

    private void setEventResult() {
        if (FightSystem.getLastWinner() == null) {
            Config.EventKampf.setErgebnis(0);
        } else if (FightSystem.getLastWinner().isBlue()) {
            Config.EventKampf.setErgebnis(1);
        } else {
            Config.EventKampf.setErgebnis(2);
        }

        Config.EventKampf.getDependents().forEach(EventRelation::apply);

        Config.EventKampf.getGroup().ifPresent(group -> {
            if (group.getLastFight().map(EventFight::getFightID).orElse(-1) == Config.EventKampf.getFightID() && !group.needsTieBreak()) {
                group.getDependents().forEach(EventRelation::apply);
            }
        });
    }

    private void disable() {
        FightTeam winner = FightSystem.getLastWinner();
        String windescription = FightSystem.getLastWinreason();
        String gameMode = Config.GameModeConfig.Schematic.Type.toDB();
        Instant endTime = Instant.now();

        int blueLeader = getLeader(Fight.getBlueTeam());
        int redLeader = getLeader(Fight.getRedTeam());

        int win = 0;
        if (winner == Fight.getBlueTeam()) {
            win = 1;
        } else if (winner == Fight.getRedTeam()) {
            win = 2;
        }

        Integer blueSchem;
        Integer redSchem;
        try {
            blueSchem = SchematicNode.getSchematicNode(Fight.getBlueTeam().getSchematic()).getId();
        } catch (NullPointerException e) {
            blueSchem = null;
        }
        try {
            redSchem = SchematicNode.getSchematicNode(Fight.getRedTeam().getSchematic()).getId();
        } catch (NullPointerException e) {
            redSchem = null;
        }

        int remainingTime = 0;
        Countdown timeOverCountdown = Wincondition.getTimeOverCountdown();
        if (timeOverCountdown != null) {
            remainingTime = timeOverCountdown.getTimeLeft();
        }

        try {
            int fightId = create(gameMode, Config.world.getName(), starttime, remainingTime,
                    blueLeader, redLeader, blueSchem, redSchem, win, windescription);

            for (FightPlayer fp : Fight.getBlueTeam().getPlayers()) {
                savePlayerStats(fp, fightId);
            }
            for (FightPlayer fp : Fight.getRedTeam().getPlayers()) {
                savePlayerStats(fp, fightId);
            }

            if (ArenaMode.Event.contains(Config.mode)) {
                Config.EventKampf.setFight(fightId);
            }

            Files.move(FileRecorder.getFile().toPath(), FileSource.replayFile(fightId).toPath());

            markReplayAvailable(fightId);
        } catch (Exception e) {
            Bukkit.getLogger().log(Level.SEVERE, "Failed to save statistics", e);
        }
    }

    private int getLeader(FightTeam team) {
        if (team.getLeader() != null) {
            return team.getLeader().getUser().getId();
        } else if (team.getDesignatedLeader() != null) {
            return SteamwarUser.get(team.getDesignatedLeader()).getId();
        }
        return 0;
    }

    private void savePlayerStats(FightPlayer fp, int fightId) {
        de.steamwar.sql.FightPlayer.create(fightId, fp.getUser().getId(), fp.getTeam().isBlue(), fp.getKit().getName(), fp.getKills(), !fp.isLiving());
    }
}
