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

public class PlayerDeathManager : MonoBehaviour, IPunObservable
{
    [SerializeField] private GameEvent onLocalPlayerCheatedDeath;
    [SerializeField] private GameEvent onLocalPlayerFainted;
    [SerializeField] private GameEvent onLocalPlayerPermaDeath;
    [SerializeField] private GameEvent onLocalPlayerRevived;
    [Space]
    [SerializeField] private GameObject reviveProgressGO;
    [SerializeField] private GameObject bleedOutProgressGO;
    [SerializeField] private Image reviveProgressFill;
    [SerializeField] private Image bleedOutProgressFill;

    private bool hasUsedCheatDeath = false;
    public bool HasUsedCheatDeath => hasUsedCheatDeath;

    private bool isFainted = false;
    public bool IsFainted => isFainted;

    private float bleedOutTimer = 0f;
    public float BleedOutTimer => bleedOutTimer;

    private float reviveProgress = 0f;
    public float ReviveProgress => reviveProgress;

    PhotonView owner;
    RiftPlayer ownerPlayer;
    Health health;
    SphereCollider reviveTrigger;
    CharacterAnimatorController anim;

    RiftPlayer possibleReviver;
    public List<RiftPlayer> playersInsideReviveTrigger = new List<RiftPlayer>();

    private void Awake()
    {
        owner = GetComponentInParent<PhotonView>();
        ownerPlayer = GetComponentInParent<RiftPlayer>();
        health = GetComponentInParent<Health>();
        anim = ownerPlayer.GetComponentInChildren<CharacterAnimatorController>();

        reviveTrigger = GetComponentInChildren<SphereCollider>();
        reviveTrigger.transform.localScale = Vector3.one * GameConstants.CharacterBalancing.ReviveTriggerRadius * 2;
        reviveTrigger.isTrigger = true;
        reviveTrigger.gameObject.SetActive(false);

        reviveProgressGO.SetActive(false);
        bleedOutProgressGO.SetActive(false);
    }

    private void Start()
    {
        if (!owner.IsMine) return;

        health.onDeath.AddListener(OnDeath);
    }

    private void OnDeath()
    {
        if (PhotonNetwork.CurrentRoom.PlayerCount <= 1)
        {
            HandleSoloDeath();
        }
        else
        {
            HandleGroupDeath();
        }
    }


    private void HandleSoloDeath()
    {
        if (!hasUsedCheatDeath)
        {
            CheatDeath();
        }
        else
        {
            Die();
        }
    }

    private void HandleGroupDeath()
    {
        EnterFaintedState();
    }

    private void CheatDeath()
    {
        hasUsedCheatDeath = true;
        health.SetCurrentValue(health.GetMaxValue() * GameConstants.CharacterBalancing.SoloCheatDeathHealthPercent);
        StartCoroutine(SoloInvulnerability());

        if (!owner.IsMine) return;
        onLocalPlayerCheatedDeath.Raise();
    }

    private IEnumerator SoloInvulnerability()
    {
        // Implement invulnerability logic
        health.SetInvulnerabilityState(true);
        yield return new WaitForSeconds(GameConstants.CharacterBalancing.SoloCheatDeathInvulnerabilityDuration);
        // Remove invulnerability
        health.SetInvulnerabilityState(false);
    }

    private void EnterFaintedState()
    {
        isFainted = true;
        reviveTrigger.gameObject.SetActive(true);
        // Disable player movement and abilities
        // Change player visuals to "fainted" state
        health.SetIsDeadState(true);
        StartCoroutine(BleedOutTimerCoroutine());

        if (!owner.IsMine) return;

        reviveProgressGO.SetActive(true);
        bleedOutProgressGO.SetActive(true);

        anim.OnDeath();
        onLocalPlayerFainted.Raise();
    }

    private IEnumerator BleedOutTimerCoroutine()
    {
        bleedOutTimer = GameConstants.CharacterBalancing.GroupBleedOutDuration;

        bleedOutProgressFill.fillAmount = bleedOutTimer / GameConstants.CharacterBalancing.GroupBleedOutDuration;

        while (bleedOutTimer > 0 && isFainted)
        {
            bleedOutTimer -= Time.deltaTime;
            bleedOutProgressFill.fillAmount = bleedOutTimer / GameConstants.CharacterBalancing.GroupBleedOutDuration;
            yield return null;
        }

        if (isFainted)
        {
            Die();
        }
    }

    private void OnTriggerEnter(Collider other)
    {
        if (!isFainted) return;

        possibleReviver = other.GetComponentInParent<RiftPlayer>();
        if (possibleReviver == null) return;

        if (possibleReviver == ownerPlayer) return;

        if (playersInsideReviveTrigger.Contains(possibleReviver)) return;

        Debug.Log($"R: added {possibleReviver.name} as a reviver");
        playersInsideReviveTrigger.Add(possibleReviver);
    }

    private void Update()
    {
        if (!owner.IsMine)
        {
            reviveProgressGO.SetActive(reviveTrigger.gameObject.activeSelf);
            bleedOutProgressGO.SetActive(reviveTrigger.gameObject.activeSelf);

            reviveProgressFill.fillAmount = reviveProgress / GameConstants.CharacterBalancing.ReviveTime;
            bleedOutProgressFill.fillAmount = bleedOutTimer / GameConstants.CharacterBalancing.GroupBleedOutDuration;

            return;
        }

        if (!isFainted) return;

        if (playersInsideReviveTrigger.Count > 0)
        {
            reviveProgress += GameConstants.CharacterBalancing.ReviveSpeed * playersInsideReviveTrigger.Count * Time.deltaTime;
            reviveProgressFill.fillAmount = reviveProgress / GameConstants.CharacterBalancing.ReviveTime;
            if (reviveProgress >= GameConstants.CharacterBalancing.ReviveTime)
            {
                Revive();
            }
        }
    }

    private void OnTriggerExit(Collider other)
    {
        if (!isFainted) return;

        possibleReviver = other.GetComponentInParent<RiftPlayer>();
        if (possibleReviver == null) return;

        if (possibleReviver == ownerPlayer) return;

        if (!playersInsideReviveTrigger.Contains(possibleReviver)) return;

        Debug.Log($"R: removed {possibleReviver.name} from revivers pool");
        playersInsideReviveTrigger.Remove(possibleReviver);
    }

    private void Revive()
    {
        isFainted = false;
        reviveTrigger.gameObject.SetActive(false);
        reviveProgress = 0;

        health.SetIsDeadState(false);
        health.SetCurrentValue(health.GetMaxValue() * GameConstants.CharacterBalancing.ReviveHealthPercent);

        // Re-enable player movement and abilities
        // Restore normal player visuals
        playersInsideReviveTrigger.Clear();

        if (!owner.IsMine) return;

        reviveProgressGO.SetActive(false);
        bleedOutProgressGO.SetActive(false);

        anim.OnRevived();
        onLocalPlayerRevived.Raise();
    }

    private void Die()
    {
        health.SetIsDeadState(true);

        // Implement full death logic (respawn, etc.)
        if (PhotonNetwork.CurrentRoom.PlayerCount <= 1)
        {
            //Player perma death on the job, wait X to show info, then PhotonNetwork.LoadLevel(RiftHuntersInn)
            StartCoroutine(Return());
        }
        else
        {
            //if players alive > 0
            //wait motherfucker
            //else
            //all dead, host load level
        }

        if (!owner.IsMine) return;

        if(!isFainted)
            anim.OnDeath();

        reviveProgressGO.SetActive(false);
        bleedOutProgressGO.SetActive(false);

        onLocalPlayerPermaDeath.Raise();
    }

    IEnumerator Return()
    {
        yield return new WaitForSeconds(GameConstants.GameBalancing.PermaDeathInfoTime);


        if (PhotonNetwork.IsMasterClient) PhotonNetwork.LoadLevel("4-RiftHuntersInn");
    }

    public void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if(stream.IsWriting)
        {
            stream.SendNext(isFainted);
            stream.SendNext(reviveTrigger.gameObject.activeSelf);
            stream.SendNext(reviveProgress);
            stream.SendNext(bleedOutTimer);
        }
        else if(stream.IsReading)
        {
            isFainted = (bool)stream.ReceiveNext();
            reviveTrigger.gameObject.SetActive((bool)stream.ReceiveNext());
            reviveProgress = (float)stream.ReceiveNext();
            bleedOutTimer = (float)stream.ReceiveNext();
        }
    }
}
