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

namespace Kryz.CharacterStats.Examples
{
    public class PlayerCharacterStats : CharacterStats
    {
        [Header("Listeners:")]
        [SerializeField] private GameEventListener_Float experienceOnNPCDeath;
        [SerializeField] private GameEventListener_JobInstance onJobCompleted;
        [Header("Components:")]
        [SerializeField] Inventory inventory;
        [SerializeField] EquipmentPanel equipmentPanel;
        [SerializeField] StatPanel statPanel;

        [HideInInspector]
        public RiftPlayer player;

        public Level level;

        PhotonView photonView;

        Health health;
        Mana mana;

        [Header("Runtime Data:")]
        public List<int> AllocatedStatPoints = new List<int>();

        public int AvailablePointsToAllocate;

        CharacterData characterData = new CharacterData();

        List<CharacterStat> statsCollection = new List<CharacterStat>();

        protected override void Awake()
        {
            player = GetComponentInParent<RiftPlayer>();
            photonView = GetComponentInParent<PhotonView>();
            health = player.GetComponent<Health>();
            mana = player.GetComponent<Mana>();

            level = new Level();

            base.Awake();

            statsCollection.Add(Strength);
            statsCollection.Add(Agility);
            statsCollection.Add(Intelligence);
            statsCollection.Add(Spirit);
            statsCollection.Add(Vitality);

            if (!photonView.IsMine) return;

            characterData = PlayerDataHandler.Instance.LoadCharacterData(PlayerDataHandler.Instance.currentPlayerName.Value, PlayerDataHandler.Instance.currentCharacterName.Value);

            if (characterData != null)
            {
                Debug.Log("Success Loading CharacterData");
                level = new Level(characterData.currentLevel, characterData.currentExperience);

                for (int i = 0; i < statsDictionary.Keys.Count; i++)
                {
                    AllocatedStatPoints.Add(characterData.allocatedStatPoints[i]);
                }
                AvailablePointsToAllocate = characterData.availablePointsToAllocate;

                UpdateAllocatedStats();
                UpdateStatsBasedOnLevel();
            }
            else
            {
                characterData = new CharacterData();

                for (int i = 0; i < statsDictionary.Keys.Count; i++)
                {
                    AllocatedStatPoints.Add(0);
                }
                AvailablePointsToAllocate = GameConstants.CharacterBalancing.StatPointsPerLevel;
            }

            characterData.playerOwnerID = PlayerDataHandler.Instance.currentPlayerName.Value;



            if (statPanel == null)
                statPanel = FindObjectOfType<StatPanel>();

            statPanel.SetPlayerStats(this);
            statPanel.SetStats(Strength, Agility, Intelligence, Spirit, Vitality);
            statPanel.UpdateStatValues();
            statPanel.SetCharacterInfo(PlayerDataHandler.Instance.currentCharacterName.Value, level.currentLevel.ToString());
            statPanel.ToggleAllocateButtonsInteractable(AvailablePointsToAllocate > 0);
            onUpdateStatValues.AddListener(statPanel.UpdateStatValues);
            onUpdateStatValues.Invoke();

            if (inventory == null)
                inventory = FindObjectOfType<Inventory>();

            inventory.OnItemRightClickedEvent.AddListener(EquipFromInventory);

            if (equipmentPanel == null)
                equipmentPanel = FindObjectOfType<EquipmentPanel>();

            equipmentPanel.OnItemRightClickedEvent.AddListener(UnequipFromEquipPanel);

            RefreshCurrentEquipmentStats();

            level.OnLevelUpEvent.AddListener(OnLevelUp);
            level.OnExperienceChanged.AddListener(OnExperienceChanged);
            experienceOnNPCDeath.Response.AddListener(level.GainExperience);
            onJobCompleted.Response.AddListener(job => level.GainExperience(job.experienceReward));

            UpdateLevelOnOthers();

            statPanel.UpdateStatValues();
        }
        private void Start()
        {
            if (photonView.IsMine)
                onUpdateStatValues.Invoke();
        }

        private void OnExperienceChanged()
        {
            characterData.currentLevel = level.currentLevel;
            characterData.currentExperience = level.GetCurrentExperience();

            PlayerDataHandler.Instance.SaveCharacterData(PlayerDataHandler.Instance.currentPlayerName.Value, PlayerDataHandler.Instance.currentCharacterName.Value, characterData);
        }

        private void OnLevelUp()
        {
            UpdateLevelOnOthers();

            statPanel.UpdateLevelInfo(level.currentLevel.ToString());

            UpdateStatsBasedOnLevel();

            AvailablePointsToAllocate += GameConstants.CharacterBalancing.StatPointsPerLevel;

            characterData.availablePointsToAllocate = AvailablePointsToAllocate;

            statPanel.ToggleAllocateButtonsInteractable(AvailablePointsToAllocate > 0);

            health.ChangeValue(health.GetMaxValue() * 0.2f);
            mana.ChangeValue(mana.GetMaxValue() * 0.2f);
        }


        private void EquipFromInventory(Item item)
        {
            if (item is EquippableItem)
            {
                Equip((EquippableItem)item);
            }
        }

        private void UnequipFromEquipPanel(Item item)
        {
            if (item is EquippableItem)
            {
                Unequip((EquippableItem)item);
            }
        }

        public void Equip(EquippableItem item)
        {
            if (inventory.RemoveItem(item))
            {
                ItemTooltip.Instance.HideTooltip();

                EquippableItem previousItem;
                if (equipmentPanel.AddItem(item, out previousItem))
                {
                    if (previousItem != null)
                    {
                        inventory.AddItem(previousItem);
                        previousItem.Unequip(this);
                        statPanel.UpdateStatValues();
                    }
                    item.Equip(this);
                    statPanel.UpdateStatValues();
                    onUpdateStatValues.Invoke();
                }
                else
                {
                    inventory.AddItem(item);
                }
            }
        }

        public void Unequip(EquippableItem item)
        {
            if (!inventory.IsFull() && equipmentPanel.RemoveItem(item))
            {
                item.Unequip(this);
                statPanel.UpdateStatValues();
                inventory.AddItem(item);
                onUpdateStatValues.Invoke();
            }
        }

        public void UpdateLevelOnOthers()
        {
            if (photonView.IsMine)
                photonView.RPC(nameof(RPC_OnLevelUp), RpcTarget.OthersBuffered, this.level.currentLevel);
        }

        [PunRPC]
        public void RPC_OnLevelUp(int level)
        {
            this.level.currentLevel = level;
            this.level.OnLevelUpEvent.Invoke();
        }

        public void UpdateAllocatedStats()
        {
            Strength.RemoveAllModifiersFromSource(GameConstants.ObjectSources.AllocatedSource);
            Agility.RemoveAllModifiersFromSource(GameConstants.ObjectSources.AllocatedSource);
            Intelligence.RemoveAllModifiersFromSource(GameConstants.ObjectSources.AllocatedSource);
            Spirit.RemoveAllModifiersFromSource(GameConstants.ObjectSources.AllocatedSource);
            Vitality.RemoveAllModifiersFromSource(GameConstants.ObjectSources.AllocatedSource);

            Strength.AddModifier(new StatModifier(AllocatedStatPoints[0], StatModType.Flat, GameConstants.ObjectSources.AllocatedSource));
            Agility.AddModifier(new StatModifier(AllocatedStatPoints[1], StatModType.Flat, GameConstants.ObjectSources.AllocatedSource));
            Intelligence.AddModifier(new StatModifier(AllocatedStatPoints[2], StatModType.Flat, GameConstants.ObjectSources.AllocatedSource));
            Spirit.AddModifier(new StatModifier(AllocatedStatPoints[3], StatModType.Flat, GameConstants.ObjectSources.AllocatedSource));
            Vitality.AddModifier(new StatModifier(AllocatedStatPoints[4], StatModType.Flat, GameConstants.ObjectSources.AllocatedSource));


            characterData.availablePointsToAllocate = AvailablePointsToAllocate;
            for (int i = 0; i < AllocatedStatPoints.Count; i++)
            {
                characterData.allocatedStatPoints[i] = AllocatedStatPoints[i];
            }

            PlayerDataHandler.Instance.SaveCharacterData(PlayerDataHandler.Instance.currentPlayerName.Value, PlayerDataHandler.Instance.currentCharacterName.Value, characterData);

            onUpdateStatValues.Invoke();
        }

        public void UpdateStatsBasedOnLevel()
        {
            Strength.RemoveAllModifiersFromSource(GameConstants.ObjectSources.LevelSource);
            Agility.RemoveAllModifiersFromSource(GameConstants.ObjectSources.LevelSource);
            Intelligence.RemoveAllModifiersFromSource(GameConstants.ObjectSources.LevelSource);
            Spirit.RemoveAllModifiersFromSource(GameConstants.ObjectSources.LevelSource);
            Vitality.RemoveAllModifiersFromSource(GameConstants.ObjectSources.LevelSource);

            Strength.AddModifier(new StatModifier((level.currentLevel - 1), StatModType.Flat, GameConstants.ObjectSources.LevelSource));
            Agility.AddModifier(new StatModifier((level.currentLevel - 1), StatModType.Flat, GameConstants.ObjectSources.LevelSource));
            Intelligence.AddModifier(new StatModifier((level.currentLevel - 1), StatModType.Flat, GameConstants.ObjectSources.LevelSource));
            Spirit.AddModifier(new StatModifier((level.currentLevel - 1), StatModType.Flat, GameConstants.ObjectSources.LevelSource));
            Vitality.AddModifier(new StatModifier((level.currentLevel - 1), StatModType.Flat, GameConstants.ObjectSources.LevelSource));

            onUpdateStatValues.Invoke();
        }

        EquipmentSlot[] slots;
        public void RefreshCurrentEquipmentStats()
        {
            slots = equipmentPanel.GetCurrentSlots();
            for (int i = 0; i < slots.Length; i++)
            {
                if (slots[i].Item == null) continue;

                for (int j = 0; j < statsCollection.Count; j++)
                {
                    if (statsCollection[j].HasModifiersFromSource(slots[i].Item))
                    {
                        break;
                    }
                }
                ((EquippableItem)slots[i].Item).Equip(this);
            }
        }
    }
}
