using System;
using System.Collections;
using System.Collections.Generic;
using AlicizaX;
using AlicizaX.UI.Extension;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI;

[Serializable]
public enum ButtonModeType
{
    Normal,
    Toggle
}

[System.Serializable]
public class TransitionData
{
    public Graphic targetGraphic;
    public Selectable.Transition transition = Selectable.Transition.ColorTint;
    public ColorBlock colors;
    public SpriteState spriteState;
    public AnimationTriggers animationTriggers = new();
}

internal enum SelectionState
{
    Normal,
    Highlighted,
    Pressed,
    Selected,
    Disabled,
}

[ExecuteInEditMode]
[DisallowMultipleComponent]
public class UXButton : UIBehaviour, IButton,
    IPointerDownHandler, IPointerUpHandler, IPointerEnterHandler,
    IPointerExitHandler, IPointerClickHandler, ISubmitHandler
{
    #region Serialized Fields

    [SerializeField] private bool m_Interactable = true;
    [SerializeField] private ButtonModeType m_Mode;
    [SerializeField] private Button.ButtonClickedEvent m_OnClick = new();
    [SerializeField] private TransitionData m_TransitionData = new();
    [SerializeField] private List<TransitionData> m_ChildTransitions = new();
    [SerializeField] private UXGroup m_UXGroup;
    [SerializeField] private AudioClip hoverAudioClip;
    [SerializeField] private AudioClip clickAudioClip;
#if INPUTSYSTEM_SUPPORT
    [SerializeField] internal UnityEngine.InputSystem.InputActionReference _hotKeyRefrence;
    [SerializeField] internal EHotkeyPressType _hotkeyPressType;

    internal void OnHotkeyTriggered()
    {
        if (m_Interactable)
        {
            OnSubmit(null);
        }
    }

#endif

    #endregion

    #region Private Variables

    [SerializeField] private SelectionState m_SelectionState = SelectionState.Normal;
    private bool m_DownAndExistUI;
    private bool m_IsDown;
    private bool m_IsTogSelected;
    private Animator _animator;
    private WaitForSeconds _waitTimeFadeDuration;
    private Coroutine _resetRoutine;

    private readonly Dictionary<string, int> _animTriggerIDs = new();
    private readonly Dictionary<string, int> _animResetTriggerIDs = new();

    #endregion

    #region Properties

    internal bool Interactable
    {
        get => m_Interactable;
    }

    internal Animator animator
    {
        get
        {
            if (!_animator)
                _animator = GetComponent<Animator>();
            return _animator;
        }
    }

    public bool IsSelected
    {
        get => m_IsTogSelected;
        internal set
        {
            if (m_IsTogSelected == value) return;
            m_IsTogSelected = value;
            onValueChanged?.Invoke(m_IsTogSelected);
            m_SelectionState = value ? SelectionState.Selected : SelectionState.Normal;
            UpdateVisualState(m_SelectionState, false);
        }
    }

    public Button.ButtonClickedEvent onClick
    {
        get => m_OnClick;
        set => m_OnClick = value;
    }

    [SerializeField] private UnityEvent<bool> m_OnValueChanged = new();

    public UnityEvent<bool> onValueChanged
    {
        get => m_OnValueChanged;
        set => m_OnValueChanged = value;
    }

    #endregion

    #region Unity Lifecycle

    protected override void Awake()
    {
        base.Awake();
        Initlize();
    }
#if UNITY_EDITOR
    protected override void OnValidate()
    {
        base.OnValidate();
        if (!Application.isPlaying)
        {
            Initlize();
        }
    }


#endif // if UNITY_EDITOR

    protected void Initlize()
    {
        _waitTimeFadeDuration = new WaitForSeconds(
            Mathf.Max(0.01f, m_TransitionData.colors.fadeDuration));
        _animTriggerIDs.Clear();
        _animResetTriggerIDs.Clear();
        var triggers = m_TransitionData.animationTriggers;
        AddTriggerID(triggers.normalTrigger);
        AddTriggerID(triggers.highlightedTrigger);
        AddTriggerID(triggers.pressedTrigger);
        AddTriggerID(triggers.selectedTrigger);
        AddTriggerID(triggers.disabledTrigger);


        UpdateVisualState(m_SelectionState, true);
    }

    protected override void OnDestroy()
    {
        if (_resetRoutine != null)
        {
            StopCoroutine(_resetRoutine);
            _resetRoutine = null;
        }

        base.OnDestroy();
    }

    #endregion

    #region Event Handlers

    void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
    {
        if (!ShouldProcessEvent(eventData)) return;

        m_IsDown = true;
        m_SelectionState = SelectionState.Pressed;
        UpdateVisualState(m_SelectionState, false);
    }

    void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
    {
        if (!m_Interactable || eventData.button != PointerEventData.InputButton.Left)
            return;

        m_IsDown = false;
        if (m_IsTogSelected)
        {
            m_SelectionState = SelectionState.Selected;
        }
        else
        {
            m_SelectionState = m_DownAndExistUI ? SelectionState.Normal : SelectionState.Highlighted;
        }

        UpdateVisualState(m_SelectionState, false);
    }

    void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData)
    {
        if (!ShouldProcessEvent(eventData)) return;

        m_DownAndExistUI = false;
        if (m_IsDown) return;

        m_SelectionState = SelectionState.Highlighted;
        UpdateVisualState(m_SelectionState, false);
        PlayButtonSound(hoverAudioClip);
    }

    void IPointerExitHandler.OnPointerExit(PointerEventData eventData)
    {
        if (!m_Interactable) return;
        if (m_IsDown)
        {
            m_DownAndExistUI = true;
            return;
        }

        m_SelectionState = IsSelected ? SelectionState.Selected : SelectionState.Normal;
        UpdateVisualState(m_SelectionState, false);
    }

    void IPointerClickHandler.OnPointerClick(PointerEventData eventData)
    {
        if (eventData.button != PointerEventData.InputButton.Left || !m_Interactable)
            return;

        PlayButtonSound(clickAudioClip);
        ProcessClick();
    }


    public void OnSubmit(BaseEventData eventData)
    {
        UpdateVisualState(SelectionState.Pressed, false);
        ProcessClick();

        if (_resetRoutine != null)
            StopCoroutine(OnFinishSubmit());

        _resetRoutine = StartCoroutine(OnFinishSubmit());
    }

    #endregion

    #region Public Methods

    public bool Selected
    {
        get => IsSelected;
        set
        {
            if (m_Mode == ButtonModeType.Toggle)
            {
                ProcessClick();
            }
        }
    }

    #endregion

    #region Private Methods

    private bool ShouldProcessEvent(PointerEventData eventData)
    {
        return m_Interactable &&
               eventData.button == PointerEventData.InputButton.Left &&
               !(m_Mode == ButtonModeType.Toggle && IsSelected);
    }

    private void ProcessClick()
    {
        if (m_Mode == ButtonModeType.Normal)
        {
            UISystemProfilerApi.AddMarker("Button.onClick", this);
            m_OnClick?.Invoke();
        }
        else if (m_UXGroup)
        {
            m_UXGroup.NotifyButtonClicked(this);
        }
        else
        {
            IsSelected = !IsSelected;
        }
    }

    private void UpdateVisualState(SelectionState state, bool instant)
    {
        ProcessTransitionData(m_TransitionData, state, instant);
        foreach (var transition in m_ChildTransitions)
        {
            ProcessTransitionData(transition, state, instant);
        }
    }

    private void ProcessTransitionData(TransitionData transition, SelectionState state, bool instant)
    {
        if ((transition.transition == Selectable.Transition.ColorTint || transition.transition == Selectable.Transition.SpriteSwap) && transition.targetGraphic == null) return;

        Color tintColor;
        Sprite transitionSprite;
        string triggerName;

        switch (state)
        {
            case SelectionState.Normal:
                tintColor = transition.colors.normalColor;
                transitionSprite = transition.spriteState.highlightedSprite;
                triggerName = transition.animationTriggers.normalTrigger;
                break;
            case SelectionState.Highlighted:
                tintColor = transition.colors.highlightedColor;
                transitionSprite = transition.spriteState.highlightedSprite;
                triggerName = transition.animationTriggers.highlightedTrigger;
                break;
            case SelectionState.Pressed:
                tintColor = transition.colors.pressedColor;
                transitionSprite = transition.spriteState.pressedSprite;
                triggerName = transition.animationTriggers.pressedTrigger;
                break;
            case SelectionState.Selected:
                tintColor = transition.colors.selectedColor;
                transitionSprite = transition.spriteState.selectedSprite;
                triggerName = transition.animationTriggers.selectedTrigger;
                break;
            case SelectionState.Disabled:
                tintColor = transition.colors.disabledColor;
                transitionSprite = transition.spriteState.disabledSprite;
                triggerName = transition.animationTriggers.disabledTrigger;
                break;
            default:
                return;
        }

        switch (transition.transition)
        {
            case Selectable.Transition.ColorTint:
                StartColorTween(transition, tintColor * transition.colors.colorMultiplier, instant);
                break;
            case Selectable.Transition.SpriteSwap:
                DoSpriteSwap(transition, transitionSprite);
                break;
            case Selectable.Transition.Animation:
                TriggerAnimation(triggerName);
                break;
        }
    }

    private void StartColorTween(TransitionData data, Color targetColor, bool instant)
    {
        if (Application.isPlaying)
        {
            data.targetGraphic.CrossFadeColor(
                targetColor,
                instant ? 0f : data.colors.fadeDuration,
                true,
                true
            );
        }
        else
        {
            data.targetGraphic.canvasRenderer.SetColor(targetColor);
        }
    }

    private void DoSpriteSwap(TransitionData data, Sprite newSprite)
    {
        if (data.targetGraphic is Image image)
        {
            image.overrideSprite = newSprite;
        }
    }

    private void TriggerAnimation(string trigger)
    {
        if (animator == null ||
            !animator.isActiveAndEnabled ||
            !animator.hasBoundPlayables ||
            string.IsNullOrEmpty(trigger))
            return;

        foreach (var resetTrigger in _animResetTriggerIDs.Keys)
        {
            animator.ResetTrigger(_animTriggerIDs[resetTrigger]);
        }

        if (_animTriggerIDs.TryGetValue(trigger, out int id))
        {
            animator.SetTrigger(id);
        }
    }

    private void AddTriggerID(string triggerName)
    {
        if (!string.IsNullOrEmpty(triggerName))
        {
            int id = Animator.StringToHash(triggerName);
            if (!_animTriggerIDs.ContainsKey(triggerName))
            {
                _animTriggerIDs.Add(triggerName, id);
                _animResetTriggerIDs.Add(triggerName, id);
            }
        }
    }

    private void PlayButtonSound(AudioClip clip)
    {
        if (clip == null || UXComponentExtensionsHelper.AudioHelper == null) return;
        UXComponentExtensionsHelper.AudioHelper.PlayAudio(clip);
    }

    private IEnumerator OnFinishSubmit()
    {
        yield return _waitTimeFadeDuration;
        UpdateVisualState(
            m_IsTogSelected ? SelectionState.Selected : SelectionState.Normal,
            false
        );
        _resetRoutine = null;
    }

    #endregion
}
