using System;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;

namespace Gyvr.Mythril2D
{
    [Serializable]
    public class PetSummonAbilityDataBlock : PassiveAbilityBaseDataBlock
    {
        public CharacterBaseDataBlock petData;
    }

    public class PetSummonAbility : PassiveAbility<PetSummonAbilitySheet>
    {
        private CharacterBase m_pet;
        private HashSet<CharacterBase> m_summons = new();
        private float m_respawnTimerRemaining;

        protected override Type GetDataBlockType() => typeof(PetSummonAbilityDataBlock);

        public override void Init(CharacterBase character, AbilitySheet settings)
        {
            base.Init(character, settings);
            m_respawnTimerRemaining = passiveAbilitySheet.respawnTimer;
        }

        public override void Destroy()
        {
            base.Destroy();
            if (m_pet != null && !m_pet.dead)
            {
                m_pet.Kill();
            }
        }

        private void MakeSpaceIfNecessary()
        {
            if (m_summons.Count >= passiveAbilitySheet.maxSummonCount)
            {
                CharacterBase summon = m_summons.Last();
                m_summons.Remove(summon);

                if (summon != null && !summon.dead)
                {
                    summon.Kill();
                }
            }
        }

        public void Summon(CharacterBaseDataBlock data = null)
        {
            MakeSpaceIfNecessary();

            Vector2 direction = m_character.GetTargetDirection();

            Persistable persistable = GameManager.PersistenceSystem.InstantiateCustom(
                passiveAbilitySheet.toSummon,
                Vector3.zero,
                Quaternion.identity,
                m_character.transform,
                data != null && data.info is IIdentifiablePersistentDataHandler identifiablePersistentDataHandler
                    ? identifiablePersistentDataHandler.GetIdentifier()
                    : null
            );

            persistable.transform.parent = null;
            persistable.transform.position = m_character.transform.position + new Vector3(direction.x, direction.y, 0.0f);

            Pet character = persistable as Pet;
            Debug.Assert(character != null, "PetSummonAbility: toSummon must be a Pet prefab with a Pet component.");

            m_pet = character;

            character.destroyedEvent.AddListener(() => m_summons.Remove(character));
            if (data != null)
            {
                character.LoadDataBlock(data);
            }

            m_summons.Add(character);
            character.FlagAsSummoned();
            character.SetAlignmentOverride(EAlignment.Neutral);

            Debug.Assert(character.controller is AIController, "The summoned character must have an AIController to follow the summoner.");
            AIController aiController = (AIController)character.controller;
            aiController.SetMaster(m_character, passiveAbilitySheet.followDistance);
        }

        private void Update()
        {
            if (m_pet == null || m_pet.dead)
            {
                m_respawnTimerRemaining -= Time.deltaTime;
                if (m_respawnTimerRemaining <= 0f)
                {
                    Summon();
                    m_respawnTimerRemaining = passiveAbilitySheet.respawnTimer;
                }
            }
        }

        private void LateUpdate()
        {
            if (m_pet && !m_pet.dead && m_character)
            {
                float dist = Vector2.Distance(m_pet.transform.position, m_character.transform.position);
                if (dist > passiveAbilitySheet.teleportDistance)
                {
                    m_pet.transform.position = m_character.transform.position;
                }
            }
        }

        protected override void OnSave(PersistableDataBlock block)
        {
            base.OnSave(block);
            block.As<PetSummonAbilityDataBlock>().petData = m_pet?.CreateDataBlock().As<CharacterBaseDataBlock>();
        }

        protected override void OnLoad(PersistableDataBlock block)
        {
            base.OnLoad(block);
            var petData = block.As<PetSummonAbilityDataBlock>().petData;
            Summon(petData);
        }
    }
}
