using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Jellyfin.Plugin.SRFPlay.Api.Models;
using Jellyfin.Plugin.SRFPlay.Configuration;
using Jellyfin.Plugin.SRFPlay.Constants;
using Jellyfin.Plugin.SRFPlay.Services.Interfaces;
using Jellyfin.Plugin.SRFPlay.Utilities;
using MediaBrowser.Controller;
using MediaBrowser.Model.Dto;
using MediaBrowser.Model.Entities;
using MediaBrowser.Model.MediaInfo;
using Microsoft.Extensions.Logging;

namespace Jellyfin.Plugin.SRFPlay.Services;

/// <summary>
/// Factory for creating MediaSourceInfo objects with consistent configuration.
/// Consolidates duplicated logic from SRFMediaProvider and SRFPlayChannel.
/// </summary>
public class MediaSourceFactory : IMediaSourceFactory
{
    private readonly ILogger<MediaSourceFactory> _logger;
    private readonly IStreamUrlResolver _streamResolver;
    private readonly IStreamProxyService _proxyService;
    private readonly IServerApplicationHost _appHost;

    /// <summary>
    /// Initializes a new instance of the <see cref="MediaSourceFactory"/> class.
    /// </summary>
    /// <param name="logger">The logger.</param>
    /// <param name="streamResolver">The stream URL resolver.</param>
    /// <param name="proxyService">The stream proxy service.</param>
    /// <param name="appHost">The server application host.</param>
    public MediaSourceFactory(
        ILogger<MediaSourceFactory> logger,
        IStreamUrlResolver streamResolver,
        IStreamProxyService proxyService,
        IServerApplicationHost appHost)
    {
        _logger = logger;
        _streamResolver = streamResolver;
        _proxyService = proxyService;
        _appHost = appHost;
    }

    /// <inheritdoc />
    public Task<MediaSourceInfo?> CreateMediaSourceAsync(
        Chapter chapter,
        string itemId,
        string urn,
        QualityPreference qualityPreference,
        CancellationToken cancellationToken = default)
    {
        // Get stream URL based on quality preference (unauthenticated)
        var streamUrl = _streamResolver.GetStreamUrl(chapter, qualityPreference);

        if (string.IsNullOrEmpty(streamUrl))
        {
            _logger.LogWarning("Could not resolve stream URL for chapter: {ChapterId}", chapter.Id);
            return Task.FromResult<MediaSourceInfo?>(null);
        }

        // A scheduled livestream is only treated as live when the broadcast window is open.
        // Past replays (ValidTo in the past) and upcoming events (ValidFrom in the future)
        // are treated as VOD to avoid IsInfiniteStream/IgnoreDts flags and FFmpeg -re mode.
        var isScheduledLivestream = chapter.Type == "SCHEDULED_LIVESTREAM" ||
                                    UrnHelper.IsLivestreamUrn(urn);
        var now = DateTime.UtcNow;
        var isLiveStream = isScheduledLivestream &&
            (chapter.ValidFrom == null || chapter.ValidFrom.Value.ToUniversalTime() <= now) &&
            (chapter.ValidTo == null || chapter.ValidTo.Value.ToUniversalTime() > now);

        // Register stream with UNAUTHENTICATED URL - proxy will authenticate on-demand
        // This avoids wasting 30-second tokens during category browsing
        _proxyService.RegisterStreamDeferred(itemId, streamUrl, urn, isLiveStream);

        // Build proxy URL
        var proxyUrl = BuildProxyUrl(itemId);

        _logger.LogDebug(
            "Created media source for {Title} - ItemId: {ItemId}, IsLiveStream: {IsLiveStream}",
            chapter.Title,
            itemId,
            isLiveStream);

        // Create MediaSourceInfo with codec info so clients know they can direct play
        // Provide MediaStreams with H.264+AAC so Android TV/ExoPlayer doesn't trigger transcoding
        var mediaStreams = CreateMediaStreams(qualityPreference);

        var mediaSource = new MediaSourceInfo
        {
            Id = itemId,
            Name = chapter.Title,
            Path = proxyUrl,
            Protocol = MediaProtocol.Http,
            // Use "hls" to trigger hls.js player in web client
            Container = "hls",
            SupportsDirectStream = false,
            SupportsDirectPlay = true,
            SupportsTranscoding = true,
            IsRemote = true,
            Type = MediaSourceType.Default,
            RunTimeTicks = chapter.Duration > 0 ? TimeSpan.FromMilliseconds(chapter.Duration).Ticks : null,
            VideoType = VideoType.VideoFile,
            IsInfiniteStream = isLiveStream,
            // RequiresOpening = false: proxy handles auth directly, no need for Jellyfin to open the stream
            RequiresOpening = false,
            RequiresClosing = false,
            // Disable probing - we provide stream info directly
            SupportsProbing = false,
            ReadAtNativeFramerate = isLiveStream,
            // Provide codec info so clients know they can direct play H.264+AAC
            MediaStreams = mediaStreams.ToList(),
            AnalyzeDurationMs = isLiveStream ? 1000 : 3000,
            IgnoreDts = isLiveStream,
            IgnoreIndex = isLiveStream,
        };

        return Task.FromResult<MediaSourceInfo?>(mediaSource);
    }

    /// <inheritdoc />
    public string BuildProxyUrl(string itemId)
    {
        return $"{GetServerBaseUrl()}{ApiEndpoints.ProxyMasterManifestPath.Replace("{0}", itemId, StringComparison.Ordinal)}";
    }

    /// <inheritdoc />
    public string GetServerBaseUrl()
    {
        var config = Plugin.Instance?.Configuration;
        return config != null && !string.IsNullOrWhiteSpace(config.PublicServerUrl)
            ? config.PublicServerUrl.TrimEnd('/')
            : _appHost.GetSmartApiUrl(string.Empty);
    }

    /// <inheritdoc />
    public IReadOnlyList<MediaStream> CreateMediaStreams(QualityPreference quality)
    {
        // Set resolution/bitrate based on quality preference
        var (width, height, videoBitrate) = quality switch
        {
            QualityPreference.SD => (1280, 720, 2500000),
            QualityPreference.HD => (1920, 1080, 5000000),
            _ => (1280, 720, 3000000)
        };

        return new List<MediaStream>
        {
            new MediaStream
            {
                Type = MediaStreamType.Video,
                Codec = "h264",
                Profile = "high",
                Level = 40,
                Width = width,
                Height = height,
                BitRate = videoBitrate,
                BitDepth = 8,
                IsInterlaced = false,
                IsDefault = true,
                Index = 0,
                IsAVC = true,
                PixelFormat = "yuv420p"
            },
            new MediaStream
            {
                Type = MediaStreamType.Audio,
                Codec = "aac",
                Profile = "LC",
                Channels = 2,
                SampleRate = 48000,
                BitRate = 128000,
                IsDefault = true,
                Index = 1
            }
        };
    }
}
