using System;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Plugin.SRFPlay.Configuration;
using Jellyfin.Plugin.SRFPlay.Services.Interfaces;
using MediaBrowser.Controller.Entities;
using MediaBrowser.Controller.Library;
using MediaBrowser.Model.Dto;
using Microsoft.Extensions.Logging;

namespace Jellyfin.Plugin.SRFPlay.Providers;

/// <summary>
/// Provides media sources (playback URLs) for SRF Play content.
/// </summary>
public class SRFMediaProvider : IMediaSourceProvider
{
    private readonly ILogger<SRFMediaProvider> _logger;
    private readonly IMediaCompositionFetcher _compositionFetcher;
    private readonly IStreamUrlResolver _streamResolver;
    private readonly IMediaSourceFactory _mediaSourceFactory;

    /// <summary>
    /// Initializes a new instance of the <see cref="SRFMediaProvider"/> class.
    /// </summary>
    /// <param name="loggerFactory">The logger factory.</param>
    /// <param name="compositionFetcher">The media composition fetcher.</param>
    /// <param name="streamResolver">The stream URL resolver.</param>
    /// <param name="mediaSourceFactory">The media source factory.</param>
    public SRFMediaProvider(
        ILoggerFactory loggerFactory,
        IMediaCompositionFetcher compositionFetcher,
        IStreamUrlResolver streamResolver,
        IMediaSourceFactory mediaSourceFactory)
    {
        _logger = loggerFactory.CreateLogger<SRFMediaProvider>();
        _compositionFetcher = compositionFetcher;
        _streamResolver = streamResolver;
        _mediaSourceFactory = mediaSourceFactory;
    }

    /// <summary>
    /// Gets the provider name.
    /// </summary>
    public string Name => "SRF Play";

    /// <summary>
    /// Gets media sources for the specified item.
    /// </summary>
    /// <param name="item">The item.</param>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <returns>List of media sources.</returns>
    public async Task<IEnumerable<MediaSourceInfo>> GetMediaSources(BaseItem item, CancellationToken cancellationToken)
    {
        var sources = new List<MediaSourceInfo>();

        try
        {
            // Check if this is an SRF item
            if (!item.ProviderIds.TryGetValue("SRF", out var urn) || string.IsNullOrEmpty(urn))
            {
                return sources;
            }

            _logger.LogDebug("GetMediaSources for URN: {Urn}, Item: {ItemName}", urn, item.Name);

            // For scheduled livestreams, use shorter cache TTL (5 minutes) so they refresh when they go live
            var cacheDuration = urn.Contains("scheduled_livestream", StringComparison.OrdinalIgnoreCase) ? 5 : (int?)null;

            var mediaComposition = await _compositionFetcher.GetMediaCompositionAsync(urn, cancellationToken, cacheDuration).ConfigureAwait(false);

            if (mediaComposition?.HasChapters != true)
            {
                _logger.LogWarning("No chapters found for URN: {Urn}", urn);
                return sources;
            }

            // Get the first chapter (main video)
            var chapter = mediaComposition.ChapterList[0];

            // Check if content is expired
            if (_streamResolver.IsContentExpired(chapter))
            {
                _logger.LogWarning("Content expired for URN: {Urn}, ValidTo: {ValidTo}", urn, chapter.ValidTo);
                return sources;
            }

            // Check if content has playable streams
            if (!_streamResolver.HasPlayableContent(chapter))
            {
                _logger.LogWarning("No playable content found for URN: {Urn}", urn);
                return sources;
            }

            // Get quality preference from config
            var config = Plugin.Instance?.Configuration;
            var qualityPref = config?.QualityPreference ?? QualityPreference.HD;

            // Use item ID in hex format without dashes
            var itemIdStr = item.Id.ToString("N");

            // Use factory to create MediaSourceInfo
            var mediaSource = await _mediaSourceFactory.CreateMediaSourceAsync(
                chapter,
                itemIdStr,
                urn,
                qualityPref,
                cancellationToken).ConfigureAwait(false);

            // For scheduled livestreams, retry with fresh data if no stream URL
            if (mediaSource == null && chapter.Type == "SCHEDULED_LIVESTREAM")
            {
                _logger.LogDebug("URN {Urn}: Scheduled livestream has no stream URL, fetching fresh data", urn);

                // Force fresh fetch with short cache duration
                var freshMediaComposition = await _compositionFetcher.GetMediaCompositionAsync(urn, cancellationToken, 1).ConfigureAwait(false);

                if (freshMediaComposition?.HasChapters == true)
                {
                    var freshChapter = freshMediaComposition.ChapterList[0];
                    mediaSource = await _mediaSourceFactory.CreateMediaSourceAsync(
                        freshChapter,
                        itemIdStr,
                        urn,
                        qualityPref,
                        cancellationToken).ConfigureAwait(false);

                    if (mediaSource != null)
                    {
                        chapter = freshChapter;
                        _logger.LogInformation("URN {Urn}: Got fresh stream URL for scheduled livestream", urn);
                    }
                }
            }

            if (mediaSource == null)
            {
                _logger.LogWarning("Could not resolve stream URL for URN: {Urn}", urn);
                return sources;
            }

            sources.Add(mediaSource);
            _logger.LogDebug(
                "MediaSource created for {Title} - Id={Id}, DirectPlay={DirectPlay}, Transcoding={Transcoding}",
                chapter.Title,
                mediaSource.Id,
                mediaSource.SupportsDirectPlay,
                mediaSource.SupportsTranscoding);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Error getting media sources for item: {Name}", item.Name);
        }

        return sources;
    }

    /// <summary>
    /// Gets direct stream provider by unique ID.
    /// </summary>
    /// <param name="uniqueId">The unique ID.</param>
    /// <param name="cancellationToken">The cancellation token.</param>
    /// <returns>The direct stream provider.</returns>
    public Task<IDirectStreamProvider?> GetDirectStreamProviderByUniqueId(string uniqueId, CancellationToken cancellationToken)
    {
        _logger.LogInformation("GetDirectStreamProviderByUniqueId called with uniqueId: {UniqueId}", uniqueId);
        // Not needed for HTTP streams
        return Task.FromResult<IDirectStreamProvider?>(null);
    }

    /// <inheritdoc />
    public Task<ILiveStream> OpenMediaSource(string openToken, List<ILiveStream> currentLiveStreams, CancellationToken cancellationToken)
    {
        // Not used - RequiresOpening is false, proxy handles authentication directly
        _logger.LogWarning("OpenMediaSource unexpectedly called with openToken: {OpenToken}", openToken);
        throw new NotSupportedException("OpenMediaSource not supported - streams use direct proxy access");
    }
}
