package org.project.game;

import org.project.entity.enemies.*;
import org.project.entity.players.Player;
import org.project.entity.players.Knight;
import org.project.location.Location;
import org.project.item.consumables.Consumable;
import org.project.item.consumables.Flask;
import org.project.item.consumables.ManaPotion;
import org.project.utils.ConsoleColors;

import java.util.*;

/**
 * GameManager.java
 * Controls the main game loop, combat system, and progression mechanics.
 *
 * @author [Your Name]
 * @version 1.0
 */
public class GameManager {

    // Game state fields
    private Player player;
    private Location currentLocation;
    private List<Location> locations;
    private Scanner scanner;
    private List<Consumable> inventory;

    // Key collection - required to unlock the castle
    private Set<Key> collectedKeys;
    private boolean dragonDefeated;
    private boolean castleUnlocked;

    // Random number generator for enemy spawns and drops
    private Random random;

    /**
     * Initializes the game with a player and starting location.
     *
     * @param player The player character (Knight, Assassin, or Wizard)
     * @param startingLocation Where the adventure begins
     */
    public GameManager(Player player, Location startingLocation) {
        this.player = player;
        this.currentLocation = startingLocation;
        this.locations = new ArrayList<>();
        this.scanner = new Scanner(System.in);
        this.inventory = new ArrayList<>();
        this.collectedKeys = new HashSet<>();
        this.dragonDefeated = false;
        this.castleUnlocked = false;
        this.random = new Random();

        // Starter items
        inventory.add(new Flask());
        inventory.add(new Flask());
        inventory.add(new ManaPotion());
    }

    /**
     * Adds a location to the game world.
     *
     * @param location New location to add
     */
    public void addLocation(Location location) {
        locations.add(location);
    }

    /**
     * Starts the game and displays the welcome message.
     */
    public void startGame() {
        System.out.println(ConsoleColors.YELLOW_BOLD + "=== WELCOME TO THE REALM OF DRAGONS ===" + ConsoleColors.RESET);
        System.out.println(ConsoleColors.RED + "A curse has befallen the land!" + ConsoleColors.RESET);
        System.out.println(ConsoleColors.CYAN + "You must collect the 3 ancient keys to unlock the castle," + ConsoleColors.RESET);
        System.out.println(ConsoleColors.CYAN + "defeat the Dragon, and break the curse!" + ConsoleColors.RESET);
        System.out.println(ConsoleColors.PURPLE + "----------------------------------------" + ConsoleColors.RESET);
        System.out.println();

        gameLoop();
    }

    /**
     * Main game loop - handles player turns and game state.
     */
    private void gameLoop() {
        boolean running = true;

        while (running) {
            // Check for game over
            if (!player.isAlive()) {
                System.out.println(ConsoleColors.RED_BOLD + "💀 You have been defeated! Game Over!" + ConsoleColors.RESET);
                break;
            }

            // Check for victory
            if (dragonDefeated) {
                System.out.println(ConsoleColors.YELLOW_BOLD + "🎉 CONGRATULATIONS! YOU HAVE DEFEATED THE DRAGON!" + ConsoleColors.RESET);
                System.out.println(ConsoleColors.GREEN_BOLD + "The curse is broken and the land is saved!" + ConsoleColors.RESET);
                break;
            }

            displayLocationInfo();
            System.out.println();

            // Spawn enemy if location is empty
            if (!currentLocation.hasEnemies()) {
                spawnRandomEnemy();
            }

            checkCastleUnlock();
            showMainMenu();

            String choice = scanner.nextLine().trim();

            switch (choice) {
                case "1":
                    fightEnemy();
                    break;
                case "2":
                    moveToLocation();
                    break;
                case "3":
                    if (castleUnlocked) {
                        fightDragon();
                    } else {
                        System.out.println(ConsoleColors.RED + "The castle is locked! You need all 3 keys to enter." + ConsoleColors.RESET);
                        System.out.println(ConsoleColors.YELLOW + "Keys collected: " + collectedKeys.size() + "/3" + ConsoleColors.RESET);
                    }
                    break;
                case "4":
                    useInventory();
                    break;
                case "5":
                    displayStatus();
                    break;
                case "6":
                    running = false;
                    System.out.println(ConsoleColors.YELLOW + "Thanks for playing!" + ConsoleColors.RESET);
                    break;
                default:
                    System.out.println(ConsoleColors.RED + "Invalid choice. Try again." + ConsoleColors.RESET);
            }
        }
        scanner.close();
    }

    /**
     * Displays current location information and player stats.
     */
    private void displayLocationInfo() {
        System.out.println(ConsoleColors.CYAN_BOLD + "\n📍 " + currentLocation.getName() + ConsoleColors.RESET);
        System.out.println(currentLocation.getDescription());
        System.out.println(ConsoleColors.GREEN + "HP: " + player.getHP() + "/" + player.getMaxHP() +
                " | " + ConsoleColors.BLUE + "MP: " + player.getMP() + "/" + player.getMaxMP() +
                " | " + ConsoleColors.YELLOW + "Level: " + player.getLevel() + ConsoleColors.RESET);
    }

    /**
     * Displays the main menu with all available actions.
     * Castle option only appears when unlocked.
     */
    private void showMainMenu() {
        System.out.println("\n" + ConsoleColors.WHITE_BOLD + "What would you like to do?" + ConsoleColors.RESET);
        System.out.println("1. ⚔️ Fight the enemy");
        System.out.println("2. 🚶 Move to another location");

        if (castleUnlocked) {
            System.out.println(ConsoleColors.YELLOW_BOLD + "3. 🏰 Go to the Castle to fight the Dragon" + ConsoleColors.RESET);
        } else {
            System.out.println("3. 🔒 Castle (Locked - Need all 3 keys)");
        }

        System.out.println("4. 🎒 Use Inventory");
        System.out.println("5. 📊 Check Status");
        System.out.println("6. ❌ Quit");

        // Show key progress if castle is locked
        if (!castleUnlocked) {
            System.out.println(ConsoleColors.CYAN + "Keys collected: " + collectedKeys.size() + "/3" + ConsoleColors.RESET);
            System.out.println(ConsoleColors.CYAN + "  - Goblin Key: " + (collectedKeys.contains(Key.GOBLIN_KEY) ? "✅" : "❌") + ConsoleColors.RESET);
            System.out.println(ConsoleColors.CYAN + "  - Skeleton Key: " + (collectedKeys.contains(Key.SKELETON_KEY) ? "✅" : "❌") + ConsoleColors.RESET);
            System.out.println(ConsoleColors.CYAN + "  - Vampire Key: " + (collectedKeys.contains(Key.VAMPIRE_KEY) ? "✅" : "❌") + ConsoleColors.RESET);
        }
    }

    /**
     * Spawns a random enemy with randomized stats.
     * Enemy types: Goblin, Skeleton, Vampire.
     */
    private void spawnRandomEnemy() {
        int enemyType = random.nextInt(3);
        Enemy enemy;

        switch (enemyType) {
            case 0:
                int goblinHP = 40 + random.nextInt(20);
                int goblinMP = 15 + random.nextInt(10);
                int goblinDamage = 8 + random.nextInt(5);
                int goblinXP = 35 + random.nextInt(20);
                enemy = new Goblin("Goblin", goblinHP, goblinMP, goblinDamage, goblinXP);
                break;
            case 1:
                int skeletonHP = 50 + random.nextInt(20);
                int skeletonMP = 20 + random.nextInt(10);
                int skeletonDamage = 10 + random.nextInt(5);
                int skeletonXP = 50 + random.nextInt(25);
                enemy = new Skeleton("Skeleton", skeletonHP, skeletonMP, skeletonDamage, null, skeletonXP);
                break;
            case 2:
                int vampireHP = 65 + random.nextInt(25);
                int vampireMP = 40 + random.nextInt(15);
                int vampireDamage = 15 + random.nextInt(5);
                int vampireXP = 70 + random.nextInt(30);
                enemy = new Vampire("Vampire", vampireHP, vampireMP, vampireDamage, null, vampireXP);
                break;
            default:
                enemy = new Goblin();
        }

        currentLocation.addEnemy(enemy);
        System.out.println(ConsoleColors.RED_BOLD + "💀 A wild " + enemy.getName() + " appears!" + ConsoleColors.RESET);
    }

    /**
     * Checks if all 3 keys have been collected and unlocks the castle.
     */
    private void checkCastleUnlock() {
        if (!castleUnlocked && collectedKeys.size() >= 3) {
            castleUnlocked = true;
            System.out.println(ConsoleColors.YELLOW_BOLD + "🔓 The castle gates have opened!" + ConsoleColors.RESET);
            System.out.println(ConsoleColors.GREEN + "You have collected all 3 keys! You may now face the Dragon." + ConsoleColors.RESET);
        }
    }

    /**
     * Starts combat with the enemy at the current location.
     */
    private void fightEnemy() {
        if (!currentLocation.hasEnemies()) {
            System.out.println(ConsoleColors.YELLOW + "There are no enemies here!" + ConsoleColors.RESET);
            return;
        }

        Enemy enemy = currentLocation.getEnemies().get(0);
        combatLoop(enemy);
    }

    /**
     * Moves the player to a connected location.
     */
    private void moveToLocation() {
        List<Location> connections = currentLocation.getConnectedLocations();
        if (connections.isEmpty()) {
            System.out.println(ConsoleColors.YELLOW + "There's nowhere to go from here." + ConsoleColors.RESET);
            return;
        }

        System.out.println("\n" + ConsoleColors.CYAN + "Available destinations:" + ConsoleColors.RESET);
        for (int i = 0; i < connections.size(); i++) {
            System.out.println((i + 1) + ". " + connections.get(i).getName());
        }
        System.out.println("0. Cancel");

        try {
            int choice = Integer.parseInt(scanner.nextLine().trim());
            if (choice > 0 && choice <= connections.size()) {
                currentLocation = connections.get(choice - 1);
                System.out.println(ConsoleColors.GREEN + "You travel to " + currentLocation.getName() + "..." + ConsoleColors.RESET);
                System.out.println();
            }
        } catch (NumberFormatException e) {
            System.out.println(ConsoleColors.RED + "Invalid input." + ConsoleColors.RESET);
        }
    }

    /**
     * Opens the inventory menu for using consumable items.
     */
    private void useInventory() {
        if (inventory.isEmpty()) {
            System.out.println(ConsoleColors.YELLOW + "Your inventory is empty." + ConsoleColors.RESET);
            return;
        }

        System.out.println("\n" + ConsoleColors.CYAN_BOLD + "Inventory:" + ConsoleColors.RESET);
        for (int i = 0; i < inventory.size(); i++) {
            System.out.println((i + 1) + ". " + inventory.get(i).getName());
        }
        System.out.println("0. Cancel");

        try {
            int choice = Integer.parseInt(scanner.nextLine().trim());
            if (choice > 0 && choice <= inventory.size()) {
                Consumable item = inventory.remove(choice - 1);
                item.use(player);
            }
        } catch (NumberFormatException e) {
            System.out.println(ConsoleColors.RED + "Invalid input." + ConsoleColors.RESET);
        }
    }

    /**
     * Displays player status including stats, equipment, and key progress.
     */
    private void displayStatus() {
        System.out.println("\n" + ConsoleColors.YELLOW_BOLD + "=== Player Status ===" + ConsoleColors.RESET);
        System.out.println(ConsoleColors.CYAN + "Name: " + player.getName() + ConsoleColors.RESET);
        System.out.println(ConsoleColors.YELLOW + "Level: " + player.getLevel() + ConsoleColors.RESET);
        System.out.println(ConsoleColors.GREEN + "HP: " + player.getHP() + "/" + player.getMaxHP() + ConsoleColors.RESET);
        System.out.println(ConsoleColors.BLUE + "MP: " + player.getMP() + "/" + player.getMaxMP() + ConsoleColors.RESET);
        System.out.println(ConsoleColors.RED + "Damage: " + player.getDamage() + ConsoleColors.RESET);
        System.out.println(ConsoleColors.PURPLE + "Weapon: " + (player.getWeapon() != null ? player.getWeapon().getName() : "None") + ConsoleColors.RESET);
        System.out.println("Inventory Items: " + inventory.size());

        System.out.println("\n" + ConsoleColors.YELLOW_BOLD + "=== Keys Collected ===" + ConsoleColors.RESET);
        System.out.println("Goblin Key: " + (collectedKeys.contains(Key.GOBLIN_KEY) ? "✅" : "❌"));
        System.out.println("Skeleton Key: " + (collectedKeys.contains(Key.SKELETON_KEY) ? "✅" : "❌"));
        System.out.println("Vampire Key: " + (collectedKeys.contains(Key.VAMPIRE_KEY) ? "✅" : "❌"));
        System.out.println("Total: " + collectedKeys.size() + "/3");
        System.out.println("Castle: " + (castleUnlocked ? "🔓 UNLOCKED" : "🔒 LOCKED"));
    }

    /**
     * Turn-based combat loop.
     *
     * @param enemy The enemy being fought
     */
    private void combatLoop(Enemy enemy) {
        System.out.println("\n" + ConsoleColors.RED_BOLD + "⚔️ Combat with " + enemy.getName() + "!" + ConsoleColors.RESET);

        while (enemy.isAlive() && player.isAlive()) {
            // Show combat status
            System.out.println("\n" + ConsoleColors.RED + enemy.getName() + " HP: " + enemy.getHP() + "/" + enemy.getMaxHP() + ConsoleColors.RESET);
            System.out.println(ConsoleColors.GREEN + "Your HP: " + player.getHP() + "/" + player.getMaxHP() + ConsoleColors.RESET);
            System.out.println(ConsoleColors.BLUE + "Your MP: " + player.getMP() + "/" + player.getMaxMP() + ConsoleColors.RESET);

            // Player action menu
            System.out.println("\n" + ConsoleColors.WHITE_BOLD + "Choose your action:" + ConsoleColors.RESET);
            System.out.println("1. ⚔️ Light Attack");
            System.out.println("2. 💥 Heavy Attack (8 MP)");
            System.out.println("3. ✨ Special Ability");
            System.out.println("4. 🛡️ Defend (5 MP)");
            System.out.println("5. 💚 Heal (10 MP)");
            System.out.println("6. 🎒 Use Item");

            String choice = scanner.nextLine().trim();

            // Execute player action
            switch (choice) {
                case "1":
                    player.lightAttack(enemy);
                    break;
                case "2":
                    player.heavyAttack(enemy);
                    break;
                case "3":
                    player.specialAbility(enemy);
                    break;
                case "4":
                    player.defend();
                    break;
                case "5":
                    player.heal();
                    break;
                case "6":
                    useInventory();
                    break;
                default:
                    System.out.println(ConsoleColors.RED + "Invalid choice." + ConsoleColors.RESET);
                    continue;
            }

            // Enemy turn
            if (enemy.isAlive()) {
                // Knight's Shield Bash stuns the enemy
                if (player instanceof Knight && ((Knight) player).shouldStunEnemy()) {
                    enemy.stun();
                    ((Knight) player).resetStun();
                }

                enemy.nextTurn();
                enemy.attack(player);
            } else {
                handleEnemyDefeat(enemy);
                break;
            }
        }
    }

    /**
     * Processes enemy defeat: grants XP, handles key drops, and recovers health.
     *
     * @param enemy The defeated enemy
     */
    private void handleEnemyDefeat(Enemy enemy) {
        // Grant XP
        int xpReward = enemy.getXPReward();
        player.gainXP(xpReward);
        System.out.println(ConsoleColors.YELLOW_BOLD + "🏆 Defeated " + enemy.getName() + "! Gained " + xpReward + " XP." + ConsoleColors.RESET);

        // Remove enemy from location
        currentLocation.removeEnemy(enemy);

        // Key drop (20% chance)
        Key key = getKeyForEnemy(enemy);
        if (key != null && !collectedKeys.contains(key)) {
            if (random.nextDouble() < 0.2) {
                collectedKeys.add(key);
                System.out.println(ConsoleColors.GREEN_BOLD + "🔑 You obtained the " + key.getDisplayName() + "!" + ConsoleColors.RESET);
                System.out.println(ConsoleColors.CYAN + "Keys collected: " + collectedKeys.size() + "/3" + ConsoleColors.RESET);
                checkCastleUnlock();
            } else {
                System.out.println(ConsoleColors.YELLOW + "No key dropped this time." + ConsoleColors.RESET);
            }
        } else if (key != null && collectedKeys.contains(key)) {
            System.out.println(ConsoleColors.YELLOW + "You already have the " + key.getDisplayName() + "." + ConsoleColors.RESET);
        }

        // Item drop (30% chance)
        if (random.nextDouble() < 0.3) {
            Consumable drop = random.nextBoolean() ? new Flask() : new ManaPotion();
            inventory.add(drop);
            System.out.println(ConsoleColors.GREEN_BOLD + "💎 " + enemy.getName() + " dropped a " + drop.getName() + "!" + ConsoleColors.RESET);
        }

        // Full recovery after battle
        player.heal(player.getMaxHP());
        player.fillMana(player.getMaxMP());
        System.out.println(ConsoleColors.GREEN + "🔄 You recovered fully after battle!" + ConsoleColors.RESET);
    }

    /**
     * Determines which key an enemy can drop based on its type.
     *
     * @param enemy The enemy to check
     * @return The key type, or null if no key is dropped
     */
    private Key getKeyForEnemy(Enemy enemy) {
        if (enemy instanceof Goblin) {
            return Key.GOBLIN_KEY;
        } else if (enemy instanceof Skeleton) {
            return Key.SKELETON_KEY;
        } else if (enemy instanceof Vampire) {
            return Key.VAMPIRE_KEY;
        }
        return null;
    }

    /**
     * Final boss battle against the Dragon.
     * Only accessible after collecting all 3 keys.
     */
    private void fightDragon() {
        if (!castleUnlocked) {
            System.out.println(ConsoleColors.RED + "The castle is locked! You need all 3 keys." + ConsoleColors.RESET);
            return;
        }

        Dragon dragon = new Dragon("Ember, the Ancient Dragon", 300, 120, 35, null, 800);

        System.out.println(ConsoleColors.RED_BOLD + "🐉 THE DRAGON EMERGES FROM THE CASTLE!" + ConsoleColors.RESET);
        System.out.println(ConsoleColors.RED + "Ember roars: 'You dare challenge me, mortal?'" + ConsoleColors.RESET);
        System.out.println(ConsoleColors.YELLOW + "This is the final battle!" + ConsoleColors.RESET);

        while (dragon.isAlive() && player.isAlive()) {
            System.out.println("\n" + ConsoleColors.RED_BOLD + "🐉 DRAGON BOSS FIGHT!" + ConsoleColors.RESET);
            System.out.println(ConsoleColors.RED + "Dragon HP: " + dragon.getHP() + "/" + dragon.getMaxHP() + ConsoleColors.RESET);
            System.out.println(ConsoleColors.GREEN + "Your HP: " + player.getHP() + "/" + player.getMaxHP() + ConsoleColors.RESET);
            System.out.println(ConsoleColors.BLUE + "Your MP: " + player.getMP() + "/" + player.getMaxMP() + ConsoleColors.RESET);

            if (dragon.isEnraged()) {
                System.out.println(ConsoleColors.RED_BOLD + "🔥 The Dragon is ENRAGED!" + ConsoleColors.RESET);
            }

            System.out.println("\n" + ConsoleColors.WHITE_BOLD + "Choose your action:" + ConsoleColors.RESET);
            System.out.println("1. ⚔️ Light Attack");
            System.out.println("2. 💥 Heavy Attack (8 MP)");
            System.out.println("3. ✨ Special Ability");
            System.out.println("4. 🛡️ Defend (5 MP)");
            System.out.println("5. 💚 Heal (10 MP)");
            System.out.println("6. 🎒 Use Item");

            String choice = scanner.nextLine().trim();

            switch (choice) {
                case "1":
                    player.lightAttack(dragon);
                    break;
                case "2":
                    player.heavyAttack(dragon);
                    break;
                case "3":
                    player.specialAbility(dragon);
                    break;
                case "4":
                    player.defend();
                    break;
                case "5":
                    player.heal();
                    break;
                case "6":
                    useInventory();
                    break;
                default:
                    System.out.println(ConsoleColors.RED + "Invalid choice." + ConsoleColors.RESET);
                    continue;
            }

            // Dragon's turn
            if (dragon.isAlive()) {
                if (player instanceof Knight && ((Knight) player).shouldStunEnemy()) {
                    dragon.stun();
                    ((Knight) player).resetStun();
                }

                dragon.nextTurn();
                dragon.attack(player);
            } else {
                dragonDefeated = true;
                System.out.println(ConsoleColors.YELLOW_BOLD + "🏆 YOU HAVE DEFEATED THE DRAGON!" + ConsoleColors.RESET);
                System.out.println(ConsoleColors.GREEN_BOLD + "🎉 The curse is broken! The land is saved!" + ConsoleColors.RESET);
                System.out.println(ConsoleColors.YELLOW + "You are a true hero!" + ConsoleColors.RESET);
            }
        }

        if (!player.isAlive()) {
            System.out.println(ConsoleColors.RED_BOLD + "💀 The Dragon has defeated you!" + ConsoleColors.RESET);
            System.out.println(ConsoleColors.RED + "Game Over!" + ConsoleColors.RESET);
        }
    }

    // Getters
    public Player getPlayer() {
        return player;
    }

    public Location getCurrentLocation() {
        return currentLocation;
    }

    public Set<Key> getCollectedKeys() {
        return collectedKeys;
    }

    public boolean isCastleUnlocked() {
        return castleUnlocked;
    }
}