/*
 *   This file is a part of the SteamWar software.
 *
 *   Copyright (C) 2026 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.velocitycore.advancements;

import de.steamwar.messages.Chatter;
import de.steamwar.persistent.Storage;
import de.steamwar.sql.CheckedSchematic;
import de.steamwar.sql.EventFight;
import de.steamwar.sql.FightPlayer;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import net.kyori.adventure.text.Component;

import java.time.Instant;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;

@UtilityClass
public class Advancements {

    @Getter
    static final List<Advancement> all = new ArrayList<>();

    @Getter
    private static final List<Advancement> playtime = new ArrayList<>();

    public static final Advancement.Value.Key<Double> PLAY_TIME_KEY = new Advancement.Value.Key<>(user -> {
        double playtime = user.getOnlinetime();
        playtime += Instant.now().getEpochSecond() - Storage.sessions.get(Chatter.of(user).getPlayer()).toInstant().getEpochSecond();
        playtime /= 60d * 60d;
        return playtime;
    });

    public static final Advancement.Value.Key<Long> FIGHT_COUNT = new Advancement.Value.Key<>(user -> {
        return FightPlayer.countFights(user.getId());
    });

    public static final Advancement.Value.Key<Integer> FIGHT_COUNT_WAR_GEAR = new Advancement.Value.Key<>(user -> {
        return FightPlayer.countFights(user.getId(), "WarGear");
    });

    public static final Advancement.Value.Key<Integer> FIGHT_COUNT_MINI_WAR_GEAR = new Advancement.Value.Key<>(user -> {
        return FightPlayer.countFights(user.getId(), "MiniWarGear");
    });

    public static final Advancement.Value.Key<Integer> FIGHT_COUNT_WAR_SHIP = new Advancement.Value.Key<>(user -> {
        return FightPlayer.countFights(user.getId(), "WarShip");
    });

    public static final Advancement.Value.Key<Long> EVENT_FIGHT_COUNT = new Advancement.Value.Key<>(user -> {
        return EventFight.countEventFights(user);
    });

    public static final Advancement.Value.Key<Integer> EVENT_FIGHT_FIRST_PLACE_COUNT = new Advancement.Value.Key<>(user -> {
        return EventFight.countPlacement(user, 1);
    });

    public static final Advancement.Value.Key<Integer> EVENT_FIGHT_SECOND_PLACE_COUNT = new Advancement.Value.Key<>(user -> {
        return EventFight.countPlacement(user, 2);
    });

    public static final Advancement.Value.Key<Integer> EVENT_FIGHT_THIRDPLACE_COUNT = new Advancement.Value.Key<>(user -> {
        return EventFight.countPlacement(user, 3);
    });

    public static final Advancement.Value.Key<Long> CHECKED_SCHEMATIC_COUNT = new Advancement.Value.Key<>(user -> {
        return CheckedSchematic.countChecked(user);
    });

    public static final Advancement.Value.Key<Long> ACCEPTED_SCHEMATIC_COUNT = new Advancement.Value.Key<>(user -> {
        return CheckedSchematic.countAccepted(user);
    });

    public static final Advancement.Value.Key<Long> ACCEPTED_SCHEMATIC_COUNT_WAR_GEAR = new Advancement.Value.Key<>(user -> {
        return CheckedSchematic.countAccepted(user, "WarGear");
    });

    public static final Advancement.Value.Key<Long> ACCEPTED_SCHEMATIC_COUNT_MINI_WAR_GEAR = new Advancement.Value.Key<>(user -> {
        return CheckedSchematic.countAccepted(user, "MiniWarGear");
    });

    public static final Advancement.Value.Key<Long> ACCEPTED_SCHEMATIC_COUNT_WAR_SHIP = new Advancement.Value.Key<>(user -> {
        return CheckedSchematic.countAccepted(user, "WarShip");
    });

    public static final Advancement ROOT = new Advancement(
            "steamwar:advancements/root",
            Optional.empty(),
            new Advancement.Display(
                    Component.text("SteamWar"),
                    Component.text("Join SteamWar for the first time!"),
                    "cactus_flower",
                    Advancement.Display.FrameType.CHALLENGE,
                    Optional.of("minecraft:gui/advancements/backgrounds/adventure"),
                    0f,
                    3f
            ),
            Advancement.HidePolicy.NEVER,
            1,
            user -> 1
    );

    static {
        Advancement previous = ROOT;
        int[] playTimes = new int[]{1, 10, 100, 500, 1000, 2500, 5000, 7500, 10000, 15000, 20000};
        for (int i = 0; i < playTimes.length; i++) {
            int neededPlayTime = playTimes[i];
            previous = new Advancement(
                    "steamwar:advancements/playtime_" + neededPlayTime + "_hour",
                    Optional.of(previous),
                    new Advancement.Display(
                            Component.text("Play " + neededPlayTime + " Hour" + (neededPlayTime > 1 ? "s" : "")),
                            Component.text("Play " + neededPlayTime + " hour" + (neededPlayTime > 1 ? "s" : "") + " on SteamWar"),
                            "clock",
                            Advancement.Display.FrameType.TASK,
                            i + 1f,
                            3f
                    ),
                    Advancement.HidePolicy.PREVIOUS_UNFINISHED,
                    Math.min(neededPlayTime, 100),
                    PLAY_TIME_KEY.max(neededPlayTime)
            );
            playtime.add(previous);
        }
    }

    static {
        Advancement previous = ROOT;
        int[] fightCounts = new int[]{1, 10, 50, 100, 200, 500, 1000, 2500, 5000, 7500, 10000, 15000, 20000};
        for (int i = 0; i < fightCounts.length; i++) {
            int fightCount = fightCounts[i];
            previous = new Advancement(
                    "steamwar:advancements/fights_" + fightCount,
                    Optional.of(previous),
                    new Advancement.Display(
                            Component.text(fightCount + " Fight" + (fightCount > 1 ? "s" : "")),
                            Component.text(fightCount + " Fight" + (fightCount > 1 ? "s" : "")),
                            "iron_sword",
                            Advancement.Display.FrameType.TASK,
                            i + 1f,
                            4f
                    ),
                    Advancement.HidePolicy.PREVIOUS_UNFINISHED,
                    Math.min(fightCount, 100),
                    FIGHT_COUNT.max(fightCount)
            );

            if (i == 0) {
                fightsPerType(previous, 5f, "WarGear", FIGHT_COUNT_WAR_GEAR, "stone_bricks");
                fightsPerType(previous, 6f, "MiniWarGear", FIGHT_COUNT_MINI_WAR_GEAR, "stone_brick_slab");
                fightsPerType(previous, 7f, "WarShip", FIGHT_COUNT_WAR_SHIP, "dark_oak_boat");
            }
        }
    }

    private static void fightsPerType(Advancement previous, float yCoord, String type, Advancement.Value.Key<Integer> typeKey, String item) {
        int[] fightCounts = new int[]{1, 10, 25, 50, 75, 100, 250, 500, 750, 1000, 2500, 5000, 7500, 10000};
        for (int i = 0; i < fightCounts.length; i++) {
            int fightCount = fightCounts[i];
            previous = new Advancement(
                    "steamwar:advancements/fights_" + type + "_" + fightCount,
                    Optional.of(previous),
                    new Advancement.Display(
                            Component.text(type + " " + fightCount + " Fight" + (fightCount > 1 ? "s" : "")),
                            Component.text(type + " " + fightCount + " Fight" + (fightCount > 1 ? "s" : "")),
                            item,
                            Advancement.Display.FrameType.TASK,
                            i + 2f,
                            yCoord
                    ),
                    i == 0 ? Advancement.HidePolicy.WITH_PREVIOUS : Advancement.HidePolicy.PREVIOUS_UNFINISHED,
                    Math.min(fightCount, 100),
                    typeKey.max(fightCount)
            );
        }
    }

    static {
        Advancement previous = ROOT;
        int[] eventFightCounts = new int[]{1, 5, 10, 15, 25, 50, 100, 150, 200, 250};
        for (int i = 0; i < eventFightCounts.length; i++) {
            int eventFightCount = eventFightCounts[i];
            previous = new Advancement(
                    "steamwar:advancements/event_fights_" + eventFightCount,
                    Optional.of(previous),
                    new Advancement.Display(
                            Component.text(eventFightCount + " Event-Fight" + (eventFightCount > 1 ? "s" : "")),
                            Component.text(eventFightCount + " Event-Fight" + (eventFightCount > 1 ? "s" : "")),
                            "golden_sword",
                            Advancement.Display.FrameType.TASK,
                            i + 1f,
                            8f
                    ),
                    Advancement.HidePolicy.PREVIOUS_UNFINISHED,
                    Math.min(eventFightCount, 100),
                    EVENT_FIGHT_COUNT.max(eventFightCount)
            );

            if (i == 0) {
                placementsCounts(previous, 9f, 1, "gold_block", EVENT_FIGHT_FIRST_PLACE_COUNT, Advancement.Display.FrameType.CHALLENGE);
                placementsCounts(previous, 10f, 2, "iron_block", EVENT_FIGHT_SECOND_PLACE_COUNT, Advancement.Display.FrameType.GOAL);
                placementsCounts(previous, 11f, 3, "copper_block", EVENT_FIGHT_THIRDPLACE_COUNT, Advancement.Display.FrameType.TASK);
            }
        }
    }

    private static void placementsCounts(Advancement previous, float yCoord, int placement, String item, Advancement.Value.Key<Integer> typeKey, Advancement.Display.FrameType frameType) {
        for (int placementCount = 1; placementCount <= 10; placementCount++) {
            int finalPlacementCount = placementCount;
            previous = new Advancement(
                    "steamwar:advancements/event_placement_" + placement + "_" + placementCount,
                    Optional.of(previous),
                    new Advancement.Display(
                            Component.text(placementCount + "x " + placement + ". Place in Event"),
                            Component.text(""),
                            item,
                            frameType,
                            2f + (placementCount - 1f),
                            yCoord
                    ),
                    placementCount == 1 ? Advancement.HidePolicy.WITH_PREVIOUS : Advancement.HidePolicy.PREVIOUS_UNFINISHED,
                    1,
                    typeKey.reached(placementCount)
            );
        }
    }

    static {
        Advancement previous = ROOT;
        int[] checkedCounts = new int[]{1, 10, 100, 250, 500, 750, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000};
        for (int i = 0; i < checkedCounts.length; i++) {
            int checkedCount = checkedCounts[i];
            previous = new Advancement(
                    "steamwar:advancements/checked_" + checkedCount,
                    Optional.of(previous),
                    new Advancement.Display(
                            Component.text(checkedCount + " Check Session" + (checkedCount > 1 ? "s" : "")),
                            Component.text(checkedCount + " Check Session" + (checkedCount > 1 ? "s" : "")),
                            "paper",
                            Advancement.Display.FrameType.TASK,
                            i + 1f,
                            0f
                    ),
                    i == 0 ? Advancement.HidePolicy.NO_PROGRESS : Advancement.HidePolicy.PREVIOUS_UNFINISHED,
                    Math.min(checkedCount, 100),
                    CHECKED_SCHEMATIC_COUNT.max(checkedCount)
            );
        }
    }

    static {
        Advancement previous = ROOT;
        int[] acceptedCounts = new int[]{1, 5, 10, 15, 25, 50, 100, 150, 200, 250, 500, 750, 1000};
        for (int i = 0; i < acceptedCounts.length; i++) {
            int acceptedCount = acceptedCounts[i];
            previous = new Advancement(
                    "steamwar:advancements/accepted_" + acceptedCount,
                    Optional.of(previous),
                    new Advancement.Display(
                            Component.text(acceptedCount + " Accepted Schematic" + (acceptedCount > 1 ? "s" : "")),
                            Component.text(acceptedCount + " Accepted Schematic" + (acceptedCount > 1 ? "s" : "")),
                            "cauldron",
                            Advancement.Display.FrameType.TASK,
                            i + 1f,
                            2f
                    ),
                    Advancement.HidePolicy.PREVIOUS_UNFINISHED,
                    Math.min(acceptedCount, 100),
                    ACCEPTED_SCHEMATIC_COUNT.max(acceptedCount)
            );

            if (i == 0) {
                acceptedPerType(previous, 2f, "WarGear", ACCEPTED_SCHEMATIC_COUNT_WAR_GEAR, "end_stone_bricks");
                acceptedPerType(previous, 3f, "MiniWarGear", ACCEPTED_SCHEMATIC_COUNT_MINI_WAR_GEAR, "end_stone_brick_slab");
                acceptedPerType(previous, 4f, "WarShip", ACCEPTED_SCHEMATIC_COUNT_WAR_SHIP, "oak_boat");
            }
        }
    }

    private static void acceptedPerType(Advancement previous, float xCoord, String type, Advancement.Value.Key<Long> typeKey, String item) {
        new Advancement(
                "steamwar:advancements/accepted_" + type,
                Optional.of(previous),
                new Advancement.Display(
                        Component.text(type + " Accepted"),
                        Component.text(""),
                        item,
                        Advancement.Display.FrameType.GOAL,
                        xCoord,
                        1f
                ),
                Advancement.HidePolicy.WITH_PREVIOUS,
                1,
                typeKey.reached(1)
        );
    }
}
