/*
 *   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;

import de.steamwar.fightsystem.utils.Region;
import de.steamwar.fightsystem.winconditions.Winconditions;
import de.steamwar.sql.*;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.World;
import org.bukkit.configuration.file.FileConfiguration;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.entity.Player;
import org.bukkit.util.Vector;

import java.io.File;
import java.util.*;
import java.util.logging.Level;
import java.util.stream.Collectors;

public class Config {

    private Config(){}

    public static final World world = Bukkit.getWorlds().get(0);

    //Fight sequence
    public static final int NoPlayerOnlineDuration;
    public static final int PreSchemPasteDuration;
    public static final int SetupDuration;
    public static final int PreFightDuration;
    public static final int SpectatorDuration;

    // entern parameter
    public static final List<Integer> EnterStages;

    //arena parameter
    public static final Region BluePasteRegion;
    public static final Region RedPasteRegion;
    public static final Region BlueExtendRegion;
    public static final Region RedExtendRegion;
    public static final Region ArenaRegion;
    public static final Region PlayerRegion;
    public static final Region BlueInsetRegion;

    public static final Location TeamBlueSpawn;
    public static final Location TeamRedSpawn;
    public static final Location SpecSpawn;

    public static final int BlueToRedX;
    private static final int BlueToRedY;
    public static final int BlueToRedZ;

    public static final int PreperationArea;
    public static final int WaterDepth;
    public static final boolean GroundWalkable;
    public static final boolean DisableSnowMelt;
    public static final boolean ArenaLeaveable;
    public static final boolean AllowMissiles;

    //schematic parameter
    public static final boolean RanksEnabled;
    public static final boolean OnlyPublicSchematics;
    public static final boolean IgnorePublicOnly;
    public static final de.steamwar.sql.SchematicType SchematicType;
    public static final List<de.steamwar.sql.SchematicType> SubTypes;
    public static final boolean RedRotate;
    public static final boolean BlueRotate;
    public static final boolean PasteAligned;
    public static final boolean ReplaceObsidianBedrock;
    public static final boolean ReplaceWithBlockupdates;
    public static final boolean UnlimitedPrepare;

    //team parameter
    public static final String TeamRedName;
    public static final String TeamBlueName;
    public static final String TeamRedColor;
    public static final String TeamBlueColor;
    public static final String GameName;
    public static final String TeamChatDetection;
    public static final UUID BlueLeader;
    public static final UUID RedLeader;

    //Active win conditions
    public static final Set<Winconditions> ActiveWinconditions;

    //win condition parameters
    public static final int TimeoutTime;
    public static final double PercentWin;
    public static final boolean PercentEntern;
    public static final boolean PercentBlocksWhitelist;
    public static final Set<Material> PercentBlocks;
    public static final int TechKoTime;

    //default kits
    public static final String MemberDefault;
    public static final String LeaderDefault;
    public static final boolean PersonalKits;
    public static final Set<Material> ForbiddenItems;
    public static final String KitFile;

    //tech hider parameter
    public static final boolean TechhiderActive;
    public static final Set<Material> HiddenBlocks;
    public static final Set<String> HiddenBlockEntities;
    public static final Material ObfuscateWith;

    //event parameter
    public static final EventFight EventKampf;
    private static final Set<Integer> Referees;
    public static final int EventTeamBlueID;
    public static final int EventTeamRedID;
    public static final boolean BothTeamsPublic;
    public static final int MaximumTeamMembers;
    public static final boolean LiveReplay;

    //check parameter
    public static final int CheckSchemID;
    public static final int PrepareSchemID;

    public static final ArenaMode mode;

    //replay system parameter
    public static final String spectateIP = "127.0.0.1";
    public static final int SpectatePort;
    public static final int ReplayID;

    static{
        CheckSchemID = Integer.parseInt(System.getProperty("checkSchemID", "0"));
        PrepareSchemID = Integer.parseInt(System.getProperty("prepareSchemID", "0"));
        ReplayID = Integer.parseInt(System.getProperty("replay", "0"));

        String configFile = System.getProperty("config", "config.yml");
        if(!new File(FightSystem.getPlugin().getDataFolder(), configFile).exists()) {
            FightSystem.getPlugin().saveDefaultConfig();
            Bukkit.getLogger().log(Level.SEVERE, "Arenaconfig fehlt!");
            Bukkit.shutdown();
        }
        FileConfiguration config = YamlConfiguration.loadConfiguration(new File(FightSystem.getPlugin().getDataFolder(), configFile));

        File worldConfigFile = new File(world.getWorldFolder(), "config.yml");
        if(!worldConfigFile.exists()) {
            Bukkit.getLogger().log(Level.SEVERE, "Weltconfig fehlt!");
            Bukkit.shutdown();
        }
        FileConfiguration worldconfig = YamlConfiguration.loadConfiguration(worldConfigFile);

        NoPlayerOnlineDuration = config.getInt("Times.NoPlayersOnlineDuration", 300);
        PreSchemPasteDuration = config.getInt("Times.PreSchemPasteDuration", 120);
        SetupDuration = config.getInt("Times.SetupDuration", 300);
        PreFightDuration = config.getInt("Times.PreFightDuration", 30);
        SpectatorDuration = config.getInt("Times.SpectatorDuration", 30);

        int blueCornerX = worldconfig.getInt("BlueCorner.x");
        int blueCornerY = worldconfig.getInt("BlueCorner.y");
        int blueCornerZ = worldconfig.getInt("BlueCorner.z");
        int underBorder = worldconfig.getInt("UnderBorder", blueCornerY);

        WaterDepth = config.getInt("Arena.WaterDepth", 0);
        int schem2BorderX = config.getInt("Arena.Schem2Border.x", 24);
        int schem2BorderZ = config.getInt("Arena.Schem2Border.z", 24);
        PreperationArea = config.getInt("Arena.BorderFromSchematic", 12);
        GroundWalkable = config.getBoolean("Arena.GroundWalkable", true);
        DisableSnowMelt = config.getBoolean("Arena.DisableSnowMelt", false);
        ArenaLeaveable = config.getBoolean("Arena.Leaveable", false);

        int schemsizeX = config.getInt("Schematic.Size.x");
        int schemsizeY = config.getInt("Schematic.Size.y");
        int schemsizeZ = config.getInt("Schematic.Size.z");
        RanksEnabled = !config.getStringList("Ranks").isEmpty();
        SchematicType = de.steamwar.sql.SchematicType.fromDB(Objects.requireNonNull(config.getString("Schematic.Type", "normal")));
        SubTypes = config.getStringList("Schematic.SubTypes").stream().map(de.steamwar.sql.SchematicType::fromDB).collect(Collectors.toList());
        IgnorePublicOnly = config.getBoolean("Schematic.IgnorePublicOnly", false);
        boolean rotate = config.getBoolean("Schematic.Rotate", true);
        PasteAligned = config.getBoolean("Schematic.PasteAligned", false);
        ReplaceObsidianBedrock = config.getBoolean("Schematic.ReplaceObsidianBedrock", false);
        ReplaceWithBlockupdates = config.getBoolean("Schematic.ReplaceWithBlockupdates", false);
        UnlimitedPrepare = config.getBoolean("Schematic.UnlimitedPrepare", false);

        int schemInsetX = config.getInt("Schematic.Inset.x", 0);
        int schemInsetZ = config.getInt("Schematic.Inset.z", 0);
        int schemInsetBottom = config.getInt("Schematic.Inset.bottom", 0);
        int schemInsetTop = config.getInt("Schematic.Inset.top", 0);

        GameName = config.getString("GameName", "WarGear");
        TeamChatDetection = config.getString("TeamChatPrefix", "+");

        ActiveWinconditions = Collections.unmodifiableSet(config.getStringList("WinConditions").stream().map(Winconditions::valueOf).collect(Collectors.toSet()));

        TimeoutTime = config.getInt("WinConditionParams.TimeoutTime", 1200);
        PercentWin = config.getDouble("WinConditionParams.PercentWin", 7.0);
        PercentEntern = config.getBoolean("WinConditionParams.PercentEntern", true);
        PercentBlocksWhitelist = config.getBoolean("WinConditionParams.BlocksWhitelist", false);
        PercentBlocks = Collections.unmodifiableSet(config.getStringList("WinConditionParams.Blocks").stream().map(Material::valueOf).collect(Collectors.toSet()));
        TechKoTime = config.getInt("WinConditionParams.TechKoTime", 90);

        EnterStages = Collections.unmodifiableList(config.getIntegerList("EnterStages"));
        AllowMissiles = config.getBoolean("Arena.AllowMissiles", !EnterStages.isEmpty());

        KitFile = config.getString("Kits.File", "kits.yml");
        MemberDefault = config.getString("Kits.MemberDefault", "default");
        LeaderDefault = config.getString("Kits.LeaderDefault", "default");
        PersonalKits = config.getBoolean("Kits.PersonalKits", false);
        ForbiddenItems = Collections.unmodifiableSet(config.getStringList("Kits.ForbiddenItems").stream().map(Material::valueOf).collect(Collectors.toSet()));

        TechhiderActive = config.getBoolean("Techhider.Active", false);
        ObfuscateWith = Material.getMaterial(config.getString("Techhider.ObfuscateWith", "end_stone").toUpperCase());
        HiddenBlocks = config.getStringList("Techhider.HiddenBlocks").stream().map(String::toUpperCase).map(Material::getMaterial).collect(Collectors.toSet());
        HiddenBlockEntities = Collections.unmodifiableSet(new HashSet<>(config.getStringList("Techhider.HiddenBlockEntities")));

        if(schemsizeX < 0){
            schemsizeX = -schemsizeX;
            blueCornerX = blueCornerX - schemsizeX;
        }
        if(schemsizeY < 0){
            schemsizeY = -schemsizeY;
            blueCornerY = blueCornerY - schemsizeY;
        }
        if(schemsizeZ < 0){
            schemsizeZ = -schemsizeZ;
            blueCornerZ = blueCornerZ - schemsizeZ;
        }

        BlueToRedX = worldconfig.getInt("BlueToRed.x", 0);
        BlueToRedY = worldconfig.getInt("BlueToRed.y", 0);
        BlueToRedZ = worldconfig.getInt("BlueToRed.z", schemsizeZ + 50);
        double teamBlueSpawnOffsetX = config.getDouble("Arena.SpawnOffset.x", 0);
        double teamBlueSpawnOffsetY = config.getDouble("Arena.SpawnOffset.y", schemsizeY);
        double teamBlueSpawnOffsetZ = config.getDouble("Arena.SpawnOffset.z", 0);

        int teamRedCornerX = BlueToRedX + blueCornerX;
        int teamRedCornerY = BlueToRedY + blueCornerY;
        int teamRedCornerZ = BlueToRedZ + blueCornerZ;

        int teamBluePasteX = blueCornerX + schemsizeX / 2;
        int teamBluePasteZ = blueCornerZ + schemsizeZ / 2;
        int teamRedPasteX = teamBluePasteX + BlueToRedX;
        int teamRedPasteZ = teamBluePasteZ + BlueToRedZ;

        TeamBlueSpawn = new Location(world,
                teamBluePasteX + 0.5 + teamBlueSpawnOffsetX,
                blueCornerY + 0.5 + teamBlueSpawnOffsetY,
                teamBluePasteZ + 0.5 + teamBlueSpawnOffsetZ);

        TeamRedSpawn = new Location(world,
                teamRedPasteX + 0.5 - teamBlueSpawnOffsetX,
                teamRedCornerY + 0.5 + teamBlueSpawnOffsetY,
                teamRedPasteZ + 0.5 - teamBlueSpawnOffsetZ);

        SpecSpawn = new Location(world,
                teamBluePasteX + BlueToRedX /2.0,
                blueCornerY + BlueToRedY /2.0 + schemsizeY/2.0,
                teamBluePasteZ + BlueToRedZ /2.0);

        Vector v1 = TeamBlueSpawn.toVector().subtract(TeamRedSpawn.toVector());
        double pitch = Math.toDegrees(v1.angle(v1.clone().setY(0)));
        double yaw = Math.toDegrees(v1.clone().setY(0).angle(new Vector(0, 0, 1)));

        TeamBlueSpawn.setYaw((float) yaw + 180);
        TeamBlueSpawn.setPitch((float) pitch);

        TeamRedSpawn.setYaw((float) yaw);
        TeamRedSpawn.setPitch((float) pitch * -1);


        boolean teamRedRotate;
        boolean teamBlueRotate;
        int arenaMinX;
        int arenaMaxX;
        int arenaMinZ;
        int arenaMaxZ;
        if(BlueToRedX > 0){
            arenaMinX = blueCornerX - schem2BorderX;
            arenaMaxX = teamRedCornerX + schemsizeX + schem2BorderX;
            teamRedRotate = true;
            teamBlueRotate = false;
        }else{
            arenaMinX = teamRedCornerX - schem2BorderX;
            arenaMaxX = blueCornerX + schemsizeX + schem2BorderX;
            teamRedRotate = false;
            teamBlueRotate = true;
        }
        if(BlueToRedZ > 0){
            arenaMinZ = blueCornerZ - schem2BorderZ;
            arenaMaxZ = teamRedCornerZ + schemsizeZ + schem2BorderZ;
            teamRedRotate = true;
            teamBlueRotate = false;
        }else{
            arenaMinZ = teamRedCornerZ - schem2BorderZ;
            arenaMaxZ = blueCornerZ + schemsizeZ + schem2BorderZ;
            if(BlueToRedZ != 0){
                teamRedRotate = false;
                teamBlueRotate = true;
            }
        }
        if(!rotate){
            teamRedRotate = false;
            teamBlueRotate = false;
        }
        RedRotate = teamRedRotate;
        BlueRotate = teamBlueRotate;

        RedPasteRegion = Region.fromSize(teamRedCornerX, teamRedCornerY, teamRedCornerZ, schemsizeX, schemsizeY, schemsizeZ);
        BluePasteRegion = Region.fromSize(blueCornerX, blueCornerY, blueCornerZ, schemsizeX, schemsizeY, schemsizeZ);

        RedExtendRegion = Region.withExtension(teamRedCornerX, teamRedCornerY, teamRedCornerZ, schemsizeX, schemsizeY, schemsizeZ, PreperationArea, PreperationArea, PreperationArea);
        BlueExtendRegion = Region.withExtension(blueCornerX, blueCornerY, blueCornerZ, schemsizeX, schemsizeY, schemsizeZ, PreperationArea, PreperationArea, PreperationArea);
        ArenaRegion = Region.withExtension(arenaMinX, blueCornerY, arenaMinZ, arenaMaxX - arenaMinX, schemsizeY, arenaMaxZ - arenaMinZ, 0, PreperationArea, 0);
        PlayerRegion = new Region(arenaMinX, underBorder, arenaMinZ, arenaMaxX, world.getMaxHeight(), arenaMaxZ);

        BlueInsetRegion = new Region(BluePasteRegion.getMinX() + schemInsetX, BluePasteRegion.getMinY() + schemInsetBottom, BluePasteRegion.getMinZ() + schemInsetZ, BluePasteRegion.getMaxX() - schemInsetX, BluePasteRegion.getMaxY() - schemInsetTop, BluePasteRegion.getMaxZ() - schemInsetZ);

        int eventKampfID = Integer.parseInt(System.getProperty("fightID", "0"));
        if(eventKampfID >= 1){
            EventKampf = EventFight.get(eventKampfID);
            if(EventKampf == null){
                Bukkit.getLogger().log(Level.SEVERE, "Failed to load EventFight");
                Bukkit.shutdown();
            }

            assert EventKampf != null;
            Team team1 = Team.get(EventKampf.getTeamBlue());
            Team team2 = Team.get(EventKampf.getTeamRed());

            if(team1 == null || team2 == null){
                Bukkit.getLogger().log(Level.SEVERE, "Failed to load Team");
                Bukkit.shutdown();
            }

            assert team1 != null;
            assert team2 != null;
            TeamBlueName = team1.getTeamKuerzel();
            TeamRedName = team2.getTeamKuerzel();
            TeamBlueColor = "§" + team1.getTeamColor();
            TeamRedColor = "§" + team2.getTeamColor();
            EventTeamBlueID = team1.getTeamId();
            EventTeamRedID = team2.getTeamId();
            BothTeamsPublic = EventTeamRedID == 0 && EventTeamBlueID == 0;
            SpectatePort = EventKampf.getSpectatePort() == null ? 0 : EventKampf.getSpectatePort();
            LiveReplay = SpectatePort != 0;
            Referees = Referee.get(Config.EventKampf.getEventID());

            Event event = Event.get(EventKampf.getEventID());
            if(BothTeamsPublic) {
                OnlyPublicSchematics = true;
                MaximumTeamMembers = Integer.MAX_VALUE;
            }else{
                OnlyPublicSchematics = event.publicSchemsOnly();
                MaximumTeamMembers = event.getMaximumTeamMembers();
            }
        }else{
            //No event
            TeamRedColor = config.getString("Red.Prefix", "§c");
            TeamBlueColor = config.getString("Blue.Prefix", "§9");
            TeamRedName = config.getString("Red.Name", "Rot");
            TeamBlueName = config.getString("Blue.Name", "Blau");
            OnlyPublicSchematics = config.getBoolean("Schematic.OnlyPublicSchematics", false);
            EventTeamBlueID = 0;
            EventTeamRedID = 0;
            EventKampf = null;
            BothTeamsPublic = true;
            Referees = Collections.emptySet();
            MaximumTeamMembers = Integer.MAX_VALUE;
            LiveReplay = false;
            SpectatePort = -ReplayID;
        }

        String blueLeader = System.getProperty("blueLeader", null);
        String redLeader = System.getProperty("redLeader", null);
        if(blueLeader != null)
            BlueLeader = UUID.fromString(blueLeader);
        else
            BlueLeader = null;
        if(redLeader != null)
            RedLeader = UUID.fromString(redLeader);
        else
            RedLeader = null;

        if(CheckSchemID != 0){
            mode = ArenaMode.CHECK;
        }else if(PrepareSchemID != 0){
            mode = ArenaMode.PREPARE;
        }else if(eventKampfID >= 1){
            mode = ArenaMode.EVENT;
        }else if(eventKampfID == -1){
            mode = ArenaMode.TEST;
        }else if(ReplayID != 0){
            mode = ArenaMode.REPLAY;
        }else{
            mode = ArenaMode.NORMAL;
        }
    }

    public static boolean test(){
        return ArenaMode.Test.contains(mode);
    }
    public static boolean replayserver(){
        return ReplayID < 0;
    }
    public static boolean blueNegZ(){
        return BlueToRedZ > 0;
    }

    public static boolean isReferee(Player player) {
        return Referees.contains(SteamwarUser.get(player.getUniqueId()).getId());
    }
}
