using Photon.Pun;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class BossController : NPCController
{
    [Header("Boss Related:")]
    [SerializeField] protected GameEvent onBossDead;
    [SerializeField] protected float bossRangedAttacksRange;
    [SerializeField] protected float bossMeleeAttacksRange;

    protected BossAbilityBinder bossAbilityBinder;

    protected bool lockedInPhase = false;

    protected float targetDistance = float.MaxValue;
    protected float distance;
    protected Taggable resultTarget;

    protected float endSearchTime;

    protected Collider[] hits;

    protected override void Awake()
    {
        base.Awake();
        bossAbilityBinder = GetComponent<BossAbilityBinder>();
    }

    [PunRPC]
    protected override void RPC_OnDeath(bool lootDropped)
    {
        if (isDead) return;

        onBossDead.Raise();

        base.RPC_OnDeath(lootDropped);
    }

    protected override void Start()
    {
        base.Start();

        if (!photonView.IsMine) return;

        CheckSurroundings();

        LockInTarget(CheckClosestTarget());
    }

    protected override void Update()
    {
        if (!photonView.IsMine) return;
        if (isDead) return;

        if (PrimaryMovementBehaviourCheck())
        {
            PerformPrimaryMovementBehaviour();
        }
        else if (agent.remainingDistance <= agent.stoppingDistance)
            agent.isStopped = true;

        counter += Time.deltaTime;
        if (counter >= timeBetweenAttacks)
        {
            if (HighestPriorityCheck())
            {
                PerformHighestPriorityAction();
            }
            else if (SecondHighestPriorityCheck())
            {
                PerformSecondHighestPriorityAction();
            }
            else if (ThirdHighestPriorityCheck())
            {
                PerformThirdHighestPriorityAction();
            }
            else
            {
                agent.isStopped = false;
            }
        }
        else
        {
            agent.isStopped = false;
        }
    }

    protected virtual bool PrimaryMovementBehaviourCheck()
    {
        return Vector3.Distance(agent.transform.position, currentTarget.transform.position) > (bossRangedAttacksRange * 0.8f);
    }

    protected virtual bool HighestPriorityCheck()
    {
        return bossAbilityBinder.CanCastUltimate();
    }
    protected virtual bool SecondHighestPriorityCheck()
    {
        return (bossAbilityBinder.CanCastCoreAbility() && agent.remainingDistance <= bossRangedAttacksRange);
    }
    protected virtual bool ThirdHighestPriorityCheck()
    {
        return agent.remainingDistance <= bossMeleeAttacksRange;
    }


    protected virtual void PerformPrimaryMovementBehaviour()
    {
        patrolDestination = currentTarget.transform.position;
        patrolDestination.y = 0f;
        agent.SetDestination(patrolDestination);
        agent.isStopped = false;
    }

    protected virtual void PerformHighestPriorityAction()
    {
        PerformUltimate();
    }
    protected virtual void PerformSecondHighestPriorityAction()
    {
        PerformCoreAbility();
    }
    protected virtual void PerformThirdHighestPriorityAction()
    {
        PerformMeleeAbility();
    }

    protected virtual void PerformUltimate()
    {
        Debug.Log("INSIDE CAN CAST ULTIMATE: " + bossAbilityBinder.CanCastUltimate());
        ResetValuesOnAbilityPerformed();
        bossAbilityBinder.UseUltimateAbility(currentTarget.transform);
    }

    protected virtual void PerformCoreAbility()
    {
        ResetValuesOnAbilityPerformed();
        bossAbilityBinder.UseCoreAbility(currentTarget.transform.position);
    }

    protected virtual void PerformMeleeAbility()
    {
        ResetValuesOnAbilityPerformed();
        bossAbilityBinder.UseMeleeAbility();
    }

    protected virtual void ResetValuesOnAbilityPerformed()
    {
        agent.isStopped = true;
        counter = 0f;
    }

    protected virtual Taggable CheckClosestTarget()
    {
        targetDistance = float.MaxValue;
        Debug.Log("CLOSEST COUNT: " + possibleTargets.Count);
        for (int i = 0; i < possibleTargets.Count; i++)
        {
            distance = Vector3.Distance(possibleTargets[i].transform.position, agent.transform.position);
            if (distance < targetDistance)
            {
                targetDistance = distance;
                resultTarget = possibleTargets[i];
            }
        }

        return resultTarget;
    }

    protected virtual Taggable CheckFurthestAwayTarget()
    {
        targetDistance = 0f;

        for (int i = 0; i < possibleTargets.Count; i++)
        {
            distance = Vector3.Distance(possibleTargets[i].transform.position, agent.transform.position);
            if (distance > targetDistance)
            {
                targetDistance = distance;
                resultTarget = possibleTargets[i];
            }
        }

        return resultTarget;
    }

    protected virtual IEnumerator TargetLockedInPhaseDurationCoroutine()
    {
        yield return new WaitForSeconds(GameConstants.GameBalancing.BossTargetLockInPhaseDuration);

        SearchForNewTargetToLockIn();
    }

    protected virtual IEnumerator SearchForNewTargetToLockInCoroutine()
    {
        endSearchTime = Time.time + GameConstants.GameBalancing.BossSearchForNewTargetLockInDuration;

        while (Time.time < endSearchTime)
        {
            UpdateCurrentTarget(possibleTargets[Random.Range(0, possibleTargets.Count)]);
            yield return new WaitForSeconds(GameConstants.GameBalancing.BossSearchForNewTargetLockInDuration / 3);
        }

        LockInTarget(CheckClosestTarget());
    }

    protected virtual void LockInTarget(Taggable target)
    {
        lockedInPhase = true;

        UpdateCurrentTarget(target);

        Debug.Log("Target Locked In: " + target.name);
        StartCoroutine(TargetLockedInPhaseDurationCoroutine());
    }

    protected virtual void SearchForNewTargetToLockIn()
    {
        lockedInPhase = false;

        Debug.Log("Searching for new lockin: ");
        StartCoroutine(SearchForNewTargetToLockInCoroutine());
    }

    protected virtual void UpdateCurrentTarget(Taggable target)
    {
        currentTarget = target;
        SetupAgentStats();
    }

    protected virtual void SetupAgentStats()
    {
        agent.speed = chasingAgentSpeed;
        patrolDestination = currentTarget.transform.position;
        patrolDestination.y = 0f;
        agent.SetDestination(patrolDestination);
    }

    protected virtual void CheckSurroundings()
    {
        if (!photonView.IsMine) return;

        hits = Physics.OverlapSphere(this.transform.position, sightRange);
        Debug.Log("CHECK SURROUNDINGS COUNT: " + hits.Length);
        foreach (Collider collider in hits)
        {
            Debug.Log("CHECKING COLLIDER: " + collider.name);
            otherView = collider.GetComponentInParent<PhotonView>();
            if (otherView != null)
            {
                if (otherView == photonView) continue;
            }

            possibleTarget = collider.GetComponentInParent<Taggable>();

            if (possibleTarget == null) continue;

            if (possibleTarget.targetTag == myTag.targetTag || myTag.targetTag.AlliedTags.Contains(possibleTarget.targetTag)) continue;

            if (possibleTargets.Contains(possibleTarget)) continue;

            Debug.Log("ADDING TARGET " + possibleTarget.name);
            possibleTargets.Add(possibleTarget);
        }
    }
}
