using System.Collections.Generic;
using System.Linq;
using UnityEngine;
using UnityEngine.UIElements;
using VOID.Generic.Dict;
using VOID.Generic.Objects.Unit;
using VOID.Generic.Objects.Unit.Events;
using VOID.Generic.Player;
using VOID.Generic.Player.Input;
using VOID.UserInterface.Game.Interfaces;
using VOID.UserInterface.Game.Util;
using VOID.ScriptableObjects.Abilities;

namespace VOID.UserInterface.Game
{
    public class AbilityBookUI : MonoBehaviour, IHasWindow
    {
        public static AbilityBookUI current { get; private set; }
        
        // WindowUI
        public WindowUI windowUI { get; private set; }
        
        // Active Unit and his Backpack
        private UnitObject _unit;
        private UnitObjectAbilityBook _abilityBook;
        
        // Visual Elements
        private VisualElement _rootElement;
        private VisualElement _abilityListElement;
        private readonly List<SlotUI> _abilitySlots = new();
        
        // References
        private ShortcutsInputManager _shortcutsInputManager;
        private PlayerController _playerController;

        private void Awake()
        {
            current = this;

            var uiDocument = gameObject.GetComponent<UIDocument>();
            var templateElement = uiDocument.rootVisualElement;
            
            // Find all important elements
            _rootElement = templateElement.Q("root-ability-book"); 
            _abilityListElement = templateElement.Q("ability-slots"); 
            _abilityListElement.Clear();
            windowUI = gameObject.GetComponent<WindowUI>();

            _rootElement.dataSource = this;
            _rootElement.pickingMode = PickingMode.Position;
        }

        private void Start()
        {
            _shortcutsInputManager = ShortcutsInputManager.current;
            _playerController = PlayerController.current;
            
            // Toggle show/hide when pressing shortcut for ability book
            _shortcutsInputManager.abilityBook.performed += _ => windowUI.Toggle();
            windowUI.OnOpen += OnOpen;
            windowUI.OnClose += OnClose;
            
            windowUI.SetTitle("Ability Book");

            // Hidden by default
            windowUI.Close();
        }
        
        public void Toggle() => windowUI.Toggle();

        public void Open() => windowUI.Open();

        public void Close() => windowUI.Close();

        private void OnOpen()
        {
            // References to active unit 
            _unit = PlayerController.current.activeUnit;
            _abilityBook = _unit.unitAbilityBook;
            
            // Refresh UI when we change active unit
            _playerController.OnActiveUnitChange += OnActiveUnitChange;
            
            // Initialize all VisualElements
            _abilityBook.abilities.ForEach(AddSlot);

            // Listen to all changes in backpack and update UI 
            _unit.unitEvents.onAbilityAdded.AddListener(OnAbilityAdded);
            _unit.unitEvents.onAbilityRemoved.AddListener(OnAbilityRemoved);
        }

        private void OnClose()
        {
            // Stop listening 
            _playerController.OnActiveUnitChange -= OnActiveUnitChange;

            // Backpack closed, no need to listen for backpack changes
            if (_unit)
            {
                _unit.unitEvents.onAbilityAdded.RemoveListener(OnAbilityAdded);
                _unit.unitEvents.onAbilityRemoved.RemoveListener(OnAbilityRemoved);
            }

            // Remove all VisualElement slots
            _abilitySlots.ForEach(slot => slot.Destroy());

            // Clear variables
            _abilitySlots.Clear();
            _unit = null;
            _abilityBook = null;
        }

        private void OnActiveUnitChange(UnitObject unitObject)
        {
            windowUI.Close();
            windowUI.Open();
        }

        private void OnAbilityAdded(AbilityBookAbilityAddedEvent abilityAddedEvent) => AddSlot(abilityAddedEvent.abilityAdded);
        private void AddSlot(BasicAbility ability)
        {
            var newSlot = new SlotUI();
            newSlot.InitNew(_abilityListElement, SlotType.AbilityBookSlot, -1);
            newSlot.Set(ability);
            _abilitySlots.Add(newSlot);
        }

        private void OnAbilityRemoved(AbilityBookAbilityRemovedEvent abilityRemovedEvent) => RemoveSlot(abilityRemovedEvent.abilityRemoved);
        private void RemoveSlot(BasicAbility ability)
        {
            var oldSlot = _abilitySlots.FirstOrDefault(slot => slot.ability == ability);
            oldSlot?.Destroy();
            _abilitySlots.Remove(oldSlot);
        }
    }
}
