package org.project;

import org.project.entity.players.Player;
import org.project.entity.players.Knight;
import org.project.entity.players.Wizard;
import org.project.entity.players.Assassin;

import org.project.entity.enemies.Enemy;
import org.project.entity.enemies.Goblin;
import org.project.entity.enemies.Skeleton;
import org.project.entity.enemies.Vampire;
import org.project.entity.enemies.Dragon;

import org.project.item.Item;

import org.project.location.Location;

import java.util.*;

public class GameEngine {

    private Player player;
    private Location currentLocation;
    private Location castle;

    private Scanner scanner;

    private Map<Location, Enemy[]> possibleEnemiesMap = new HashMap<>();

    private Enemy currentEnemy;

    public GameEngine() {
        scanner = new Scanner(System.in);
        setupGame();
    }

    private void setupGame() {

        System.out.println("Choose your class:");
        System.out.println("1. Knight");
        System.out.println("2. Mage");
        System.out.println("3. Assassin");

        int choice = scanner.nextInt();
        scanner.nextLine();

        switch (choice) {
            case 1:
                player = new Knight("Hero");
                break;
            case 2:
                player = new Wizard("Hero");
                break;
            case 3:
                player = new Assassin("Hero");
                break;
            default:
                System.out.println("Invalid choice, defaulting to Knight.");
                player = new Knight("Hero");
        }

        Location village = new Location("Village");
        Location forest = new Location("Forest");
        Location dungeon = new Location("Dungeon");
        Location graveyard = new Location("Graveyard");

        castle = new Location("Castle");

        village.connectLocation(forest);
        forest.connectLocation(village);

        forest.connectLocation(dungeon);
        dungeon.connectLocation(forest);

        forest.connectLocation(graveyard);
        graveyard.connectLocation(forest);

        village.connectLocation(castle);
        castle.connectLocation(village);

        currentLocation = village;

        possibleEnemiesMap.put(forest, new Enemy[] { new Goblin() });
        possibleEnemiesMap.put(dungeon, new Enemy[] { new Skeleton(), new Vampire() });
        possibleEnemiesMap.put(graveyard, new Enemy[] { new Skeleton(), new Vampire() });
        possibleEnemiesMap.put(castle, new Enemy[] { new Dragon() });

        spawnEnemyForLocation(currentLocation);
    }

    private void spawnEnemyForLocation(Location loc) {
        Enemy[] possibilities = possibleEnemiesMap.get(loc);
        if (possibilities == null || possibilities.length == 0) {
            currentEnemy = null;
            return;
        }

        Random rand = new Random();
        Enemy template = possibilities[rand.nextInt(possibilities.length)];
        currentEnemy = createEnemyInstance(template);
    }

    private Enemy createEnemyInstance(Enemy e) {
        if (e instanceof Goblin) return new Goblin();
        if (e instanceof Skeleton) return new Skeleton();
        if (e instanceof Vampire) return new Vampire();
        if (e instanceof Dragon) return new Dragon();
        return null;
    }

    public void startGame() {

        System.out.println("=== RPG Game Started ===");

        boolean running = true;

        while (running) {

            System.out.println("\n===== MENU =====");

            System.out.println("Location: " + currentLocation.getName());
            System.out.println("HP: " + player.getHp());
            System.out.println("Mana: " + player.getMana());
            System.out.println("Level: " + player.getLevel());
            System.out.println("XP: " + player.getXp());
            System.out.println("Keys: " + player.getKeyCount());

            System.out.println("\n1. Move");
            System.out.println("2. Fight");
            System.out.println("3. Show Inventory");
            System.out.println("4. Exit");

            int choice = scanner.nextInt();
            scanner.nextLine();

            switch (choice) {
                case 1:
                    move();
                    break;
                case 2:
                    fight();
                    break;
                case 3:
                    player.getInventory().showInventory();
                    break;
                case 4:
                    running = false;
                    System.out.println("Game Over.");
                    break;
                default:
                    System.out.println("Invalid choice.");
            }

            if (!player.isAlive()) {
                System.out.println("\nYou died. Game Over.");
                running = false;
            }
        }
    }

    private void move() {
        if (currentLocation.getConnectedLocations().isEmpty()) {
            System.out.println("No connected locations.");
            return;
        }

        System.out.println("\nWhere do you want to go?");

        for (int i = 0; i < currentLocation.getConnectedLocations().size(); i++) {
            System.out.println((i + 1) + ". " + currentLocation.getConnectedLocations().get(i).getName());
        }

        int choice = scanner.nextInt();

        if (choice < 1 || choice > currentLocation.getConnectedLocations().size()) {
            System.out.println("Invalid choice.");
            return;
        }

        Location nextLocation = currentLocation.getConnectedLocations().get(choice - 1);

        if (nextLocation == castle && player.getKeyCount() < 3) {
            System.out.println("\nThe castle gate is locked!");
            System.out.println("You need 3 keys.");
            return;
        }

        currentLocation = nextLocation;

        spawnEnemyForLocation(currentLocation);

        System.out.println("\nYou moved to " + currentLocation.getName());

        if (currentLocation == castle) {
            System.out.println("\nThe final battle awaits...");
        }
    }

    private void fight() {
        if (currentEnemy == null || !currentEnemy.isAlive()) {
            System.out.println("No enemies here.");
            return;
        }

        Enemy enemy = currentEnemy;

        System.out.println("\nA wild " + enemy.getName() + " appeared!");

        while (player.isAlive() && enemy.isAlive()) {

            System.out.println("\n===== COMBAT =====");
            System.out.println(player.getName() + " HP: " + player.getHp());
            System.out.println(enemy.getName() + " HP: " + enemy.getHp());

            System.out.println("\nChoose action:");
            System.out.println("1. Light Attack");
            System.out.println("2. Heavy Attack");
            System.out.println("3. Defend");
            System.out.println("4. Heal");
            System.out.println("5. Special Ability");

            int choice = scanner.nextInt();
            scanner.nextLine();

            switch (choice) {
                case 1:
                    player.lightAttack(enemy);
                    break;
                case 2:
                    player.heavyAttack(enemy);
                    break;
                case 3:
                    player.defend();
                    break;
                case 4:
                    player.heal();
                    break;
                case 5:
                    player.specialAbility(enemy);
                    break;
                default:
                    System.out.println("Invalid choice.");
                    continue;
            }

            if (enemy.isAlive()) {
                System.out.println("\n" + enemy.getName() + "'s turn!");
                enemy.attack(player);
            }
        }

        if (!enemy.isAlive()) {
            System.out.println("\n" + enemy.getName() + " was defeated!");

            player.gainXP(enemy.getXpReward());

            Item loot = enemy.dropLoot();

            if (loot != null) {
                player.getInventory().addItem(loot);
                System.out.println(enemy.getName() + " dropped: " + loot.getName());
            }

            currentEnemy = null;

            if (enemy instanceof Dragon) {
                System.out.println("\n====================");
                System.out.println("YOU DEFEATED THE DRAGON!");
                System.out.println("====== YOU WIN ======");
                System.exit(0);
            }
        }
    }
}
