using System;
using System.Collections.Generic;
using AlicizaX;
using AlicizaX.UI.Extension;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.EventSystems;
using UnityEngine.Serialization;
using UnityEngine.UI;
using AudioType = AlicizaX.Audio.Runtime.AudioType;

[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 AnimationTriggers();
}


internal enum SelectionState
{
    Normal,

    Highlighted,

    Pressed,

    Selected,
    Disabled,
}

[DisallowMultipleComponent]
public class UXButton : UIBehaviour, IButton,
    IPointerDownHandler,
    IPointerUpHandler,
    IPointerEnterHandler,
    IPointerExitHandler,
    IPointerClickHandler
{
    [SerializeField] private bool m_Interactable = true;

    [SerializeField] private ButtonModeType m_Mode;

    [SerializeField] private Button.ButtonClickedEvent m_OnClick = new Button.ButtonClickedEvent();

    [SerializeField] private TransitionData m_TransitionData = new TransitionData();

    [SerializeField] private List<TransitionData> m_ChildTransitions = new List<TransitionData>();
    [SerializeField] private UXGroup m_UXGroup;

    [SerializeField] private AudioClip hoverAudioClip;
    [SerializeField] private AudioClip clickAudioClip;

    [SerializeField] private SelectionState m_SelectionState = SelectionState.Normal;
    private bool m_DownAndExistUI;
    private bool m_IsDown;
    private bool m_IsTogSelected;

    private Animator _animator;

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

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

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

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

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

    protected override void Awake()
    {
        base.Awake();
        if (m_Mode == ButtonModeType.Toggle)
        {
            onValueChanged?.Invoke(IsSelected);
        }

        UpdateVisualState(m_SelectionState, true);
    }


    void IPointerDownHandler.OnPointerDown(PointerEventData eventData)
    {
        if (!m_Interactable) return;
        if (eventData.button != PointerEventData.InputButton.Left) return;
        m_IsDown = true;
        m_SelectionState = SelectionState.Pressed;
        UpdateVisualState(m_SelectionState, false);
    }

    void IPointerUpHandler.OnPointerUp(PointerEventData eventData)
    {
        if (!m_Interactable) return;

        m_IsDown = false;

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

    void IPointerEnterHandler.OnPointerEnter(PointerEventData eventData)
    {
        if (!m_Interactable || CantTouch()) return;

        m_SelectionState = SelectionState.Highlighted;
        m_DownAndExistUI = false;
        if (m_IsDown) return;
        UpdateVisualState(m_SelectionState, false);
        PlayButtonSound(hoverAudioClip);
    }

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

        if (CantTouch())
        {
            return;
        }

        m_SelectionState = SelectionState.Normal;

        UpdateVisualState(m_SelectionState, false);
    }

    private bool CantTouch()
    {
        return m_Mode == ButtonModeType.Toggle && m_IsTogSelected;
    }

    public void SetSelect(bool state, bool boardEvent = false)
    {
        if (m_Mode != ButtonModeType.Toggle) return;
        m_IsTogSelected = state;
        if (boardEvent) onValueChanged?.Invoke(m_IsTogSelected);
        m_SelectionState = m_IsTogSelected ? SelectionState.Selected : SelectionState.Normal;
        UpdateVisualState(m_SelectionState, false);
    }

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

            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.targetGraphic == null) return;

        Color tintColor;
        Sprite transitionSprite;
        string triggerName;
        switch (state)
        {
            case SelectionState.Normal:
                tintColor = transition.colors.normalColor;
                transitionSprite = null;
                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:
                tintColor = Color.black;
                transitionSprite = null;
                triggerName = string.Empty;
                break;
        }

        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(transition.animationTriggers, triggerName);
                break;
        }
    }

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

    protected void DoSpriteSwap(TransitionData transitionData, Sprite newSprite)
    {
        if (transitionData.targetGraphic is Image image)
        {
            image.overrideSprite = newSprite;
        }
        else if (transitionData.targetGraphic != null)
        {
            Log.Error($"Target Graphic must be Image for SpriteSwap. Object: {transitionData.targetGraphic.name}");
        }
    }

    void TriggerAnimation(AnimationTriggers animationTriggers, string triggername)
    {
        if (animator == null || !animator.isActiveAndEnabled || !animator.hasBoundPlayables || string.IsNullOrEmpty(triggername))
            return;

        animator.ResetTrigger(animationTriggers.normalTrigger);
        animator.ResetTrigger(animationTriggers.highlightedTrigger);
        animator.ResetTrigger(animationTriggers.pressedTrigger);
        animator.ResetTrigger(animationTriggers.selectedTrigger);
        animator.ResetTrigger(animationTriggers.disabledTrigger);

        animator.SetTrigger(triggername);
    }

    protected void PlayButtonSound(AudioClip clip)
    {
        GameApp.Audio?.Play(AudioType.UISound, clip, false, GameApp.Audio.UISoundVolume);
    }


    public void OnPointerClick(PointerEventData eventData)
    {
        if (eventData.button != PointerEventData.InputButton.Left)
            return;
        PlayButtonSound(clickAudioClip);
        ProcessClick();
    }
}
