using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
using TMPro;
using Newtonsoft.Json;
using CommanderAILab.Models;
using CommanderAILab.Services;
namespace CommanderAILab.UI
{
///
/// Collection scene controller — card grid with search, filter, sort,
/// card detail panel, async image loading, pagination, and mobile support.
///
public class CollectionController : MonoBehaviour
{
// ── Header ──────────────────────────────────────────────────────────
[Header("Navigation")]
[SerializeField] private Button backButton;
[Header("Search & Sort")]
[SerializeField] private TMP_InputField searchInput;
[SerializeField] private TMP_Dropdown sortDropdown;
[SerializeField] private Button searchButton;
[SerializeField] private Button filterToggleButton; // mobile: open/close drawer
[Header("Filter Sidebar")]
[SerializeField] private GameObject filterPanel; // root – toggled on mobile
[SerializeField] private TMP_Dropdown colorFilterDropdown;
[SerializeField] private TMP_Dropdown typeFilterDropdown;
[SerializeField] private TMP_Dropdown setFilterDropdown;
[SerializeField] private TMP_Dropdown rarityFilterDropdown;
[SerializeField] private TMP_Dropdown ownedFilterDropdown; // All / Owned / Missing
[SerializeField] private Button applyFilterButton;
[SerializeField] private Button clearFilterButton;
[Header("Card Grid")]
[SerializeField] private Transform cardGridParent; // ScrollRect content
[SerializeField] private GameObject cardPrefab; // CardPrefab instance
[SerializeField] private ScrollRect gridScrollRect;
[Header("Pagination")]
[SerializeField] private Button loadMoreButton;
[SerializeField] private TMP_Text pageInfoText;
[SerializeField] private GameObject loadingSpinner;
[Header("Card Detail Panel")]
[SerializeField] private GameObject detailPanel;
[SerializeField] private Image detailCardFront;
[SerializeField] private Image detailCardBack; // placeholder / back face
[SerializeField] private TMP_Text detailCardName;
[SerializeField] private TMP_Text detailManaCost;
[SerializeField] private TMP_Text detailType;
[SerializeField] private TMP_Text detailRarity;
[SerializeField] private TMP_Text detailSet;
[SerializeField] private TMP_Text detailOracleText;
[SerializeField] private TMP_Text detailQuantity;
[SerializeField] private Button detailAddToDeckButton;
[SerializeField] private Button detailCloseButton;
[Header("Error")]
[SerializeField] private GameObject errorPanel;
[SerializeField] private TMP_Text errorText;
[SerializeField] private Button retryButton;
// ── State ────────────────────────────────────────────────────────────
private List _cards = new();
private CardModel _selectedCard;
private int _currentPage = 1;
private int _totalCards = 0;
private const int PageSize = 40;
private bool _isLoading = false;
private bool _filterOpen = false;
// Current filter / sort state
private string _searchQuery = "";
private string _colorFilter = "";
private string _typeFilter = "";
private string _setFilter = "";
private string _rarityFilter = "";
private string _ownedFilter = ""; // "all" | "owned" | "missing"
private string _sortField = "name";
// Pooled card tile GameObjects
private readonly List _cardTiles = new();
// ── Lifecycle ────────────────────────────────────────────────────────
private void Start()
{
// Back button → MainMenu
backButton.onClick.AddListener(() => SceneManager.LoadScene("MainMenu"));
// Search
searchButton.onClick.AddListener(OnSearchClicked);
searchInput.onSubmit.AddListener(_ => OnSearchClicked());
// Sort
sortDropdown.onValueChanged.AddListener(_ => RefreshFromPage1());
// Filter actions
applyFilterButton.onClick.AddListener(RefreshFromPage1);
clearFilterButton.onClick.AddListener(OnClearFilters);
filterToggleButton.onClick.AddListener(ToggleFilterPanel);
// Pagination
loadMoreButton.onClick.AddListener(OnLoadMore);
loadMoreButton.gameObject.SetActive(false);
// Detail panel
detailCloseButton.onClick.AddListener(CloseDetailPanel);
detailAddToDeckButton.onClick.AddListener(OnAddToDeck);
detailPanel.SetActive(false);
// Error retry
retryButton.onClick.AddListener(RefreshFromPage1);
errorPanel.SetActive(false);
// On mobile start with filter panel hidden
_filterOpen = Screen.width > 900;
filterPanel.SetActive(_filterOpen);
// Initial load
RefreshFromPage1();
}
// ── Search / Filter / Sort ───────────────────────────────────────────
private void OnSearchClicked()
{
_searchQuery = searchInput.text.Trim();
RefreshFromPage1();
}
private void OnClearFilters()
{
colorFilterDropdown.value = 0;
typeFilterDropdown.value = 0;
setFilterDropdown.value = 0;
rarityFilterDropdown.value = 0;
ownedFilterDropdown.value = 0;
searchInput.text = "";
_searchQuery = "";
RefreshFromPage1();
}
private void ToggleFilterPanel()
{
_filterOpen = !_filterOpen;
filterPanel.SetActive(_filterOpen);
}
// ── Data Loading ─────────────────────────────────────────────────────
private void RefreshFromPage1()
{
_currentPage = 1;
_cards.Clear();
ClearCardTiles();
LoadPage(_currentPage);
}
private void OnLoadMore()
{
_currentPage++;
LoadPage(_currentPage);
}
private void LoadPage(int page)
{
if (_isLoading) return;
_isLoading = true;
loadingSpinner.SetActive(true);
loadMoreButton.interactable = false;
errorPanel.SetActive(false);
// Collect filter values from dropdowns
_colorFilter = DropdownValue(colorFilterDropdown);
_typeFilter = DropdownValue(typeFilterDropdown);
_setFilter = DropdownValue(setFilterDropdown);
_rarityFilter = DropdownValue(rarityFilterDropdown);
_ownedFilter = DropdownValue(ownedFilterDropdown);
_sortField = SortFieldFromDropdown(sortDropdown.value);
string path = BuildQueryPath(page);
StartCoroutine(ApiClient.Instance.GetRaw(path,
json => OnPageLoaded(json, page),
errMsg => OnLoadError(errMsg)));
}
private string BuildQueryPath(int page)
{
var sb = new StringBuilder("/api/collection?");
sb.Append($"page={page}&per_page={PageSize}");
if (!string.IsNullOrEmpty(_searchQuery))
sb.Append($"&search={UnityWebRequest.EscapeURL(_searchQuery)}");
if (!string.IsNullOrEmpty(_colorFilter))
sb.Append($"&colors={UnityWebRequest.EscapeURL(_colorFilter)}");
if (!string.IsNullOrEmpty(_typeFilter))
sb.Append($"&type={UnityWebRequest.EscapeURL(_typeFilter)}");
if (!string.IsNullOrEmpty(_setFilter))
sb.Append($"&set={UnityWebRequest.EscapeURL(_setFilter)}");
if (!string.IsNullOrEmpty(_rarityFilter))
sb.Append($"&rarity={UnityWebRequest.EscapeURL(_rarityFilter)}");
if (!string.IsNullOrEmpty(_ownedFilter) && _ownedFilter != "all")
sb.Append($"&owned={_ownedFilter}");
if (!string.IsNullOrEmpty(_sortField))
sb.Append($"&sort={_sortField}");
return sb.ToString();
}
private void OnPageLoaded(string json, int page)
{
_isLoading = false;
loadingSpinner.SetActive(false);
CollectionResponse response;
try { response = JsonConvert.DeserializeObject(json); }
catch (Exception e)
{
OnLoadError($"Parse error: {e.Message}");
return;
}
_totalCards = response.total;
_cards.AddRange(response.cards);
UpdatePageInfo();
SpawnCardTiles(response.cards);
bool hasMore = _cards.Count < _totalCards;
loadMoreButton.gameObject.SetActive(hasMore);
loadMoreButton.interactable = true;
}
private void OnLoadError(string msg)
{
_isLoading = false;
loadingSpinner.SetActive(false);
errorText.text = $"Failed to load collection:\n{msg}";
errorPanel.SetActive(true);
Debug.LogWarning($"[CollectionController] {msg}");
}
// ── Card Tiles ───────────────────────────────────────────────────────
private void SpawnCardTiles(List cards)
{
foreach (var card in cards)
{
var tile = Instantiate(cardPrefab, cardGridParent);
_cardTiles.Add(tile);
// Set name label (assumes CardPrefab has a TMP child named "CardName")
var nameLabel = tile.GetComponentInChildren();
if (nameLabel != null) nameLabel.text = card.name;
// Async image load
var img = tile.GetComponentInChildren();
if (img != null && !string.IsNullOrEmpty(card.imageUrl))
{
ImageCache.Instance.GetSprite(card.imageUrl, sprite =>
{
if (img != null) img.sprite = sprite;
});
}
// Click → open detail panel
var btn = tile.GetComponentInChildren