/*
 *   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.towerrun.config;

import de.steamwar.sql.SchematicType;
import de.steamwar.towerrun.TowerRun;
import de.steamwar.towerrun.winconditions.FirstOutsideWincondition;
import de.steamwar.towerrun.winconditions.LastOutsideWincondition;
import de.steamwar.towerrun.winconditions.LastRemainingWincondition;
import de.steamwar.towerrun.winconditions.WinCondition;
import lombok.Getter;
import lombok.experimental.UtilityClass;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.Material;
import org.bukkit.configuration.ConfigurationSection;
import org.bukkit.configuration.file.YamlConfiguration;
import org.bukkit.util.Vector;

import java.io.File;
import java.util.ArrayList;
import java.util.List;

@UtilityClass
public class WorldConfig {

    public static final Region[] REGIONS;
    public static final Location[] DOORS;
    public static final Location[] KEYS;
    public static final int LAVA_Y;
    public static final int LAVE_SPACE;
    public static final Location SPAWN;
    public static final int ESCAPE_HEIGHT;
    public static final Location MIN_TOWER;
    public static final Location MAX_TOWER;
    public static final List<String> ACTIVE_WINCONDITIONS;
    public static final int MAP_MIN_X;
    public static final int MAP_MIN_Z;
    public static final int MAP_MAX_X;
    public static final int MAP_MAX_Z;
    public static final boolean MELTING;
    public static final TowerGeneratorConfig TOWER_GENERATOR_CONFIG;

    public static final List<WinCondition> WINCONDITIONS = new ArrayList<>();

    static {
        WINCONDITIONS.add(new LastRemainingWincondition());
        WINCONDITIONS.add(new LastOutsideWincondition());
        WINCONDITIONS.add(new FirstOutsideWincondition());
    }

    private static Location parseLocation(ConfigurationSection section) {
        Location loc = new Location(
                Bukkit.getWorlds().get(0),
                section.getDouble("x"),
                section.getDouble("y"),
                section.getDouble("z")
        );
        if (section.contains("yaw")) {
            loc.setYaw((float) section.getDouble("yaw"));
        }
        if (section.contains("pitch")) {
            loc.setPitch((float) section.getDouble("pitch"));
        }
        return loc;
    }

    static {
        File configFile = new File(Bukkit.getWorlds().get(0).getWorldFolder(), "config.yml");
        if (!configFile.exists()) {
            TowerRun.getInstance().getLogger().severe("World config not found!");
            Bukkit.shutdown();
        }

        ConfigurationSection config = YamlConfiguration.loadConfiguration(configFile);
        ConfigurationSection tower = config.getConfigurationSection("tower");

        List<ConfigurationSection> regions = tower.getConfigurationSection("regions").getKeys(false).stream()
                .map(tower.getConfigurationSection("regions")::getConfigurationSection)
                .toList();
        REGIONS = new Region[regions.size()];
        for (int i = 0; i < regions.size(); i++) {
            REGIONS[i] = new Region(regions.get(i));
        }

        if (REGIONS.length == 1) {
            MIN_TOWER = REGIONS[0].min;
            MAX_TOWER = REGIONS[0].max;
        } else {
            MIN_TOWER = new Location(
                    Bukkit.getWorlds().get(0),
                    Math.min(REGIONS[0].min.getX(), REGIONS[1].min.getX()),
                    0,
                    Math.min(REGIONS[0].min.getZ(), REGIONS[1].min.getZ())
            );
            MAX_TOWER = new Location(
                    Bukkit.getWorlds().get(0),
                    Math.max(REGIONS[0].max.getX(), REGIONS[1].max.getX()),
                    255,
                    Math.max(REGIONS[0].max.getZ(), REGIONS[1].max.getZ())
            );
            if (REGIONS.length > 2) {
                for (int i = 2; i < REGIONS.length; i++) {
                    MIN_TOWER.setX(Math.min(MIN_TOWER.getX(), REGIONS[i].min.getX()));
                    MIN_TOWER.setZ(Math.min(MIN_TOWER.getZ(), REGIONS[i].min.getZ()));
                    MAX_TOWER.setX(Math.max(MAX_TOWER.getX(), REGIONS[i].max.getX()));
                    MAX_TOWER.setZ(Math.max(MAX_TOWER.getZ(), REGIONS[i].max.getZ()));
                }
            }
        }

        ESCAPE_HEIGHT = tower.getInt("escapeHeight");
        SPAWN = parseLocation(tower.getConfigurationSection("spawn"));
        ConfigurationSection doorSection = tower.getConfigurationSection("doors");
        if (doorSection != null) {
            List<ConfigurationSection> doors = doorSection.getKeys(false).stream()
                    .map(tower.getConfigurationSection("doors")::getConfigurationSection)
                    .toList();
            DOORS = new Location[doors.size()];
            for (int i = 0; i < doors.size(); i++) {
                DOORS[i] = parseLocation(doors.get(i));
            }
        } else {
            DOORS = new Location[0];
        }

        ConfigurationSection keysSection = tower.getConfigurationSection("keys");
        if (keysSection != null) {
            List<ConfigurationSection> keys = keysSection.getKeys(false).stream()
                    .map(tower.getConfigurationSection("keys")::getConfigurationSection)
                    .toList();
            KEYS = new Location[keys.size()];
            for (int i = 0; i < keys.size(); i++) {
                KEYS[i] = parseLocation(keys.get(i));
            }
        } else {
            KEYS = new Location[0];
        }

        LAVA_Y = tower.getInt("lavaY");
        LAVE_SPACE = tower.getInt("laveSpace");

        MAP_MIN_X = config.getInt("minX");
        MAP_MIN_Z = config.getInt("minZ");
        MAP_MAX_X = config.getInt("maxX");
        MAP_MAX_Z = config.getInt("maxZ");

        MELTING = tower.getBoolean("melting") || tower.contains("meltingBlocks"); // Backwards compatibility with meltingBlocks key!

        ACTIVE_WINCONDITIONS = config.getStringList("winconditions");
        WINCONDITIONS.stream().filter(winCondition -> ACTIVE_WINCONDITIONS.contains(winCondition.getName())).forEach(winCondition -> winCondition.setActive(true));

        ConfigurationSection towerGeneratorSection = config.getConfigurationSection("towerGenerator");
        if (towerGeneratorSection == null) {
            TOWER_GENERATOR_CONFIG = null;
        } else {
            TOWER_GENERATOR_CONFIG = new TowerGeneratorConfig(towerGeneratorSection);
        }
    }

    @Getter
    public static final class Region {
        public final Location min;
        public final Location max;

        public Region(ConfigurationSection section) {
            min = new Location(
                    Bukkit.getWorlds().get(0),
                    section.getDouble("minX"),
                    0,
                    section.getDouble("minZ")
            );
            max = new Location(
                    Bukkit.getWorlds().get(0),
                    section.getDouble("maxX"),
                    255,
                    section.getDouble("maxZ")
            );
        }

        public boolean contains(Vector vector) {
            return vector.getX() >= min.getX() && vector.getX() <= max.getX()
                    && vector.getZ() >= min.getZ() && vector.getZ() <= max.getZ();
        }
    }

    @Getter
    public static final class TowerGeneratorConfig {
        public final int x;
        public final int y;
        public final int z;
        public final int minHeight;
        public final int maxHeight;
        public final int minNoBombFloors;
        public final int maxNoBombFloors;
        public final double tntChance;
        public final int minBombs;
        public final int maxBombs;
        public final double keyChance;
        public final int minNoKeyFloors;
        public final int maxNoKeyFloors;
        public final SchematicType schematicType;
        public final TowerGeneratorFillRegion[] fillRegions;
        public final Region[] tntRegions;
        public final TowerGeneratorDoorBlock[] doorBlocks;

        public TowerGeneratorConfig(ConfigurationSection section) {
            x = section.getInt("x");
            y = section.getInt("y");
            z = section.getInt("z");
            minHeight = section.getInt("minHeight");
            maxHeight = section.getInt("maxHeight");
            minNoBombFloors = section.getInt("minNoBombFloors");
            maxNoBombFloors = section.getInt("maxNoBombFloors");
            minBombs = section.getInt("minBombs");
            maxBombs = section.getInt("maxBombs");
            tntChance = section.getDouble("tntChance");
            keyChance = section.getDouble("keyChance");
            minNoKeyFloors = section.getInt("minNoKeyFloors");
            maxNoKeyFloors = section.getInt("maxNoKeyFloors");
            schematicType = SchematicType.fromDB(section.getString("schematicType"));

            ConfigurationSection fillRegionsSection = section.getConfigurationSection("fillRegions");
            if (fillRegionsSection != null) {
                List<ConfigurationSection> fillRegions = fillRegionsSection.getKeys(false).stream()
                        .map(fillRegionsSection::getConfigurationSection)
                        .toList();

                this.fillRegions = new TowerGeneratorFillRegion[fillRegions.size()];
                for (int i = 0; i < fillRegions.size(); i++) {
                    this.fillRegions[i] = new TowerGeneratorFillRegion(fillRegions.get(i));
                }
            } else {
                fillRegions = new TowerGeneratorFillRegion[0];
            }

            ConfigurationSection tntRegionsSection = section.getConfigurationSection("tntRegions");
            if (tntRegionsSection != null) {
                List<ConfigurationSection> tntRegions = tntRegionsSection.getKeys(false).stream()
                        .map(tntRegionsSection::getConfigurationSection)
                        .toList();

                this.tntRegions = new Region[tntRegions.size()];
                for (int i = 0; i < tntRegions.size(); i++) {
                    this.tntRegions[i] = new Region(tntRegions.get(i));
                }
            } else {
                tntRegions = new Region[0];
            }

            ConfigurationSection doorBlocksSection = section.getConfigurationSection("doorBlocks");
            if (doorBlocksSection != null) {
                List<ConfigurationSection> doorBlocks = doorBlocksSection.getKeys(false).stream()
                        .map(doorBlocksSection::getConfigurationSection)
                        .toList();

                this.doorBlocks = new TowerGeneratorDoorBlock[doorBlocks.size()];
                for (int i = 0; i < doorBlocks.size(); i++) {
                    this.doorBlocks[i] = new TowerGeneratorDoorBlock(doorBlocks.get(i));
                }
            } else {
                doorBlocks = new TowerGeneratorDoorBlock[0];
            }
        }
    }

    @Getter
    public static final class TowerGeneratorFillRegion {
        private final int minX;
        private final int minZ;
        private final int maxX;
        private final int maxZ;
        private final double percentage;
        private final Material material;

        public TowerGeneratorFillRegion(ConfigurationSection section) {
            minX = section.getInt("minX");
            minZ = section.getInt("minZ");
            maxX = section.getInt("maxX");
            maxZ = section.getInt("maxZ");
            percentage = section.getDouble("percentage");
            material = Material.valueOf(section.getString("material"));
        }
    }

    @Getter
    public static final class TowerGeneratorDoorBlock {
        private final int x;
        private final int dy;
        private final int z;

        public TowerGeneratorDoorBlock(ConfigurationSection section) {
            x = section.getInt("x");
            dy = section.getInt("dy");
            z = section.getInt("z");
        }
    }
}
