package org.project;

import org.project.entity.enemies.*;
import org.project.entity.players.*;

import org.project.location.Location;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import java.util.Scanner;

public class Main {

    private static final String RESET = "\u001B[0m";
    private static final String RED = "\u001B[31m";
    private static final String GREEN = "\u001B[32m";
    private static final String YELLOW = "\u001B[33m";
    private static final String BLUE = "\u001B[34m";

    private static boolean gameRunning = true;
    static List<Location> locations = new ArrayList<>();

    public static void main(String[] args) {

        Scanner scanner = new Scanner(System.in);

        Random random = new Random();

        Location castle = new Location("Castle", new Dragon());
        Location forest = new Location("Forest");
        Location ruins = new Location("Ruins");
        Location village = new Location("Village");

        locations.add(forest);
        locations.add(ruins);
        locations.add(village);
        locations.add(castle);

        boolean isRunning = true;

        while (isRunning){
            System.out.println(YELLOW + "------------------Java knight------------------" + RESET);
            System.out.println("  Choose your option :");
            System.out.println(BLUE + "   1." + RESET + " Start a new game");
            System.out.println(BLUE + "   2." + RESET + " Exit");
            int option = scanner.nextInt();
            switch (option){
                case 1:
                    startNewGame(scanner, random);
                    break;
                case 2:
                    System.out.println("Goodbye!");
                    isRunning = false;
                    break;
                default:
                    continue;
            }

        }

    }



    public static void startNewGame(Scanner scanner, Random random){
        Player player;
        int randNum1 = random.nextInt(3);

        System.out.println("Enter your name : ");
        String name = scanner.next();
        Location currentLocation = locations.get(randNum1);

        int charOption = 0;

        while ( (charOption <= 0) ||
                (charOption >= 4)) {
            System.out.println("Choose your character :");
            System.out.println(BLUE + "   1." + RESET + " Knight");
            System.out.println(BLUE + "   2." + RESET + " Assassin");
            System.out.println(BLUE + "   3." + RESET + " Wizard");
            charOption = scanner.nextInt();
        }

        player = switch (charOption) {
            case 1 -> new Knight(name);
            case 2 -> new Assassin(name);
            case 3 -> new Wizard(name);
            default -> null;
        };


        gameRunning = true;

        System.out.println("Going to : " + GREEN + currentLocation.getName() + RESET);
        spawnRandomEnemy(currentLocation,random);


        while (gameRunning && !player.isDead()){
            int choice = miniMenu(currentLocation,player);

            switch (choice){
                case 1:
                    if (currentLocation.hasEnemy()) {
                        fight(player, currentLocation.getEnemy(), currentLocation);
                        if (!player.isDead() && !currentLocation.hasEnemy()) {
                            // gain XP and recover
                            player.gainXP(100);
                            player.fullRestore();
                            System.out.println(GREEN + "HP and MP restored." + RESET);
                            // spawn a new enemy
                            spawnRandomEnemy(currentLocation, random);
                        }
                    }
                    break;
                case 2:
                    currentLocation = moveToLocation(scanner, currentLocation, player);
                    if (!currentLocation.hasEnemy()) {
                        spawnRandomEnemy(currentLocation, random);
                    }
                    System.out.println(GREEN + "Arrived at: " + currentLocation.getName() + RESET);
                    break;
                case 3:
                    if (player.hasAllKeys()){
                        currentLocation = locations.get(3);
                        System.out.println("--DRAGON FIGHT--" );
                        Enemy dragon = new Dragon();
                        fight(player,dragon,currentLocation);
                        if (dragon.isDead()){
                            System.out.println(YELLOW + "VICTORY!" + RESET);
                            System.out.println("Level : " + player.getLevel());
                            gameRunning = false;
                        }
                    }else {
                        System.out.println("Locked !");
                    }
                    break;

            }
        }
    }

    public static int miniMenu(Location loc,Player player){
        boolean castleUnLocked = player.hasGoblinKey() && player.hasSkelKey() && player.hasVampKey();
        Scanner scanner = new Scanner(System.in);
        int chosenOpt = 0;

        while (chosenOpt <= 0 ||
                chosenOpt >= 4){
            System.out.println("Choose an option : ");

            System.out.println(BLUE + "   1." + RESET + " Stay at " + loc.getName() + " and fight with " + loc.getEnemy().getName());
            System.out.println(BLUE + "   2." + RESET + " Move to another place");
            if (castleUnLocked){
                System.out.println(BLUE + "   3." + YELLOW +  " Go to castle and fight with the dragon" + RESET);
            }
            else {
                System.out.println("   \uD83D\uDD12" + BLUE + "3. Go to castle and fight with the dragon" + RESET);
            }

            chosenOpt = scanner.nextInt();

        }

        return chosenOpt;
    }

    private static void spawnRandomEnemy(Location location, Random random) {
        int enemyType = random.nextInt(3);
        switch (enemyType){
            case 0:
                location.setEnemy(new Vampire());
                System.out.println("A " + RED + "Vampire" + RESET + " Spawned !");
                break;
            case 1:
                location.setEnemy(new Skeleton());
                System.out.println("A Skeleton Spawned !");
                break;
            case 2:
                location.setEnemy(new Goblin());
                System.out.println("A " + GREEN + "Goblin" + RESET + " Spawned !");
                break;
        }
    }

    private static Location moveToLocation(Scanner scanner, Location current, Player player) {
        int choice = -1;
        do {
            System.out.println("Choose a location to move to:");
            for (int i = 0; i < locations.size(); i++) {
                String prefix = locations.get(i).equals(current) ? " * " : "   ";
                System.out.println(BLUE + "   " + (i + 1) + "." + RESET + " " + prefix + locations.get(i).getName());
            }
            choice = scanner.nextInt() - 1;
            if (choice == 3 && !player.hasAllKeys()){
                System.out.println("Locked!");
                choice = -1;
            }
        } while (choice < 0 || choice >= locations.size());

        return locations.get(choice);
    }

    private static void fight(Player player, Enemy enemy, Location location) {
        System.out.println(RED + " COMBAT STARTED!" + RESET);

        while (!player.isDead() && !enemy.isDead()) {
            playerTurn(player, enemy);

            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }

            if (enemy.isDead()) {
                break;
            }

            if (!enemy.isStunned()) {
                enemyTurn(player, enemy);
            } else {
                System.out.println(BLUE + enemy.getName() + RESET + " is stunned. skipping to player...");
                enemy.stun(false);
            }
        }

        if (player.isDead()){
            System.out.println("Game over.");
            System.out.println("Level : " + player.getLevel());
            gameRunning = false;
        } else if (enemy.isDead()) {
            System.out.println(player.getName() + RED + " Killed " + "a " + RESET + enemy.getName() + " at " + GREEN + location.getName() + RESET);
            keyMechanic(player,enemy);
            location.clearEnemy();

        }

    }

    private static void playerTurn(Player player, Enemy enemy) {
        Scanner scanner = new Scanner(System.in);
        System.out.println("\n" + BLUE + "Your Turn:" + RESET);
        System.out.println(RED + "HP : "  + RESET + player.getHp() + "/" + player.getMaxHP() +
                GREEN + " | MP: " + RESET + player.getMp() + "/" + player.getMaxMP());
        System.out.println(BLUE + "   1." + RESET + " Light Attack " + GREEN + "(0 MP)" + RESET);
        System.out.println(BLUE + "   2." + RESET + " Heavy Attack " + GREEN + "(-" + player.getWeapon().getManaCost() + " MP)" + RESET);
        System.out.println(BLUE + "   3." + RESET + " Defend " + GREEN + "(-5 MP)" + RESET);
        System.out.println(BLUE + "   4." + RESET + " Heal " + GREEN + "(-10 MP)" + RESET);
        System.out.println(BLUE + "   5." + RESET + " Special Ability " + GREEN + "(-" + player.specialAblMPCost() + " MP)" + RESET);

        int choice;
        do {
            choice = scanner.nextInt();
        } while (choice <= 0 ||
                choice >= 6);

        switch (choice) {
            case 1:
                player.lightAttack(enemy);
                break;
            case 2:
                if (player.getMp() >= player.getWeapon().getManaCost()) {
                    player.heavyAttack(enemy);
                } else {
                    System.out.println(RED + "Not enough MP!, light attacking." + RESET);
                    player.lightAttack(enemy);
                }
                break;
            case 3:
                player.defend();
                break;
            case 4:
                if (player.getMp() >= 10) {
                    player.heal(30);
                } else {
                    System.out.println(RED + "Not enough MP!" + RESET);
                }
                break;
            case 5:
                if (player.getMp() >= player.specialAblMPCost()) {
                    player.specialAbility(enemy);
                } else {
                    System.out.println(RED + "Not enough MP!" + RESET);
                }
                break;
        }

        System.out.println(RED + "HP : "  + RESET + player.getHp() + "/" + player.getMaxHP() +
                GREEN + " | MP: " + RESET + player.getMp() + "/" + player.getMaxMP());
        System.out.println(RED + "Enemy's HP : " + RESET + enemy.getHp());
    }

    private static void enemyTurn(Player player,Enemy enemy) {
        System.out.println(RED + "\n" + enemy.getName() + "'s Turn:" + RESET);

        // randomly choosing between attack and specialAbility with probability of 1/3
        Random random = new Random();
        if (random.nextInt(3) == 0) {
            enemy.specialAbility(player);
        } else {
            enemy.attack(player);
        }

        System.out.println(RED + "Your HP : "  + RESET + player.getHp() + "/" + player.getMaxHP());
    }

    private static void keyMechanic(Player player, Enemy enemy){
        Random random = new Random();

        if (enemy instanceof Goblin){
            if (player.hasGoblinKey()) return;
            if (random.nextInt(3) == 0) player.gotGoblinKey();
        }
        else if (enemy instanceof Vampire){
            if (player.hasVampKey()) return;
            if (random.nextInt(3) == 0) player.gotVampKey();
        }
        else if (enemy instanceof Skeleton) {
            if (player.hasSkelKey()) return;
            if (random.nextInt(3) == 0) player.gotSkelKey();
        }


    }



}