using System.Net;
using System.Text;
using System.Collections.Concurrent;
using DotNext;
using DotNext.Threading;

#if USB_CAMERA
using OpenCvSharp;
#endif

namespace server.Services;

public class VideoStreamClient
{
    public string? ClientId { get; set; } = string.Empty;
    public bool IsEnabled { get; set; } = true;
    public int FrameWidth { get; set; }
    public int FrameHeight { get; set; }
    public int FrameRate { get; set; }
    public Peripherals.CameraClient.Camera Camera { get; set; }
    public CancellationTokenSource CTS { get; set; }
    public readonly AsyncReaderWriterLock Lock = new();

    public VideoStreamClient(
        string clientId, int width, int height, Peripherals.CameraClient.Camera camera)
    {
        ClientId = clientId;
        FrameWidth = width;
        FrameHeight = height;
        FrameRate = 0;
        Camera = camera;
        CTS = new CancellationTokenSource();
    }
}

/// <summary>
/// 表示摄像头连接状态信息
/// </summary>
public class VideoStreamEndpoint
{
    public required string BoardId { get; set; } = "";
    public required string MjpegUrl { get; set; } = "";
    public required string VideoUrl { get; set; } = "";
    public required string SnapshotUrl { get; set; } = "";
    public required string HtmlUrl { get; set; } = "";
    public required string UsbCameraUrl { get; set; } = "";

    public required bool IsEnabled { get; set; }

    /// <summary>
    /// 视频流的帧率（FPS）
    /// </summary>
    public required int FrameRate { get; set; }

    public int FrameWidth { get; set; }
    public int FrameHeight { get; set; }

    /// <summary>
    /// 视频分辨率（如 640x480）
    /// </summary>
    public string Resolution => $"{FrameWidth}x{FrameHeight}";
}

/// <summary>
/// 表示视频流服务的运行状态
/// </summary>
public class VideoStreamServiceStatus
{
    /// <summary>
    /// 服务是否正在运行
    /// </summary>
    public bool IsRunning { get; set; }

    /// <summary>
    /// 服务监听的端口号
    /// </summary>
    public int ServerPort { get; set; }

    /// <summary>
    /// 当前连接的客户端端点列表
    /// </summary>
    public List<VideoStreamEndpoint> ClientEndpoints { get; set; } = new();

    /// <summary>
    /// 当前连接的客户端数量
    /// </summary>
    public int ConnectedClientsNum => ClientEndpoints.Count;
}

/// <summary>
/// HTTP 视频流服务，用于从 FPGA 获取图像数据并推送到前端网页
/// 支持动态配置摄像头地址和端口
/// </summary>
public class HttpVideoStreamService : BackgroundService
{
    private static readonly NLog.Logger logger = NLog.LogManager.GetCurrentClassLogger();

    private readonly IServiceProvider _serviceProvider;

    private HttpListener? _httpListener;
    private readonly int _serverPort = 4321;

    private readonly ConcurrentDictionary<string, VideoStreamClient> _clientDict = new();

    // USB Camera 相关
#if USB_CAMERA
    private VideoCapture? _usbCamera;
    private bool _usbCameraEnable = false;
    private readonly object _usbCameraLock = new object();
#endif

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

    private Optional<VideoStreamClient> TryGetClient(string boardId)
    {
        if (_clientDict.TryGetValue(boardId, out var client))
        {
            return client;
        }
        return null;
    }

    private async Task<VideoStreamClient?> GetOrCreateClientAsync(string boardId, int initWidth, int initHeight)
    {
        if (_clientDict.TryGetValue(boardId, out var client))
        {
            // 可在此处做分辨率/Camera等配置更新
            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;

        var camera = new Peripherals.CameraClient.Camera(board.IpAddr, board.Port);
        var ret = await camera.Init();
        if (!ret.IsSuccessful || !ret.Value)
        {
            logger.Error("Camera Init Failed!");
            return null;
        }

        client = new VideoStreamClient(boardId, initWidth, initHeight, camera);
        _clientDict[boardId] = client;
        return client;
    }

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

        await base.StartAsync(cancellationToken);
    }

    /// <summary>
    /// 停止 HTTP 视频流服务
    /// </summary>
    public override async Task StopAsync(CancellationToken cancellationToken)
    {
        foreach (var clientKey in _clientDict.Keys)
        {
            var client = _clientDict[clientKey];
            client.CTS.Cancel();
            using (await client.Lock.AcquireWriteLockAsync(cancellationToken))
            {
                await client.Camera.EnableHardwareTrans(false);
            }
        }
        _clientDict.Clear();
        await base.StopAsync(cancellationToken);
    }

    /// <summary>
    /// 执行 HTTP 视频流服务
    /// </summary>
    /// <param name="stoppingToken">取消令牌</param>
    /// <returns>任务</returns>
    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;
            }
        }
    }

    private async Task HandleRequestAsync(HttpListenerContext context, CancellationToken cancellationToken)
    {
        var path = context.Request.Url?.AbsolutePath ?? "/";
        var boardId = context.Request.QueryString["board"];
        var width = int.TryParse(context.Request.QueryString["width"], out var w) ? w : 640;
        var height = int.TryParse(context.Request.QueryString["height"], out var h) ? h : 480;

        if (string.IsNullOrEmpty(boardId))
        {
            await SendErrorAsync(context.Response, "Missing clientId");
            return;
        }

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

        var clientToken = client.CTS.Token;
        try
        {
            logger.Info("新HTTP客户端连接: {RemoteEndPoint}", context.Request.RemoteEndPoint);

            if (path == "/video-stream")
            {
                // MJPEG 流请求（FPGA）
                await HandleMjpegStreamAsync(context.Response, client, cancellationToken);
            }
#if USB_CAMERA
            else if (requestPath == "/usb-camera")
            {
                // USB Camera MJPEG流请求
                await HandleUsbCameraStreamAsync(response, cancellationToken);
            }
#endif
            else if (path == "/snapshot")
            {
                // 单帧图像请求
                await HandleSnapshotRequestAsync(context.Response, client, cancellationToken);
            }
            else if (path == "/html")
            {
                // HTML页面请求
                await SendIndexHtmlPageAsync(context.Response);
            }
            else
            {
                // 默认返回简单的HTML页面，提供链接到视频页面
                await SendIndexHtmlPageAsync(context.Response);
            }
        }
        catch (Exception ex)
        {
            logger.Error(ex, "接受HTTP客户端连接时发生错误");
        }
    }

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

    // USB Camera MJPEG流处理
#if USB_CAMERA
    private async Task HandleUsbCameraStreamAsync(HttpListenerResponse response, CancellationToken cancellationToken)
    {
        try
        {
            lock (_usbCameraLock)
            {
                if (_usbCamera == null)
                {
                    _usbCamera = new VideoCapture(1);
                    _usbCamera.Fps = _frameRate;
                    _usbCamera.FrameWidth = _frameWidth;
                    _usbCamera.FrameHeight = _frameHeight;
                    _usbCameraEnable = _usbCamera.IsOpened();
                }
            }
            if (!_usbCameraEnable || _usbCamera == null || !_usbCamera.IsOpened())
            {
                response.StatusCode = 500;
                await response.OutputStream.FlushAsync(cancellationToken);
                response.Close();
                return;
            }

            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");

            using (var mat = new Mat())
            {
                while (!cancellationToken.IsCancellationRequested)
                {
                    bool grabbed;
                    lock (_usbCameraLock)
                    {
                        grabbed = _usbCamera.Read(mat);
                    }
                    if (!grabbed || mat.Empty())
                    {
                        await Task.Delay(50, cancellationToken);
                        continue;
                    }

                    // 编码为JPEG
                    byte[]? jpegData = null;
                    try
                    {
                        jpegData = mat.ToBytes(".jpg", new int[] { (int)ImwriteFlags.JpegQuality, 80 });
                    }
                    catch (Exception ex)
                    {
                        logger.Error(ex, "USB Camera帧编码JPEG失败");
                        continue;
                    }
                    if (jpegData == null)
                        continue;

                    // MJPEG帧头
                    var header = Encoding.ASCII.GetBytes("--boundary\r\nContent-Type: image/jpeg\r\nContent-Length: " + jpegData.Length + "\r\n\r\n");
                    await response.OutputStream.WriteAsync(header, 0, header.Length, cancellationToken);
                    await response.OutputStream.WriteAsync(jpegData, 0, jpegData.Length, cancellationToken);
                    await response.OutputStream.WriteAsync(new byte[] { 0x0D, 0x0A }, 0, 2, cancellationToken); // \r\n
                    await response.OutputStream.FlushAsync(cancellationToken);

                    await Task.Delay(1000 / _frameRate, cancellationToken);
                }
            }
        }
        catch (Exception ex)
        {
            logger.Error(ex, "USB Camera MJPEG流处理异常");
        }
        finally
        {
            try { response.Close(); } catch { }
        }
    }
#endif

    private async Task HandleSnapshotRequestAsync(HttpListenerResponse response, VideoStreamClient client, CancellationToken cancellationToken)
    {
        // 读取 Camera 快照，返回 JPEG
        var frameResult = await client.Camera.ReadFrame();
        if (!frameResult.IsSuccessful || frameResult.Value == null)
        {
            response.StatusCode = 500;
            await response.OutputStream.WriteAsync(Encoding.UTF8.GetBytes("Failed to get snapshot"));
            response.Close();
            return;
        }
        var jpegResult = Common.Image.ConvertRGB24ToJpeg(frameResult.Value, client.FrameWidth, client.FrameHeight, 80);
        if (!jpegResult.IsSuccessful)
        {
            response.StatusCode = 500;
            await response.OutputStream.WriteAsync(Encoding.UTF8.GetBytes("JPEG conversion failed"));
            response.Close();
            return;
        }
        response.ContentType = "image/jpeg";
        response.ContentLength64 = jpegResult.Value.Length;
        await response.OutputStream.WriteAsync(jpegResult.Value, 0, jpegResult.Value.Length, cancellationToken);
        response.Close();
    }

    private async Task HandleMjpegStreamAsync(HttpListenerResponse response, VideoStreamClient client, CancellationToken cancellationToken)
    {
        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");

        while (!cancellationToken.IsCancellationRequested)
        {
            var frameResult = await client.Camera.ReadFrame();
            if (!frameResult.IsSuccessful || frameResult.Value == null) continue;
            var jpegResult = Common.Image.ConvertRGB24ToJpeg(frameResult.Value, client.FrameWidth, client.FrameHeight, 80);
            if (!jpegResult.IsSuccessful) continue;

            var header = Encoding.ASCII.GetBytes("--boundary\r\nContent-Type: image/jpeg\r\nContent-Length: " + jpegResult.Value.Length + "\r\n\r\n");
            await response.OutputStream.WriteAsync(header, 0, header.Length, cancellationToken);
            await response.OutputStream.WriteAsync(jpegResult.Value, 0, jpegResult.Value.Length, cancellationToken);
            await response.OutputStream.WriteAsync(new byte[] { 0x0D, 0x0A }, 0, 2, cancellationToken);
            await response.OutputStream.FlushAsync(cancellationToken);

            await Task.Delay(1000 / client.FrameWidth, cancellationToken);
        }
        response.Close();
    }

    private async Task SendVideoHtmlPageAsync(HttpListenerResponse response)
    {
        string html = $@"
                <!DOCTYPE html>
                <html>
                <head>
                    <title>FPGA 视频流</title>
                    <meta charset=""utf-8"">
                    <style>
                        body {{ font-family: Arial, sans-serif; text-align: center; margin: 20px; }}
                        h1 {{ color: #333; }}
                        .video-container {{ margin: 20px auto; max-width: 800px; }}
                        .controls {{ margin: 10px 0; }}
                        img {{ max-width: 100%; border: 1px solid #ddd; }}
                        button {{ padding: 8px 16px; margin: 0 5px; cursor: pointer; }}
                    </style>
                </head>
                <body>
                    <h1>FPGA 实时视频流</h1>
                    <div class=""video-container"">
                        <img id=""videoStream"" src=""/video-stream"" alt=""FPGA视频流"" />
                    </div>
                    <div class=""controls"">
                        <button onclick=""document.getElementById('videoStream').src='/snapshot?t=' + new Date().getTime()"">刷新快照</button>
                        <button onclick=""document.getElementById('videoStream').src='/video-stream'"">开始流媒体</button>
                        <span id=""status"">状态: 连接中...</span>
                    </div>
                    <script>
                        document.getElementById('videoStream').onload = function() {{
                            document.getElementById('status').textContent = '状态: 已连接';
                        }};
                        document.getElementById('videoStream').onerror = function() {{
                            document.getElementById('status').textContent = '状态: 连接错误';
                        }};
                    </script>
                </body>
                </html>
            ";

        response.ContentType = "text/html";
        response.ContentEncoding = Encoding.UTF8;
        byte[] buffer = Encoding.UTF8.GetBytes(html);
        response.ContentLength64 = buffer.Length;

        await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
        response.Close();
    }

    private async Task SendIndexHtmlPageAsync(HttpListenerResponse response)
    {
        string html = $@"
                <!DOCTYPE html>
                <html>
                <head>
                    <title>FPGA WebLab 视频服务</title>
                    <meta charset=""utf-8"">
                    <style>
                        body {{ font-family: Arial, sans-serif; text-align: center; margin: 20px; }}
                        h1 {{ color: #333; }}
                        .links {{ margin: 20px; }}
                        a {{ padding: 10px 15px; background-color: #4CAF50; color: white; text-decoration: none; border-radius: 4px; margin: 5px; display: inline-block; }}
                        a:hover {{ background-color: #45a049; }}
                    </style>
                </head>
                <body>
                    <h1>FPGA WebLab 视频服务</h1>
                    <div class=""links"">
                        <a href=""/video-feed.html"">观看实时视频</a>
                        <a href=""/snapshot"" target=""_blank"">获取当前快照</a>
                    </div>
                    <p>HTTP流媒体服务端口: {_serverPort}</p>
                </body>
                </html>
            ";

        response.ContentType = "text/html";
        response.ContentEncoding = Encoding.UTF8;
        byte[] buffer = Encoding.UTF8.GetBytes(html);
        response.ContentLength64 = buffer.Length;

        await response.OutputStream.WriteAsync(buffer, 0, buffer.Length);
        response.Close();
    }

    /// <summary>
    /// 从 FPGA 获取图像数据
    /// 实际从摄像头读取 RGB565 格式数据并转换为 RGB24
    /// </summary>
    private async Task<byte[]?> GetFPGAImageData(
        VideoStreamClient client, CancellationToken cancellationToken = default)
    {
        try
        {
            using (await client.Lock.AcquireWriteLockAsync(cancellationToken))
            {
                // 从摄像头读取帧数据
                var readStartTime = DateTime.UtcNow;
                var result = await client.Camera.ReadFrame();
                var readEndTime = DateTime.UtcNow;
                var readTime = (readEndTime - readStartTime).TotalMilliseconds;

                if (!result.IsSuccessful)
                {
                    logger.Error("读取摄像头帧数据失败: {Error}", result.Error);
                    return new byte[0];
                }

                var rgb565Data = result.Value;

                // 验证数据长度是否正确
                if (!Common.Image.ValidateImageDataLength(rgb565Data, client.FrameWidth, client.FrameHeight, 2))
                {
                    logger.Warn("摄像头数据长度不匹配，期望: {Expected}, 实际: {Actual}",
                        client.FrameWidth * client.FrameHeight * 2, rgb565Data.Length);
                }

                // 将 RGB565 转换为 RGB24
                var convertStartTime = DateTime.UtcNow;
                var rgb24Result = Common.Image.ConvertRGB565ToRGB24(rgb565Data, client.FrameWidth, client.FrameHeight, isLittleEndian: false);
                var convertEndTime = DateTime.UtcNow;
                var convertTime = (convertEndTime - convertStartTime).TotalMilliseconds;

                if (!rgb24Result.IsSuccessful)
                {
                    logger.Error("RGB565转RGB24失败: {Error}", rgb24Result.Error);
                    return new byte[0];
                }

                return rgb24Result.Value;
            }
        }
        catch (Exception ex)
        {
            logger.Error(ex, "获取FPGA图像数据时发生错误");
            return new byte[0];
        }
    }

    /// <summary>
    /// 设置视频流分辨率
    /// </summary>
    /// <param name="boardId">板卡ID</param>
    /// <param name="width">宽度</param>
    /// <param name="height">高度</param>
    /// <param name="timeout">超时时间（毫秒）</param>
    /// <param name="cancellationToken">取消令牌</param>
    /// <returns>设置结果</returns>
    public async Task<Result<bool>> SetResolutionAsync(
        string boardId, int width, int height,
        int timeout = 100, CancellationToken cancellationToken = default)
    {
        try
        {
            var client = TryGetClient(boardId).OrThrow(() => new Exception($"无法获取摄像头客户端: {boardId}"));

            using (await client.Lock.AcquireWriteLockAsync(TimeSpan.FromMilliseconds(timeout), cancellationToken))
            {
                var currentCamera = client.Camera;
                if (currentCamera == null)
                {
                    var message = $"获取摄像头失败";
                    logger.Error(message);
                    return new(new Exception(message));
                }

                // 设置摄像头分辨率
                var ret = await currentCamera.ChangeResolution(width, height);
                if (!ret.IsSuccessful)
                {
                    var message = $"设置摄像头分辨率失败: {ret.Error}";
                    logger.Error(message);
                    return new(new Exception(message));
                }

                if (!ret.Value)
                {
                    logger.Warn($"设置摄像头分辨率失败");
                    return false;
                }

                // 更新HTTP服务的分辨率配置
                client.FrameWidth = width;
                client.FrameHeight = height;

                logger.Info($"视频流分辨率已成功设置为 {width}x{height}");
                return true;
            }
        }
        catch (Exception ex)
        {
            var message = $"设置分辨率时发生错误: {ex.Message}";
            logger.Error(ex, message);
            return new(new Exception(message));
        }
    }

    /// <summary>
    /// 初始化摄像头自动对焦功能
    /// </summary>
    /// <returns>初始化结果</returns>
    public async Task<bool> InitAutoFocusAsync(
        string boardId, int timeout = 1000, CancellationToken cancellationToken = default)
    {
        try
        {
            var client = TryGetClient(boardId).OrThrow(() => new Exception($"无法获取摄像头客户端: {boardId}"));

            using (await client.Lock.AcquireWriteLockAsync(
                TimeSpan.FromMilliseconds(timeout), cancellationToken))
            {
                var result = await client.Camera.InitAutoFocus();

                if (result.IsSuccessful && result.Value)
                {
                    logger.Info($"Board{boardId}摄像头自动对焦功能初始化成功");
                    return true;
                }
                else
                {
                    logger.Error($"Board{boardId}摄像头自动对焦功能初始化失败: {result.Error?.Message ?? "未知错误"}");
                    return false;
                }
            }
        }
        catch (Exception ex)
        {
            logger.Error(ex, $"Board{boardId}初始化摄像头自动对焦功能时发生异常");
            return false;
        }
    }

    /// <summary>
    /// 执行摄像头自动对焦
    /// </summary>
    /// <returns>对焦结果</returns>
    public async Task<bool> PerformAutoFocusAsync(
        string boardId, int timeout = 1000, CancellationToken cancellationToken = default)
    {
        try
        {
            var client = TryGetClient(boardId).OrThrow(() => new Exception($"无法获取摄像头客户端: {boardId}"));

            logger.Info($"Board{boardId}开始执行摄像头自动对焦");

            var result = await client.Camera.PerformAutoFocus();

            if (result.IsSuccessful && result.Value)
            {
                logger.Info($"Board{boardId}摄像头自动对焦成功");
                return true;
            }
            else
            {
                logger.Error($"Board{boardId}摄像头自动对焦执行失败: {result.Error?.Message ?? "未知错误"}");
                return false;
            }
        }
        catch (Exception ex)
        {
            logger.Error(ex, $"Board{boardId}执行摄像头自动对焦时发生异常");
            return false;
        }
    }

    /// <summary>
    /// 配置摄像头连接参数
    /// </summary>
    /// <param name="boardId">板卡ID</param>
    /// <returns>配置是否成功</returns>
    public async Task<bool> ConfigureCameraAsync(string boardId)
    {
        try
        {
            var client = TryGetClient(boardId).OrThrow(() => new Exception($"无法获取摄像头客户端: {boardId}"));

            using (await client.Lock.AcquireWriteLockAsync())
            {
                var ret = await client.Camera.Init();
                if (!ret.IsSuccessful)
                {
                    logger.Error(ret.Error);
                    throw ret.Error;
                }

                if (!ret.Value)
                {
                    logger.Error($"Camera Init Failed!");
                    throw new Exception($"Camera Init Failed!");
                }
            }

            using (await client.Lock.AcquireWriteLockAsync())
            {
                var ret = await client.Camera.ChangeResolution(client.FrameWidth, client.FrameHeight);
                if (!ret.IsSuccessful)
                {
                    logger.Error(ret.Error);
                    throw ret.Error;
                }

                if (!ret.Value)
                {
                    logger.Error($"Camera Resolution Change Failed!");
                    throw new Exception($"Camera Resolution Change Failed!");
                }
            }

            return true;
        }
        catch (Exception ex)
        {
            logger.Error(ex, "配置摄像头连接时发生错误");
            return false;
        }
    }

    public async Task SetVideoStreamEnableAsync(string boardId, bool enable)
    {
        try
        {
            var client = TryGetClient(boardId).OrThrow(() => new Exception($"无法获取摄像头客户端: {boardId}"));

            if (client.IsEnabled == enable)
                return;

            using (await client.Lock.AcquireWriteLockAsync())
            {
                if (enable)
                {
                    client.CTS = new CancellationTokenSource();
                }
                else
                {
                    client.CTS.Cancel();
                }

                var camera = client.Camera;
                var disableResult = await camera.EnableHardwareTrans(enable);
                if (disableResult.IsSuccessful && disableResult.Value)
                    logger.Info($"Successfully disabled camera {boardId} hardware transmission");
                else
                    logger.Error($"Failed to disable camera {boardId} hardware transmission: {disableResult.Error}");
            }
        }
        catch (Exception ex)
        {
            logger.Error(ex, $"Exception occurred while disabling HDMI transmission for camera {boardId}");
        }
    }

    public async ValueTask<bool> TestCameraConnection(string boardId)
    {
        try
        {
            var client = TryGetClient(boardId).OrThrow(() => new Exception($"无法获取摄像头客户端: {boardId}"));

            var imageData = await GetFPGAImageData(client);
            if (imageData == null || imageData.Length == 0)
                return false;

            return true;
        }
        catch (Exception ex)
        {
            logger.Error(ex, $"Board{boardId}执行摄像头自动对焦时发生异常");
            return false;
        }
    }

    public VideoStreamEndpoint GetVideoEndpoint(string boardId)
    {
        var client = TryGetClient(boardId).OrThrow(() => new Exception($"无法获取摄像头客户端: {boardId}"));

        return new VideoStreamEndpoint
        {
            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}",
            UsbCameraUrl = $"http://{Global.LocalHost}:{_serverPort}/usbCamera?boardId={boardId}",
            HtmlUrl = $"http://{Global.LocalHost}:{_serverPort}/html?boardId={boardId}",
            IsEnabled = client.IsEnabled,
            FrameRate = client.FrameRate
        };
    }

    public List<VideoStreamEndpoint> GetAllVideoEndpoints()
    {
        var endpoints = new List<VideoStreamEndpoint>();

        foreach (var boardId in _clientDict.Keys)
            endpoints.Add(GetVideoEndpoint(boardId));

        return endpoints;
    }

    public VideoStreamServiceStatus GetServiceStatus()
    {
        return new VideoStreamServiceStatus
        {
            IsRunning = true,
            ServerPort = _serverPort,
            ClientEndpoints = GetAllVideoEndpoints()
        };
    }
}
