using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UIElements;

/// <summary>
///     class to handles overall game ui state and transitions
/// </summary>
public class UIManager : MonoBehaviour
{
    /// <summary>
    ///     enum for available menus in the game, for use with <c>ShowMenu()</c>
    /// </summary>
    public enum DisplayState
    {
        UnassociatedState,
        DefaultView,
        GameView,
        ResultsView,
        LeaderboardView,
        AccountView
    }

    /// <summary>
    ///     singleton pattern: define instance field for accessing the singleton elsewhere
    /// </summary>
    public static UIManager Instance;

    /// <summary>
    ///     the current display state of the game
    /// </summary>
    [SerializeField] private DisplayState state = DisplayState.UnassociatedState;

    /// <summary>
    ///     list of callbacks to invoke when the display state changes
    /// </summary>
    private readonly List<Action<DisplayState, DisplayState>> _onDisplayStateChangeCallbacks = new();

    /// <summary>
    ///     the visual element object for game ui
    /// </summary>
    public VisualElement UI;

    /// <summary>
    ///     enforces singleton behaviour; sets doesn't destroy on load and checks for multiple instances
    /// </summary>
    private void Awake()
    {
        // check if instance hasn't been set yet
        if (Instance == null)
        {
            Debug.Log("awake as singleton instance, setting self as the forever-alive instance");
            Instance = this;
            DontDestroyOnLoad(gameObject);
        }
        // check if instance is already set and it's not this instance
        else if (Instance != null && Instance != this)
        {
            Debug.Log("awake as non-singleton instance, destroying self");
            Destroy(gameObject);
        }
    }

    /// <summary>
    ///     modify state of initial variables
    /// </summary>
    private void Start()
    {
        SetDisplayState(DisplayState.DefaultView);
    }

    private void Update()
    {
        var gameplay = GameManager.Instance.Gameplay;
        if (gameplay == null) return;
        if (gameplay.Round >= 1) gameplay.Update();
    }

    /// <summary>
    ///     initialise variables and register callbacks
    /// </summary>
    private void OnEnable()
    {
        UI = GetComponent<UIDocument>().rootVisualElement;
    }

    /// <summary>
    ///     function to register a callback to be invoked when the display state changes
    /// </summary>
    /// <param name="callback">
    ///     callback function that takes two <c>DisplayState</c> parameters:
    /// </param>
    public void RegisterOnDisplayStateChangeCallback(Action<DisplayState, DisplayState> callback)
    {
        _onDisplayStateChangeCallbacks.Add(callback);
    }
    
    private void FireOnDisplayStateChange(DisplayState oldState, DisplayState newState)
    {
        foreach (var callback in _onDisplayStateChangeCallbacks)
            try
            {
                callback.Invoke(oldState, newState);
            }
            catch (Exception e)
            {
                Debug.LogError($"error invoking OnSignOutCallback: {e.Message}");
            }
    }

    /// <summary>
    ///     function to show a menu based on the enum passed,
    ///     and any other necessary actions
    /// </summary>
    /// <param name="newDisplayState">the game menu to show</param>
    public void SetDisplayState(DisplayState newDisplayState)
    {
        // if the new state is the same as the current state, do nothing
        if (state == newDisplayState)
        {
            Debug.Log($"staying at {state} (illogical transition)");
            return;
        }

        Debug.Log($"switching from {state} to {newDisplayState}");

        var defaultView = UI.Q<VisualElement>("DefaultView");
        var gameView = UI.Q<VisualElement>("GameView");
        var resultsView = UI.Q<VisualElement>("ResultsView");
        var leaderboardView = UI.Q<VisualElement>("LeaderboardView");
        var accountView = UI.Q<VisualElement>("AccountView");

        defaultView.style.display = newDisplayState switch
        {
            DisplayState.DefaultView => DisplayStyle.Flex,
            _ => DisplayStyle.None
        };

        gameView.style.display = newDisplayState switch
        {
            DisplayState.GameView => DisplayStyle.Flex,
            _ => DisplayStyle.None
        };

        resultsView.style.display = newDisplayState switch
        {
            DisplayState.ResultsView => DisplayStyle.Flex,
            _ => DisplayStyle.None
        };

        leaderboardView.style.display = newDisplayState switch
        {
            DisplayState.LeaderboardView => DisplayStyle.Flex,
            _ => DisplayStyle.None
        };

        accountView.style.display = newDisplayState switch
        {
            DisplayState.AccountView => DisplayStyle.Flex,
            _ => DisplayStyle.None
        };

        if (newDisplayState == DisplayState.GameView)
        {
            // if we're in the game view, everything except the account section/panel thing
            UI.Q<Button>("PlayButton").style.display = DisplayStyle.None;
            UI.Q<Button>("LeaderboardButton").style.display = DisplayStyle.None;
            UI.Q<Button>("AccountButton").style.display = DisplayStyle.None;
            UI.Q<VisualElement>("AccountSection").style.display = DisplayStyle.Flex;
            UI.Q<VisualElement>("ConnectionStatus").style.display = DisplayStyle.None;

            // start the game
            GameManager.Instance.Gameplay.StartGame();
        }
        else
        {
            // if we're not in the game view, show everything!
            UI.Q<Button>("PlayButton").style.display = DisplayStyle.Flex;
            UI.Q<Button>("LeaderboardButton").style.display =
                GameManager.Instance.Backend.Status == Backend.FirebaseConnectionStatus.Connected
                    ? DisplayStyle.Flex
                    : DisplayStyle.None;
            UI.Q<Button>("AccountButton").style.display =
                GameManager.Instance.Backend.Status == Backend.FirebaseConnectionStatus.Connected
                    ? DisplayStyle.Flex
                    : DisplayStyle.None;
            UI.Q<VisualElement>("AccountSection").style.display = DisplayStyle.Flex;
            UI.Q<VisualElement>("ConnectionStatus").style.display = DisplayStyle.Flex;
        }
        
        FireOnDisplayStateChange(state, newDisplayState);
        state = newDisplayState;
    }
}