using Ayay.SerilogLogs;
using Newtonsoft.Json.Linq;
using Serilog;
using SHH.Contracts.Grpc;

namespace SHH.CameraService;

/// <summary>
/// gRpc 指令分发器
/// 职责：接收从 GrpcCommandReceiverWorker 传入的 Proto 消息，解析参数并路由至具体的 Handler。
/// </summary>
public class CommandDispatcher
{
    private ILogger _gRpcLog = Log.ForContext("SourceContext", LogModules.gRpc);
    private readonly Dictionary<string, ICommandHandler> _handlers;

    /// <summary>
    /// 构造函数：通过 DI 注入所有已注册的处理器 (SyncCameraHandler, RemoveCameraHandler 等)
    /// </summary>
    public CommandDispatcher(IEnumerable<ICommandHandler> handlers)
    {
        // 将处理器列表转换为字典，方便 O(1) 查询
        _handlers = handlers.ToDictionary(
            h => h.ActionName,
            h => h,
            StringComparer.OrdinalIgnoreCase);
    }

    /// <summary>
    /// 执行指令分发
    /// </summary>
    /// <param name="protoMsg">从 gRpc Server Streaming 接收到的原始 Proto 指令对象</param>
    public async Task DispatchAsync(CommandPayloadProto protoMsg)
    {
        if (protoMsg == null) return;

        string cmdCode = protoMsg.CmdCode; // 例如 "Sync_Camera"
        _gRpcLog.Information($"[gRpc] 响应请求, 业务:{protoMsg.CmdCode}, 请求ID:{protoMsg.RequestId}, 业务分发.");
        _gRpcLog.Debug($"[gRpc] 响应请求, {protoMsg.CmdCode}, 请求ID:{protoMsg.RequestId}, 业务分发 => {protoMsg}");

        try
        {
            // 1. 查找对应的处理器
            if (_handlers.TryGetValue(cmdCode, out var handler))
            {
                // 2. 参数转换：将 Proto 里的 JSON 字符串转换为原有 Handler 需要的 JToken
                // 这样你之前的 SyncCameraHandler 代码不需要做任何逻辑改动即可直接复用
                var jsonStr = string.IsNullOrWhiteSpace(protoMsg.JsonParams) ? "{}" : protoMsg.JsonParams;
                var token = JToken.Parse(jsonStr);

                // 3. 调用具体业务执行
                await handler.ExecuteAsync(token);

                _gRpcLog.Information($"[gRpc] 业务:{protoMsg.CmdCode}, 请求ID:{protoMsg.RequestId}, 执行成功.");
            }
            else
            {
                _gRpcLog.Warning($"[gRpc] 业务:{protoMsg.CmdCode}, 请求ID:{protoMsg.RequestId}, 未找到指令处理器.");
            }
        }
        catch (Exception ex)
        {
            _gRpcLog.Error($"[gRpc] 业务:{protoMsg.CmdCode}, 请求ID:{protoMsg.RequestId}, 执行指令处理异常: {ex.Message}.");
        }

        // 注意：关于 ACK (require_ack)
        // 在 NetMQ 时代需要手动回发结果，在 gRpc Server Streaming 模式下，
        // 建议通过 Unary RPC (例如另设一个 ReportCommandResult 方法) 异步上报执行结果。
    }
}