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 Microsoft.Extensions.Logging;

namespace Jellyfin.Plugin.SRFPlay.Services;

/// <summary>
/// Service for refreshing content from SRF API.
/// </summary>
public class ContentRefreshService
{
    private readonly ILogger<ContentRefreshService> _logger;
    private readonly ILoggerFactory _loggerFactory;
    private readonly MetadataCache _metadataCache;

    /// <summary>
    /// Initializes a new instance of the <see cref="ContentRefreshService"/> class.
    /// </summary>
    /// <param name="loggerFactory">The logger factory.</param>
    /// <param name="metadataCache">The metadata cache.</param>
    public ContentRefreshService(
        ILoggerFactory loggerFactory,
        MetadataCache metadataCache)
    {
        _loggerFactory = loggerFactory;
        _logger = loggerFactory.CreateLogger<ContentRefreshService>();
        _metadataCache = metadataCache;
    }

    /// <summary>
    /// Refreshes latest content from SRF API using Play v3.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <returns>List of URNs for new content.</returns>
    public async Task<List<string>> RefreshLatestContentAsync(CancellationToken cancellationToken)
    {
        var urns = new List<string>();

        try
        {
            var config = Plugin.Instance?.Configuration;
            if (config == null || !config.EnableLatestContent)
            {
                _logger.LogDebug("Latest content refresh is disabled");
                return urns;
            }

            _logger.LogInformation("Refreshing latest content for business unit: {BusinessUnit}", config.BusinessUnit);

            using var apiClient = new SRFApiClient(_loggerFactory);
            var businessUnit = config.BusinessUnit.ToString().ToLowerInvariant();

            // Get all shows from Play v3 API
            var shows = await apiClient.GetAllShowsAsync(businessUnit, cancellationToken).ConfigureAwait(false);

            if (shows == null || shows.Count == 0)
            {
                _logger.LogWarning("No shows found for business unit: {BusinessUnit}", config.BusinessUnit);
                return urns;
            }

            _logger.LogInformation("Found {Count} shows, fetching latest episodes from each", shows.Count);

            // Get latest episodes from each show (limit to 20 shows to avoid overwhelming)
            var showsToFetch = shows.Where(s => s.NumberOfEpisodes > 0)
                .OrderByDescending(s => s.NumberOfEpisodes)
                .Take(20)
                .ToList();

            foreach (var show in showsToFetch)
            {
                if (show.Id == null)
                {
                    continue;
                }

                try
                {
                    var videos = await apiClient.GetVideosForShowAsync(businessUnit, show.Id, cancellationToken).ConfigureAwait(false);
                    if (videos != null && videos.Count > 0)
                    {
                        _logger.LogDebug("Show {Show} ({ShowId}): Found {Count} videos", show.Title, show.Id, videos.Count);

                        // Filter to videos that are actually published (validFrom in the past)
                        var now = DateTime.UtcNow;
                        var publishedVideos = videos.Where(v =>
                            v.ValidFrom == null || v.ValidFrom.Value.ToUniversalTime() <= now).ToList();

                        _logger.LogDebug("Show {Show}: {PublishedCount} published out of {TotalCount} videos", show.Title, publishedVideos.Count, videos.Count);

                        if (publishedVideos.Count > 0)
                        {
                            // Take only the most recent published video from each show
                            var latestVideo = publishedVideos.OrderByDescending(v => v.Date).FirstOrDefault();
                            if (latestVideo?.Urn != null)
                            {
                                urns.Add(latestVideo.Urn);
                                _logger.LogInformation(
                                    "Added latest video from show {Show}: {Title} (URN: {Urn}, Date: {Date}, ValidFrom: {ValidFrom}, ValidTo: {ValidTo})",
                                    show.Title,
                                    latestVideo.Title,
                                    latestVideo.Urn,
                                    latestVideo.Date,
                                    latestVideo.ValidFrom,
                                    latestVideo.ValidTo);
                            }
                            else
                            {
                                _logger.LogWarning("Show {Show}: Latest video has null URN", show.Title);
                            }
                        }
                        else
                        {
                            _logger.LogDebug("Show {Show} has no published videos yet", show.Title);
                        }
                    }
                    else
                    {
                        _logger.LogDebug("Show {Show} ({ShowId}): No videos returned from API", show.Title, show.Id);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Error fetching videos for show {ShowId}", show.Id);
                }

                // Respect cancellation
                if (cancellationToken.IsCancellationRequested)
                {
                    break;
                }
            }

            _logger.LogInformation("Refreshed {Count} latest content items from {ShowCount} shows", urns.Count, showsToFetch.Count);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error refreshing latest content");
        }

        return urns;
    }

    /// <summary>
    /// Refreshes trending content from SRF API using Play v3.
    /// Gets videos from shows with the most episodes.
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <returns>List of URNs for trending content.</returns>
    public async Task<List<string>> RefreshTrendingContentAsync(CancellationToken cancellationToken)
    {
        var urns = new List<string>();

        try
        {
            var config = Plugin.Instance?.Configuration;
            if (config == null || !config.EnableTrendingContent)
            {
                _logger.LogDebug("Trending content refresh is disabled");
                return urns;
            }

            _logger.LogInformation("Refreshing trending content for business unit: {BusinessUnit}", config.BusinessUnit);

            using var apiClient = new SRFApiClient(_loggerFactory);
            var businessUnit = config.BusinessUnit.ToString().ToLowerInvariant();

            // Get all shows from Play v3 API
            var shows = await apiClient.GetAllShowsAsync(businessUnit, cancellationToken).ConfigureAwait(false);

            if (shows == null || shows.Count == 0)
            {
                _logger.LogWarning("No shows found for business unit: {BusinessUnit}", config.BusinessUnit);
                return urns;
            }

            _logger.LogInformation("Found {Count} shows, fetching popular content", shows.Count);

            // Get videos from popular shows (those with many episodes)
            var popularShows = shows.Where(s => s.NumberOfEpisodes > 10)
                .OrderByDescending(s => s.NumberOfEpisodes)
                .Take(15)
                .ToList();

            foreach (var show in popularShows)
            {
                if (show.Id == null)
                {
                    continue;
                }

                try
                {
                    var videos = await apiClient.GetVideosForShowAsync(businessUnit, show.Id, cancellationToken).ConfigureAwait(false);
                    if (videos != null && videos.Count > 0)
                    {
                        _logger.LogDebug("Show {Show} ({ShowId}): Found {Count} videos for trending", show.Title, show.Id, videos.Count);

                        // Filter to videos that are actually published (validFrom in the past)
                        var now = DateTime.UtcNow;
                        var publishedVideos = videos.Where(v =>
                            v.ValidFrom == null || v.ValidFrom.Value.ToUniversalTime() <= now).ToList();

                        _logger.LogDebug("Show {Show}: {PublishedCount} published out of {TotalCount} videos for trending", show.Title, publishedVideos.Count, videos.Count);

                        if (publishedVideos.Count > 0)
                        {
                            // Take 2 recent published videos from each popular show
                            var recentVideos = publishedVideos.OrderByDescending(v => v.Date).Take(2);
                            foreach (var video in recentVideos)
                            {
                                if (video.Urn != null)
                                {
                                    urns.Add(video.Urn);
                                    _logger.LogInformation(
                                        "Added trending video from show {Show}: {Title} (URN: {Urn}, Date: {Date}, ValidFrom: {ValidFrom}, ValidTo: {ValidTo})",
                                        show.Title,
                                        video.Title,
                                        video.Urn,
                                        video.Date,
                                        video.ValidFrom,
                                        video.ValidTo);
                                }
                                else
                                {
                                    _logger.LogWarning("Show {Show}: Trending video has null URN - {Title}", show.Title, video.Title);
                                }
                            }
                        }
                    }
                    else
                    {
                        _logger.LogDebug("Show {Show} ({ShowId}): No videos returned from API for trending", show.Title, show.Id);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogWarning(ex, "Error fetching videos for show {ShowId}", show.Id);
                }

                // Respect cancellation
                if (cancellationToken.IsCancellationRequested)
                {
                    break;
                }
            }

            _logger.LogInformation("Refreshed {Count} trending content items from {ShowCount} shows", urns.Count, popularShows.Count);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error refreshing trending content");
        }

        return urns;
    }

    /// <summary>
    /// Refreshes all content (latest and trending).
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <returns>Tuple with counts of latest and trending items.</returns>
    public async Task<(int LatestCount, int TrendingCount)> RefreshAllContentAsync(CancellationToken cancellationToken)
    {
        _logger.LogInformation("Starting full content refresh");

        var latestUrns = await RefreshLatestContentAsync(cancellationToken).ConfigureAwait(false);
        var trendingUrns = await RefreshTrendingContentAsync(cancellationToken).ConfigureAwait(false);

        var latestCount = latestUrns.Count;
        var trendingCount = trendingUrns.Count;

        _logger.LogInformation(
            "Content refresh completed. Latest: {LatestCount}, Trending: {TrendingCount}",
            latestCount,
            trendingCount);

        return (latestCount, trendingCount);
    }

    /// <summary>
    /// Gets content recommendations (combines latest and trending).
    /// </summary>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <returns>List of recommended URNs.</returns>
    public async Task<List<string>> GetRecommendedContentAsync(CancellationToken cancellationToken)
    {
        var recommendations = new HashSet<string>();

        var latestUrns = await RefreshLatestContentAsync(cancellationToken).ConfigureAwait(false);
        var trendingUrns = await RefreshTrendingContentAsync(cancellationToken).ConfigureAwait(false);

        foreach (var urn in latestUrns.Concat(trendingUrns))
        {
            recommendations.Add(urn);
        }

        _logger.LogInformation("Generated {Count} content recommendations", recommendations.Count);
        return recommendations.ToList();
    }
}
