using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Plugin.SRFPlay.Api;
using Jellyfin.Plugin.SRFPlay.Api.Models;
using Jellyfin.Plugin.SRFPlay.Services.Interfaces;
using Microsoft.Extensions.Logging;

namespace Jellyfin.Plugin.SRFPlay.Services;

/// <summary>
/// Service for managing topic/category data and filtering.
/// </summary>
public class CategoryService : ICategoryService
{
    private readonly ILogger<CategoryService> _logger;
    private readonly ISRFApiClientFactory _apiClientFactory;
    private readonly TimeSpan _topicsCacheDuration = TimeSpan.FromHours(24);

    // Cache topics per business unit. A single shared cache would return one unit's
    // topics (e.g. German SRF) even after the user switched to another (e.g. French RTS).
    private readonly Dictionary<string, (Dictionary<string, PlayV3Topic> Topics, DateTime Expiry)> _topicsCacheByUnit = new(StringComparer.OrdinalIgnoreCase);

    /// <summary>
    /// Initializes a new instance of the <see cref="CategoryService"/> class.
    /// </summary>
    /// <param name="logger">The logger.</param>
    /// <param name="apiClientFactory">The API client factory.</param>
    public CategoryService(ILogger<CategoryService> logger, ISRFApiClientFactory apiClientFactory)
    {
        _logger = logger;
        _apiClientFactory = apiClientFactory;
    }

    /// <inheritdoc />
    public async Task<List<PlayV3Topic>> GetTopicsAsync(string businessUnit, CancellationToken cancellationToken = default)
    {
        // Return cached topics for this business unit if still valid
        if (_topicsCacheByUnit.TryGetValue(businessUnit, out var cached) && DateTime.UtcNow < cached.Expiry)
        {
            _logger.LogDebug("Returning cached topics for business unit: {BusinessUnit}", businessUnit);
            return cached.Topics.Values.ToList();
        }

        _logger.LogInformation("Fetching topics for business unit: {BusinessUnit}", businessUnit);
        using var apiClient = _apiClientFactory.CreateClient();
        var topics = await apiClient.GetAllTopicsAsync(businessUnit, cancellationToken).ConfigureAwait(false);

        if (topics != null && topics.Count > 0)
        {
            // Cache topics by ID for quick lookups, scoped to this business unit
            var byId = topics
                .Where(t => !string.IsNullOrEmpty(t.Id))
                .ToDictionary(t => t.Id!, t => t);
            _topicsCacheByUnit[businessUnit] = (byId, DateTime.UtcNow.Add(_topicsCacheDuration));

            _logger.LogInformation("Cached {Count} topics for business unit: {BusinessUnit}", byId.Count, businessUnit);
        }

        return topics ?? new List<PlayV3Topic>();
    }

    /// <inheritdoc />
    public async Task<PlayV3Topic?> GetTopicByIdAsync(string topicId, string businessUnit, CancellationToken cancellationToken = default)
    {
        // Ensure topics are loaded for this business unit
        if (!_topicsCacheByUnit.TryGetValue(businessUnit, out var cached) || DateTime.UtcNow >= cached.Expiry)
        {
            await GetTopicsAsync(businessUnit, cancellationToken).ConfigureAwait(false);
            _topicsCacheByUnit.TryGetValue(businessUnit, out cached);
        }

        return cached.Topics?.GetValueOrDefault(topicId);
    }

    /// <inheritdoc />
    public async Task<List<PlayV3Show>> GetShowsByTopicAsync(
        string topicId,
        string businessUnit,
        int maxResults = 50,
        CancellationToken cancellationToken = default)
    {
        using var apiClient = _apiClientFactory.CreateClient();
        var allShows = await apiClient.GetAllShowsAsync(businessUnit, cancellationToken).ConfigureAwait(false);

        if (allShows == null || allShows.Count == 0)
        {
            _logger.LogWarning("No shows available for business unit: {BusinessUnit}", businessUnit);
            return new List<PlayV3Show>();
        }

        var filteredShows = FilterShowsByTopic(allShows, topicId)
            .Where(s => s.NumberOfEpisodes > 0)
            .OrderByDescending(s => s.NumberOfEpisodes)
            .Take(maxResults)
            .ToList();

        _logger.LogDebug("Found {Count} shows for topic {TopicId}", filteredShows.Count, topicId);
        return filteredShows;
    }

    /// <inheritdoc />
    public void ClearCache()
    {
        _topicsCacheByUnit.Clear();
        _logger.LogInformation("Topics cache cleared");
    }

    /// <summary>
    /// Filters shows by topic ID.
    /// </summary>
    /// <param name="shows">The shows to filter.</param>
    /// <param name="topicId">The topic ID to filter by.</param>
    /// <returns>Filtered list of shows.</returns>
    private static IReadOnlyList<PlayV3Show> FilterShowsByTopic(IReadOnlyList<PlayV3Show> shows, string topicId)
    {
        if (string.IsNullOrEmpty(topicId))
        {
            return shows;
        }

        return shows
            .Where(s => s.TopicList != null && s.TopicList.Contains(topicId))
            .ToList();
    }
}
