using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.SceneManagement;

/// <summary>
/// Załozenia na przyszłość
///  - player atakując powinien wysyłać informacje o akcji ataku z wartością obrażeń
///     enemy przymując ją powinien kalkulować szanse uniku / bloku (i odskakiwać w bokj lub do tyłu) albo przyjmować obrazenia
///     WSZĘDZIE ANIMACJA blokuąca inne akcje na czas odtwarzania efektu
/// </summary>
public enum WizardBattleStepsEnum
{
    First,      // have 100% hp, lasts until the value will be reduced to 7%%
    Second,     // have 75%% hp, lasts until the value will be reduced to 50%
    Third,      // have 50%% hp, lasts until the value will be reduced to 25%
    Last,
    Summoning,  // after reducing hp to 75, 50, 25 per cent wizard start to summon minions
    Escaping    // when player have 25% he starts escape - go to teleport
}

[RequireComponent(typeof(NPC))]
[RequireComponent(typeof(AStarPathfindingAgent))]
class BattleWizard : MonoBehaviour
{
    public const string BATTLE_STATE = "BattleState";

    public WizardBattleStepsEnum BattleState = WizardBattleStepsEnum.First;

    [Header("Health")]
    public float defence = 0;
    public int maxHealth = 100;
    public float currentHealth;

    public bool canTakeDamage = true;

    public float attackValue = 5;
    public float attackingRadius = 6;

    [Header("Summoned Minions")]
    public GameObject MinionRespowner;
    public int SummonedMinionsCounter = 3;
    public int KilledMinionsCounter = 0;
    public bool IsAfterSummoning = false;
    
    [Header("Step points")]
    public bool approaching = false; // mean - we can go closer to Player
    [Space]
    public int CurrentPoint = 0;
    public Transform[] Points;
    public Vector3 TargetPosition;


    [Space]
    [Header("Attacking Logic")]
    public bool hit = false;

    public bool firstAttack = false;

    public float timerDmg = 0f;
    public float timeToWaitBeforeNextAttack = 1.0f;  // time which npc must wait before he can atack player again

    public float timerHit = 0f;
    public float timeToWaitBeforeNextHitFromPlayer = 0.55f;  // should be moved to player script !!!!

    public bool isPanelEnabled = true;          // flag about some panel status.... (probably youDied / respown)

    private void Start()
    {
        MinionRespowner.GetComponent<CounterRespowner>().Respown = false;
        MinionRespowner.GetComponent<CounterRespowner>().Counter = 0;

        currentHealth = maxHealth;


        if (HasProggress())
        {
            BattleState = GetProggress();

            switch(BattleState)
            {
                case WizardBattleStepsEnum.First:
                {
                    // to do nothing to do here
                    break;
                }
                case WizardBattleStepsEnum.Second:
                {
                    BattleState = WizardBattleStepsEnum.First; // go one stop earlier to propertly go throught Escaping and Summoning

                    gameObject.transform.position = Points[0].position;

                    currentHealth = maxHealth * 0.75f;

                    SummonedMinionsCounter = 3;

                    CurrentPoint = 1;
                    break;
                }
                case WizardBattleStepsEnum.Third:
                {
                    BattleState = WizardBattleStepsEnum.Second;

                    gameObject.transform.position = Points[1].position;

                    currentHealth = maxHealth * 0.50f;

                    SummonedMinionsCounter = 6;

                    CurrentPoint = 2;

                    break;
                }
                case WizardBattleStepsEnum.Last:
                {
                    BattleState = WizardBattleStepsEnum.Third;

                    gameObject.transform.position = Points[2].position;

                    currentHealth = maxHealth * 0.25f;

                    SummonedMinionsCounter = 12;

                    CurrentPoint = 3;
                    
                    break;
                }
            }
        }
        else
        {
            gameObject.GetComponent<NPC>().State = NPCStateEnum.None;

            BattleState = WizardBattleStepsEnum.First;
            CurrentPoint = 0;
            SummonedMinionsCounter = 3; // deffault value at start
        }

        defecnceCalculate();
        speedCalculate();

        // control battle proggress
        ManageBehaviourScenario();
    }

    private void Update()
    {
        if (gameObject.GetComponent<NPC>().State == NPCStateEnum.Walking)
        {
            var dir = TargetPosition - transform.position;
            float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
            dir.Normalize();

            gameObject.GetComponent<Animator>().SetBool("isRunning", new Vector2( dir.x, dir.y) != Vector2.zero);
        }

        // taking demage by time logic
        TakingDamageManagment();

        // take action based on npc state
        HandleState();


        // config battle depending on state
        if (IsAfterSummoning)
        {
            //wait for killing minions by player
            if (MinionRespowner.GetComponent<CounterRespowner>().killedMinions >= SummonedMinionsCounter * 3/5f)
            {
                SummonedMinionsCounter *= 2;

                MinionRespowner.GetComponent<CounterRespowner>().killedMinions = 0;
                IsAfterSummoning = false;

                if ((WizardBattleStepsEnum)GetProggress() == WizardBattleStepsEnum.First)
                    BattleState = WizardBattleStepsEnum.Second;
                else if ((WizardBattleStepsEnum)GetProggress() == WizardBattleStepsEnum.Second)
                    BattleState = WizardBattleStepsEnum.Third;
                else if ((WizardBattleStepsEnum)GetProggress() == WizardBattleStepsEnum.Third)
                    BattleState = WizardBattleStepsEnum.Last;

                approaching = true; // flag whivh tell to go in player direction
                canTakeDamage = true;
                gameObject.GetComponent<NPC>().State = NPCStateEnum.Walking;

                SaveProggress(BattleState);
            }

        }
    }

    public void OnTriggerEnter2D(Collider2D collision)
    {
        // after reaching teleport position remove component
        if (collision.gameObject.tag == "SceneTransition")
        {
            collision.gameObject.GetComponent<DoorBehaviour>().isEnabled = true;

            SaveProggress(WizardBattleStepsEnum.First);

            Destroy(gameObject);
        }

        // Hit logic
        if (collision.gameObject.tag == "AttackHitbox" || collision.gameObject.tag == "PickaxeHitbox")
        {
            hit = true;
        }
    }


    public void OnTriggerExit2D(Collider2D collision)
    {
        // Hit logic
        if (collision.gameObject.tag == "AttackHitbox" || collision.gameObject.tag == "PickaxeHitbox")
        {
            timerDmg = 0f;

            hit = false;
        }
    }

    // script controlling battle scenario by listening on change, depending on:
    // - Wizard life
    // - Wizard state
    // - battle state
    public void ManageBehaviourScenario()
    {
        // at first 
        if (BattleState == WizardBattleStepsEnum.First &&
            gameObject.GetComponent<NPC>().State == NPCStateEnum.None)
        {
            Debug.Log("First");
            approaching = true;

            gameObject.GetComponent<NPC>().State = NPCStateEnum.Walking; // HandleState make rest - makes sure the wizard walks up to the player and cxhange state to attacking
        } else 



        // detect health status after each taked damage from player (invoked in TakeDamage)
        if ((BattleState  == WizardBattleStepsEnum.First || BattleState == WizardBattleStepsEnum.Second || BattleState == WizardBattleStepsEnum.Third) &&
            (gameObject.GetComponent<NPC>().State & NPCStateEnum.Attacking) > 0)
        {
            if (IsAfterSummoning == true)
                return;


        } else



        // 1 Change state to pending & block damage taking & go to safe position 

        if (BattleState == WizardBattleStepsEnum.Escaping) 
        {
            gameObject.GetComponent<AStarPathfindingAgent>().speed = 5f;

            canTakeDamage = false;

            approaching = false;


            if (currentHealth <= maxHealth * 0.10f)  // summon before 
            {
                Debug.Log("Wizard HP critical");
                TargetPosition = Points[Points.Count() - 1].position;
                SummonManagment();

            }
            else
            {
                TargetPosition = Points[CurrentPoint].position;

                CurrentPoint++;
            }

            gameObject.GetComponent<NPC>().State = NPCStateEnum.Walking; // go to newxt base point
        } else

        if (BattleState == WizardBattleStepsEnum.Summoning)
        {
            if(!IsAfterSummoning)
            {
                speedCalculate();
                SummonManagment();
            }
        }
    }

    public void HandleState()
    {
        switch (gameObject.GetComponent<NPC>().State)
        {
            case NPCStateEnum.Walking:  // to player, to next point, to exit xd
                {
                    WalkingManagment();
                    break;
                }
            case NPCStateEnum.Attacking:
                {
                    gameObject.GetComponent<Animator>().SetBool("isRunning", false);

                    AttackManagment();
                    break;
                }
            case NPCStateEnum.Pending:
                {
                    gameObject.GetComponent<Animator>().SetBool("isRunning", false);

                    BattleState = WizardBattleStepsEnum.Summoning;

                    ManageBehaviourScenario();
                    break;
                }
            default:
                {
                    break;
                }
        }
    }

    public void WalkingManagment()
    {
        StopAllCoroutines();

        if(approaching)
        {
            // when we can go closer to player - we go closer and when we are close enought we start attacking
            if (!IsInAttackRadious())
            {
                gameObject.GetComponent<AStarPathfindingAgent>().FindPath();
                StartCoroutine(gameObject.GetComponent<AStarPathfindingAgent>().FollowPath());
            }
            else
            {
                timeToWaitBeforeNextAttack = 0; // to allowa to first hit withot waiting

                // in this script we set attacking mode 
                gameObject.GetComponent<NPC>().State = NPCStateEnum.Attacking;

                gameObject.GetComponent<AStarPathfindingAgent>().path.Clear(); // if we are able to talgk we dont want go go further player
            }
        }
        else
        {
            // we summon minions and go to next save point, else we escape
            // IMPOSRANT make summoning and walking independent and unbinded in code

            // first focus on targetting next step from list
            //Debug.Log(Vector2.Distance(transform.position, TargetPosition));
            if (Vector2.Distance(transform.position, TargetPosition) > 0.95f) // count value - path finding stop moving them before reachin position well..
            {
                gameObject.GetComponent<AStarPathfindingAgent>().point = TargetPosition;

                gameObject.GetComponent<AStarPathfindingAgent>().FindPoint();

                StartCoroutine(gameObject.GetComponent<AStarPathfindingAgent>().FollowPath());
            }
            else
            {
                // ------ anim.SetBool("isRunning", false);
               
                gameObject.GetComponent<Animator>().SetBool("isRunning", false);
               
                // set next point for future 
                gameObject.GetComponent<NPC>().State = NPCStateEnum.Pending;  // decide what next

            }
        }

    }

    public void SummonManagment()
    {

        // 2. Spown minions (depending on iteration increase spowned amount

        BlockRespowners();

        MinionRespowner.GetComponent<CounterRespowner>().Counter = SummonedMinionsCounter;
        MinionRespowner.GetComponent<CounterRespowner>().Respown = true;

        IsAfterSummoning = true;
        Debug.Log("After summoning");
    }

    #region damage managment
    /// <summary>
    /// Take damage only once in a time and only when IT IS ALLOWED
    /// </summary>
    public void TakingDamageManagment()
    {
        // COPIED FROM Following Enemy scripts
        // Taking hit logic
        timerHit += Time.deltaTime;
        if (hit == true && canTakeDamage)
        {
            if (timerHit >= timeToWaitBeforeNextHitFromPlayer)
            {

                TakeDamage(PlayerPrefs.GetFloat("attackValue"));
                hit = false;
                timerHit = 0f;
                TakeKnockback();
            }
        }
    }

    private void TakeDamage(float damage)
    {
        var healthBeforeDamage = currentHealth;

        damage = damage - defence;
        damage = damage < 0 ? 0 : damage;
        Debug.Log("Gamage from player: " + damage + "(defence: " + defence + ")");
        currentHealth -=  damage;

        /*        if (gameObject.GetComponent<Enemy>().health <= 0)
                {
                    gameObject.SetActive(false);
                    gameObject.GetComponent<Enemy>().isKilled = 1;
                    GameObject.FindGameObjectWithTag("Player").GetComponent<Player>().GetExp(30);

                    // pass info about killing assigned enemy to mission manager listener
                    // pass enemy name from script NOT object name (thats allow to have many different objects variantsa with this same aggregate key (private name - not preffab name) )
                    ConditionManager.Instance.UpdateKillCondition(gameObject.GetComponent<Enemy>().MinionName);
                }*/

        if (
            (healthBeforeDamage > maxHealth * 0.75f && currentHealth <= maxHealth * 0.75f) ||
            (healthBeforeDamage > maxHealth * 0.50f && currentHealth <= maxHealth * 0.50f) ||
            (healthBeforeDamage > maxHealth * 0.25f && currentHealth <= maxHealth * 0.25f) ||
            (healthBeforeDamage > maxHealth * 0.10f && currentHealth <= maxHealth * 0.10f)
        ){
            Debug.Log("escaping");
            BattleState = WizardBattleStepsEnum.Escaping;
            Debug.Log(BattleState);

            // re-calculate after each damage
            defecnceCalculate();
            speedCalculate();

            ManageBehaviourScenario();
        }
    }

    private void TakeKnockback()
    {
        Rigidbody2D enemy = gameObject.GetComponent<Rigidbody2D>();
        Rigidbody2D player = GameObject.FindGameObjectWithTag("Player").GetComponent<Rigidbody2D>();

        if (enemy != null)
        {
            enemy.isKinematic = false;
            Vector2 difference = enemy.transform.position - player.transform.position;
            difference = difference.normalized * 5; // thrust
            enemy.AddForce(difference, ForceMode2D.Impulse);
            //StartCoroutine(KnockCo(enemy));
        }
    }
    #endregion

    #region attack managment

    public void AttackManagment()
    {
        // if during Attacking mode player GO OUT of the attacking radious
        if (!IsInAttackRadious())
        {
            gameObject.GetComponent<NPC>().State = NPCStateEnum.Walking;
            return;
        }

        // Attack logic
        if (timerDmg >= timeToWaitBeforeNextAttack)
        {
            timerDmg = 0f;

            GameObject.FindGameObjectWithTag("Player").GetComponent<Player>().TakeDamage(
                attackValue,
                isPanelEnabled
            );
        }
        speedCalculate(); // to restore property timeToWaitBeforeNextAttack walue 

        timerDmg += Time.deltaTime;
    }

    public bool IsInAttackRadious()
    {
        if (Vector2.Distance(GameObject.FindGameObjectWithTag("Player").transform.position, transform.position) >= attackingRadius)
            return false;

        return true;
    }
    #endregion

    // jesli gracz ma "x" pkt siły 
    // Ze wzgledu na wyniki EXCEL'a zakładajac że
    // - gracz ma 4/5 pkt sily (skile + bonus naszyjnika)
    // obrona nie moze przekroczyc 3 - 3.2f
    public void defecnceCalculate()
    {
        if (currentHealth > maxHealth * 0.75f)
        {
            defence = 1.5f;
        }else if(currentHealth <= maxHealth * 0.75f &&  currentHealth > maxHealth * 0.50f)
        {
            defence = 2.5f;
        }
        else if (currentHealth <= maxHealth * 0.50f && currentHealth > maxHealth * 0.25f)
        {
            defence = 3.15f;
        } else if (currentHealth <= maxHealth * 0.25f && currentHealth > maxHealth * 0.10f)
        {
            defence = 4f; // or higher ???
        }
    }

    public void speedCalculate()
    {
        if (currentHealth > maxHealth * 0.75f)
        {
            gameObject.GetComponent<AStarPathfindingAgent>().speed = 1f;
            timeToWaitBeforeNextAttack = 1f;
        }
        else if (currentHealth <= maxHealth * 0.75f && currentHealth > maxHealth * 0.50f)
        {
            gameObject.GetComponent<AStarPathfindingAgent>().speed = 1.15f;
            timeToWaitBeforeNextAttack = 0.8f;
        }
        else if (currentHealth <= maxHealth * 0.50f && currentHealth > maxHealth * 0.25f)
        {
            gameObject.GetComponent<AStarPathfindingAgent>().speed = 1.30f;
            timeToWaitBeforeNextAttack = 0.7f;

        }
        else if (currentHealth <= maxHealth * 0.25f && currentHealth > maxHealth * 0.10f)
        {
            gameObject.GetComponent<AStarPathfindingAgent>().speed = 1.45f;
            timeToWaitBeforeNextAttack = 0.6f;
        }
    }

    #region Respown minions managments
    private void BlockRespowners()
    {
        ResetRespowners();

        if (SummonedMinionsCounter == 3) // use only 1
        {
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(1).GetComponent<MinionRespowner>().Blocked = true;
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(2).GetComponent<MinionRespowner>().Blocked = true;
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(3).GetComponent<MinionRespowner>().Blocked = true;
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(4).GetComponent<MinionRespowner>().Blocked = true;
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(5).GetComponent<MinionRespowner>().Blocked = true;
        }
        if (SummonedMinionsCounter == 6) // use 2 & 3
        {
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(0).GetComponent<MinionRespowner>().Blocked = true;
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(3).GetComponent<MinionRespowner>().Blocked = true;
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(4).GetComponent<MinionRespowner>().Blocked = true;
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(5).GetComponent<MinionRespowner>().Blocked = true;
        }
        if (SummonedMinionsCounter == 12) // use 2 & 4 & 5
        {
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(0).GetComponent<MinionRespowner>().Blocked = true;
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(1).GetComponent<MinionRespowner>().Blocked = true;
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(5).GetComponent<MinionRespowner>().Blocked = true;
        }
        if (SummonedMinionsCounter > 12) // use 4 & 5 & 6
        {
            SummonedMinionsCounter = 15;

            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(0).GetComponent<MinionRespowner>().Blocked = true;
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(1).GetComponent<MinionRespowner>().Blocked = true;
            MinionRespowner.GetComponent<CounterRespowner>().respownPoints.ElementAtOrDefault(2).GetComponent<MinionRespowner>().Blocked = true;
        }
    }

    private void ResetRespowners()
    {
        foreach(var respownPointObject in MinionRespowner.GetComponent<CounterRespowner>().respownPoints)
        {
            respownPointObject.GetComponent<MinionRespowner>().Blocked = false;
        }
    }
    #endregion

    #region proggress API
    public void SaveProggress(WizardBattleStepsEnum status)
    {
        Debug.Log("Save: " + status);

        PlayerPrefs.SetInt(SceneManager.GetActiveScene().name + "." + BATTLE_STATE, (int)status);
    }

    public WizardBattleStepsEnum GetProggress()
    {
        return (WizardBattleStepsEnum)PlayerPrefs.GetInt(SceneManager.GetActiveScene().name + "." + BATTLE_STATE);
    }

    public bool HasProggress()
    {
        return PlayerPrefs.HasKey(SceneManager.GetActiveScene().name + "." + BATTLE_STATE);
    }
    #endregion
}