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

/// <summary>
///     class that loads leaderboard data and displays it in the UI
/// </summary>
public class LeaderboardUI : MonoBehaviour
{
    /// <summary>
    ///     maximum number of entries to display in the leaderboard
    /// </summary>
    public const int MaxEntries = 50;

    /// <summary>
    ///     uxml template for a leaderboard entry
    /// </summary>
    [SerializeField] private VisualTreeAsset leaderboardEntryTemplate;

    /// <summary>
    ///     leaderboard data
    /// </summary>
    private List<LeaderboardEntry> _leaderboardData = new(MaxEntries);

    /// <summary>
    ///     reference to the leaderboard scroll view
    /// </summary>
    private ScrollView _leaderboardScrollView;
    
    /// <summary>
    ///     register callbacks
    /// </summary>
    private void OnEnable()
    {
        UIManager.Instance.RegisterOnDisplayStateChangeCallback((_, newState) =>
        {
            if (newState == UIManager.DisplayState.LeaderboardView) LoadLeaderboardData();
        });
        
        _leaderboardScrollView = UIManager.Instance.UI.Q<ScrollView>("LeaderboardListContent");
        if (_leaderboardScrollView == null)
            throw new NullReferenceException("leaderboard scroll view not found in the UI");
    }

    /// <summary>
    ///     load leaderboard data from the backend
    /// </summary>
    private void LoadLeaderboardData()
    {
        if (GameManager.Instance.Backend.Status != Backend.FirebaseConnectionStatus.Connected)
        {
            Debug.LogError("attempted to load leaderboard data without a connection to the backend");
            RenderLeaderboardData("Not connected to the backend, can't load leaderboard data.");
            return;
        }

        GameManager.Instance.Backend.GetLeaderboard((result, entries) =>
        {
            if (result == Backend.TransactionResult.Ok)
            {
                _leaderboardData = entries;
                RenderLeaderboardData();
            }
            else
            {
                Debug.LogError("failed to load leaderboard data");
                RenderLeaderboardData("An error occured, couldn't load leaderboard data.");
            }
        });
    }

    /// <summary>
    ///     render leaderboard data in the UI
    /// </summary>
    /// <param name="message">message to display in the leaderboard in lieu of actual data</param>
    /// <exception cref="NullReferenceException">thrown when the leaderboard scroll view is missing or when the leaderboard entry</exception>
    private void RenderLeaderboardData(string message = "")
    {
        _leaderboardScrollView.Clear();

        if (!string.IsNullOrEmpty(message))
        {
            Debug.Log($"rendering leaderboard with message entry-in-lieu: '{message}'");
            _leaderboardScrollView.Add(BuildEntryElement("", message, ""));
            return;
        }

        Debug.Log($"rendering {_leaderboardData.Count} leaderboard entries");
        foreach (var (entry, index) in _leaderboardData.Take(MaxEntries).Reverse().Select((entry, index) => (entry, index)))
            _leaderboardScrollView.Add(BuildEntryElement((index + 1).ToString(), entry.Username, $"{entry.Rating:F3}"));
    }

    /// <summary>
    ///     build a leaderboard entry element
    /// </summary>
    /// <param name="position">leaderboard position as a string</param>
    /// <param name="username">score holder's username</param>
    /// <param name="rating">score holder's rating</param>
    /// <returns>a visual element representing a leaderboard entry</returns>
    /// <exception cref="NullReferenceException">thrown when the leaderboard entry template is missing required elements</exception>
    private TemplateContainer BuildEntryElement(string position, string username, string rating)
    {
        var template = leaderboardEntryTemplate.Instantiate();
        var templatePositionText = template.Q<Label>("EntryRankPosition");
        var templateUsernameText = template.Q<Label>("EntryNameText");
        var templateRatingText = template.Q<Label>("EntryRatingText");

        if (templatePositionText == null || templateUsernameText == null || templateRatingText == null)
            throw new NullReferenceException("leaderboard entry template is missing required elements");

        templatePositionText.text = position;
        templateUsernameText.text = username;
        templateRatingText.text = rating;

        return template;
    }
}

public readonly struct LeaderboardEntry
{
    public readonly string Username;
    public readonly float Rating;

    // public LeaderboardEntry(string username, float rating)
    // {
    //     Username = username;
    //     Rating = rating;
    // }
    //
    public LeaderboardEntry(Dictionary<string, object> data)
    {
        if (!data.ContainsKey("username") || data["username"] is not string username)
            throw new ArgumentException("data['username'] not found or invalid");
        
        Username = username;
        Rating = LocalPlayerData.GetFloatyKey(data, "rating");
    }
}