using System.Net;
using System.Collections.Concurrent;
using Peripherals.HdmiInClient;
using Peripherals.JpegClient;

namespace server.Services;

public class HdmiVideoStreamEndpoint
{
    public string BoardId { get; set; } = "";
    public string MjpegUrl { get; set; } = "";
    public string VideoUrl { get; set; } = "";
    public string SnapshotUrl { get; set; } = "";
}

public class HdmiVideoStreamClient
{
    public required HdmiIn HdmiInClient { get; set; }

    public required Jpeg JpegClient { get; set; }

    public required CancellationTokenSource CTS { get; set; }
}

public class HttpHdmiVideoStreamService : BackgroundService
{
    private readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    private readonly IServiceProvider _serviceProvider;

    private HttpListener? _httpListener;
    private readonly int _serverPort = 4322;
    private readonly ConcurrentDictionary<string, HdmiVideoStreamClient> _clientDict = new();

    public HttpHdmiVideoStreamService(IServiceProvider serviceProvider)
    {
        _serviceProvider = serviceProvider;
    }

    public override async Task StartAsync(CancellationToken cancellationToken)
    {
        _httpListener = new HttpListener();
        _httpListener.Prefixes.Add($"http://{Global.LocalHost}:{_serverPort}/");
        _httpListener.Start();
        logger.Info($"HDMI Video Stream Service started on port {_serverPort}");

        await base.StartAsync(cancellationToken);
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            if (_httpListener == null) continue;
            try
            {
                logger.Debug("Waiting for HTTP request...");
                var contextTask = _httpListener.GetContextAsync();
                var completedTask = await Task.WhenAny(contextTask, Task.Delay(-1, stoppingToken));
                if (completedTask == contextTask)
                {
                    var context = contextTask.Result;
                    logger.Debug($"Received request: {context.Request.Url?.AbsolutePath}");
                    if (context != null)
                        _ = HandleRequestAsync(context, stoppingToken);
                }
                else
                {
                    break;
                }
            }
            catch (Exception ex)
            {
                logger.Error(ex, "Error in GetContextAsync");
                break;
            }
        }
    }

    public override async Task StopAsync(CancellationToken cancellationToken)
    {
        logger.Info("Stopping HDMI Video Stream Service...");
        _httpListener?.Close();

        // 禁用所有活跃的HDMI传输
        var disableTasks = new List<Task>();
        foreach (var hdmiKey in _clientDict.Keys)
        {
            disableTasks.Add(DisableHdmiTransmissionAsync(hdmiKey));
        }

        // 等待所有禁用操作完成
        await Task.WhenAll(disableTasks);

        // 清空字典
        _clientDict.Clear();

        _httpListener?.Close(); // 立即关闭监听器，唤醒阻塞
        await base.StopAsync(cancellationToken);
    }

    public async Task DisableHdmiTransmissionAsync(string key)
    {
        try
        {
            var client = _clientDict[key];
            client.CTS.Cancel();

            var disableResult = await client.HdmiInClient.EnableTrans(false);
            if (disableResult.IsSuccessful)
            {
                logger.Info("Successfully disabled HDMI transmission");
            }
            else
            {
                logger.Error($"Failed to disable HDMI transmission: {disableResult.Error}");
            }
        }
        catch (Exception ex)
        {
            logger.Error(ex, "Exception occurred while disabling HDMI transmission");
        }
    }

    private async Task<HdmiVideoStreamClient?> GetOrCreateClientAsync(string boardId)
    {
        if (_clientDict.TryGetValue(boardId, out var client)) return client;

        using var scope = _serviceProvider.CreateScope();
        var userManager = scope.ServiceProvider.GetRequiredService<Database.UserManager>();

        var boardRet = userManager.GetBoardByID(Guid.Parse(boardId));
        if (!boardRet.IsSuccessful || !boardRet.Value.HasValue)
        {
            logger.Error($"Failed to get board with ID {boardId}");
            return null;
        }

        var board = boardRet.Value.Value;

        client = new HdmiVideoStreamClient()
        {
            HdmiInClient = new HdmiIn(board.IpAddr, board.Port, 1),
            JpegClient = new Jpeg(board.IpAddr, board.Port, 1),
            CTS = new CancellationTokenSource()
        };

        // 启用HDMI传输
        try
        {
            var hdmiEnableRet = await client.HdmiInClient.EnableTrans(true);
            if (!hdmiEnableRet.IsSuccessful)
            {
                logger.Error($"Failed to enable HDMI transmission for board {boardId}: {hdmiEnableRet.Error}");
                return null;
            }
            logger.Info($"Successfully enabled HDMI transmission for board {boardId}");

            var jpegEnableRet = await client.JpegClient.Init(true);
            if (!jpegEnableRet.IsSuccessful)
            {
                logger.Error($"Failed to enable JPEG transmission for board {boardId}: {jpegEnableRet.Error}");
                return null;
            }
            logger.Info($"Successfully enabled JPEG transmission for board {boardId}");
        }
        catch (Exception ex)
        {
            logger.Error(ex, $"Exception occurred while enabling HDMI transmission for board {boardId}");
            return null;
        }

        _clientDict[boardId] = client;
        return client;
    }

    private async Task HandleRequestAsync(HttpListenerContext context, CancellationToken cancellationToken)
    {
        var path = context.Request.Url?.AbsolutePath ?? "/";
        var boardId = context.Request.QueryString["boardId"];
        if (string.IsNullOrEmpty(boardId))
        {
            await SendErrorAsync(context.Response, "Missing boardId");
            return;
        }

        var client = await GetOrCreateClientAsync(boardId);
        if (client == null)
        {
            await SendErrorAsync(context.Response, "Invalid boardId or board not available");
            return;
        }

        var hdmiInToken = _clientDict[boardId].CTS.Token;
        if (hdmiInToken == null)
        {
            await SendErrorAsync(context.Response, "HDMI input is not available");
            return;
        }

        if (path == "/snapshot")
        {
            await HandleSnapshotRequestAsync(context.Response, client, hdmiInToken);
        }
        else if (path == "/mjpeg")
        {
            await HandleMjpegStreamAsync(context.Response, client, hdmiInToken);
        }
        else if (path == "/video")
        {
            await SendVideoHtmlPageAsync(context.Response, boardId);
        }
        else
        {
            await SendIndexHtmlPageAsync(context.Response, boardId);
        }
    }

    private async Task HandleSnapshotRequestAsync(
        HttpListenerResponse response, HdmiVideoStreamClient client, CancellationToken cancellationToken)
    {
        try
        {
            logger.Debug("处理HDMI快照请求");

            // 从HDMI读取RGB565数据
            var frameResult = await client.HdmiInClient.ReadFrame();
            if (!frameResult.IsSuccessful || frameResult.Value == null)
            {
                logger.Error("HDMI快照获取失败");
                response.StatusCode = 500;
                var errorBytes = System.Text.Encoding.UTF8.GetBytes("Failed to get HDMI snapshot");
                await response.OutputStream.WriteAsync(errorBytes, 0, errorBytes.Length, cancellationToken);
                response.Close();
                return;
            }

            var jpegData = frameResult.Value;

            // 设置响应头（参考Camera版本）
            response.ContentType = "image/jpeg";
            response.ContentLength64 = jpegData.Length;
            response.Headers.Add("Cache-Control", "no-cache, no-store, must-revalidate");

            await response.OutputStream.WriteAsync(jpegData, 0, jpegData.Length, cancellationToken);
            await response.OutputStream.FlushAsync(cancellationToken);

            logger.Debug("已发送HDMI快照图像，大小：{Size} 字节", jpegData.Length);
        }
        catch (Exception ex)
        {
            logger.Error(ex, "处理HDMI快照请求时出错");
            response.StatusCode = 500;
        }
        finally
        {
            response.Close();
        }
    }

    private async Task HandleMjpegStreamAsync(
        HttpListenerResponse response, HdmiVideoStreamClient client, CancellationToken cancellationToken)
    {
        try
        {
            // 设置MJPEG流的响应头（参考Camera版本）
            response.ContentType = "multipart/x-mixed-replace; boundary=--boundary";
            response.Headers.Add("Cache-Control", "no-cache, no-store, must-revalidate");
            response.Headers.Add("Pragma", "no-cache");
            response.Headers.Add("Expires", "0");

            logger.Debug("开始HDMI MJPEG流传输");

            int frameCounter = 0;

            while (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    var frameStartTime = DateTime.UtcNow;

                    var ret = await client.HdmiInClient.GetMJpegFrame();
                    if (ret == null) continue;
                    var frame = ret.Value;

                    await response.OutputStream.WriteAsync(frame.header, 0, frame.header.Length, cancellationToken);
                    await response.OutputStream.WriteAsync(frame.data, 0, frame.data.Length, cancellationToken);
                    await response.OutputStream.WriteAsync(frame.footer, 0, frame.footer.Length, cancellationToken);
                    await response.OutputStream.FlushAsync(cancellationToken);

                    frameCounter++;

                    var totalTime = (DateTime.UtcNow - frameStartTime).TotalMilliseconds;

                    // 性能统计日志（每30帧记录一次）
                    if (frameCounter % 30 == 0)
                    {
                        logger.Debug("HDMI帧 {FrameNumber} 性能统计 - 总计: {TotalTime:F1}ms, JPEG大小: {JpegSize} 字节",
                            frameCounter, totalTime, frame.data.Length);
                    }
                }
                catch (Exception ex)
                {
                    logger.Error(ex, "处理HDMI帧时发生错误");
                }
            }
        }
        catch (Exception ex)
        {
            logger.Error(ex, "HDMI MJPEG流处理异常");
        }
        finally
        {
            try
            {
                // 停止传输时禁用HDMI传输
                await client.HdmiInClient.EnableTrans(false);
                logger.Info("已禁用HDMI传输");
            }
            catch (Exception ex)
            {
                logger.Error(ex, "禁用HDMI传输时出错");
            }

            try
            {
                response.Close();
            }
            catch
            {
                // 忽略关闭时的错误
            }
            logger.Debug("HDMI MJPEG流连接已关闭");
        }
    }

    private async Task SendVideoHtmlPageAsync(HttpListenerResponse response, string boardId)
    {
        string html = $@"<html><body>
                <h1>HDMI Video Stream for Board {boardId}</h1>
                <img src='/mjpeg?boardId={boardId}' />
                </body></html>";
        response.ContentType = "text/html";
        await response.OutputStream.WriteAsync(System.Text.Encoding.UTF8.GetBytes(html));
        response.Close();
    }

    private async Task SendIndexHtmlPageAsync(HttpListenerResponse response, string boardId)
    {
        string html = $@"<html><body>
                <h1>Welcome to HDMI Video Stream Service</h1>
                <a href='/video?boardId={boardId}'>View Video Stream for Board {boardId}</a>
                </body></html>";
        response.ContentType = "text/html";
        await response.OutputStream.WriteAsync(System.Text.Encoding.UTF8.GetBytes(html));
        response.Close();
    }

    private async Task SendErrorAsync(HttpListenerResponse response, string message)
    {
        response.StatusCode = 400;
        await response.OutputStream.WriteAsync(System.Text.Encoding.UTF8.GetBytes(message));
        response.Close();
    }

    /// <summary>
    /// 获取所有可用的HDMI视频流终端点
    /// </summary>
    /// <returns>返回所有可用的HDMI视频流终端点列表</returns>
    public List<HdmiVideoStreamEndpoint>? GetAllVideoEndpoints()
    {
        using var scope = _serviceProvider.CreateScope();
        var userManager = scope.ServiceProvider.GetRequiredService<Database.UserManager>();

        var boards = userManager.GetAllBoard();
        if (boards == null)
            return null;

        var endpoints = new List<HdmiVideoStreamEndpoint>();
        foreach (var board in boards)
        {
            endpoints.Add(new HdmiVideoStreamEndpoint
            {
                BoardId = board.ID.ToString(),
                MjpegUrl = $"http://{Global.LocalHost}:{_serverPort}/mjpeg?boardId={board.ID}",
                VideoUrl = $"http://{Global.LocalHost}:{_serverPort}/video?boardId={board.ID}",
                SnapshotUrl = $"http://{Global.LocalHost}:{_serverPort}/snapshot?boardId={board.ID}"
            });
        }
        return endpoints;
    }

    /// <summary>
    /// 获取指定板卡ID的HDMI视频流终端点
    /// </summary>
    /// <param name="boardId">板卡ID</param>
    /// <returns>返回指定板卡的HDMI视频流终端点</returns>
    public HdmiVideoStreamEndpoint GetVideoEndpoint(string boardId)
    {
        return new HdmiVideoStreamEndpoint
        {
            BoardId = boardId,
            MjpegUrl = $"http://{Global.LocalHost}:{_serverPort}/mjpeg?boardId={boardId}",
            VideoUrl = $"http://{Global.LocalHost}:{_serverPort}/video?boardId={boardId}",
            SnapshotUrl = $"http://{Global.LocalHost}:{_serverPort}/snapshot?boardId={boardId}"
        };
    }
}
