using Grpc.Core;
using GeViScopeBridge.Protos;
using GeViScopeBridge.SDK;
using GeViScopeBridge.Utils;
using Serilog;

namespace GeViScopeBridge.Services
{
    /// <summary>
    /// gRPC service for cross-switching operations
    /// </summary>
    public class CrossSwitchServiceImplementation : CrossSwitchService.CrossSwitchServiceBase
    {
        private readonly ActionDispatcher _actionDispatcher;
        private readonly StateQueryHandler _stateQuery;
        private readonly GeViDatabaseWrapper _dbWrapper;
        private readonly ILogger _logger;

        // In-memory routing state (for MVP - would be in database in production)
        private readonly Dictionary<int, int> _routingState = new();  // monitorId -> cameraId
        private readonly object _routingLock = new object();

        public CrossSwitchServiceImplementation(
            ActionDispatcher actionDispatcher,
            StateQueryHandler stateQuery,
            GeViDatabaseWrapper dbWrapper,
            ILogger logger)
        {
            _actionDispatcher = actionDispatcher ?? throw new ArgumentNullException(nameof(actionDispatcher));
            _stateQuery = stateQuery ?? throw new ArgumentNullException(nameof(stateQuery));
            _dbWrapper = dbWrapper ?? throw new ArgumentNullException(nameof(dbWrapper));
            _logger = logger ?? throw new ArgumentNullException(nameof(logger));
        }

        /// <summary>
        /// Execute cross-switch (route camera to monitor)
        /// </summary>
        public override async Task<CrossSwitchResponse> ExecuteCrossSwitch(
            CrossSwitchRequest request,
            ServerCallContext context)
        {
            try
            {
                _logger.Information("ExecuteCrossSwitch: Camera {CameraId} → Monitor {MonitorId}, Mode {Mode}",
                    request.CameraId, request.MonitorId, request.Mode);

                // Execute the cross-switch
                bool success = await _actionDispatcher.ExecuteCrossSwitchAsync(
                    request.CameraId,
                    request.MonitorId,
                    request.Mode);

                if (success)
                {
                    // Update routing state
                    lock (_routingLock)
                    {
                        _routingState[request.MonitorId] = request.CameraId;
                    }

                    var response = new CrossSwitchResponse
                    {
                        Success = true,
                        Message = $"Camera {request.CameraId} successfully routed to monitor {request.MonitorId}",
                        CameraId = request.CameraId,
                        MonitorId = request.MonitorId,
                        ExecutedAt = new Timestamp
                        {
                            Seconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
                            Nanos = 0
                        }
                    };

                    _logger.Information("Cross-switch executed successfully");
                    return response;
                }
                else
                {
                    throw new RpcException(new Status(StatusCode.Internal,
                        "Cross-switch operation failed"));
                }
            }
            catch (RpcException)
            {
                throw;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "Failed to execute cross-switch");
                throw ErrorTranslator.CreateRpcException(ex, "Cross-switch failed");
            }
        }

        /// <summary>
        /// Clear monitor (stop displaying video)
        /// </summary>
        public override async Task<ClearMonitorResponse> ClearMonitor(
            ClearMonitorRequest request,
            ServerCallContext context)
        {
            try
            {
                _logger.Information("ClearMonitor: Monitor {MonitorId}", request.MonitorId);

                bool success = await _actionDispatcher.ClearMonitorAsync(request.MonitorId);

                if (success)
                {
                    // Update routing state
                    lock (_routingLock)
                    {
                        _routingState.Remove(request.MonitorId);
                    }

                    var response = new ClearMonitorResponse
                    {
                        Success = true,
                        Message = $"Monitor {request.MonitorId} cleared successfully",
                        MonitorId = request.MonitorId,
                        ExecutedAt = new Timestamp
                        {
                            Seconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
                            Nanos = 0
                        }
                    };

                    _logger.Information("Monitor cleared successfully");
                    return response;
                }
                else
                {
                    throw new RpcException(new Status(StatusCode.Internal,
                        "Clear monitor operation failed"));
                }
            }
            catch (RpcException)
            {
                throw;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "Failed to clear monitor");
                throw ErrorTranslator.CreateRpcException(ex, "Clear monitor failed");
            }
        }

        /// <summary>
        /// Get current routing state
        /// </summary>
        public override async Task<GetRoutingStateResponse> GetRoutingState(
            GetRoutingStateRequest request,
            ServerCallContext context)
        {
            try
            {
                _logger.Information("GetRoutingState called");

                var response = new GetRoutingStateResponse
                {
                    TotalRoutes = 0,
                    RetrievedAt = new Timestamp
                    {
                        Seconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
                        Nanos = 0
                    }
                };

                // Get camera and monitor lists for names
                var cameras = await _stateQuery.EnumerateCamerasAsync();
                var monitors = await _stateQuery.EnumerateMonitorsAsync();

                lock (_routingLock)
                {
                    response.TotalRoutes = _routingState.Count;

                    foreach (var route in _routingState)
                    {
                        int monitorId = route.Key;
                        int cameraId = route.Value;

                        var camera = cameras.FirstOrDefault(c => c.Id == cameraId);
                        var monitor = monitors.FirstOrDefault(m => m.Id == monitorId);

                        response.Routes.Add(new RouteInfo
                        {
                            CameraId = cameraId,
                            MonitorId = monitorId,
                            CameraName = camera?.Name ?? $"Camera {cameraId}",
                            MonitorName = monitor?.Name ?? $"Monitor {monitorId}",
                            RoutedAt = new Timestamp
                            {
                                Seconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
                                Nanos = 0
                            }
                        });
                    }
                }

                _logger.Information("GetRoutingState completed: {Count} routes", response.TotalRoutes);
                return response;
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "Failed to get routing state");
                throw ErrorTranslator.CreateRpcException(ex, "Get routing state failed");
            }
        }

        /// <summary>
        /// Health check
        /// </summary>
        public override Task<HealthCheckResponse> HealthCheck(
            Empty request,
            ServerCallContext context)
        {
            try
            {
                _logger.Debug("HealthCheck called");

                bool isConnected = _dbWrapper.IsConnected;
                string sdkStatus = isConnected ? "connected" : "disconnected";

                var response = new HealthCheckResponse
                {
                    IsHealthy = isConnected,
                    SdkStatus = sdkStatus,
                    GeviServerHost = "localhost",  // TODO: Get from config
                    CheckedAt = new Timestamp
                    {
                        Seconds = DateTimeOffset.UtcNow.ToUnixTimeSeconds(),
                        Nanos = 0
                    }
                };

                return Task.FromResult(response);
            }
            catch (Exception ex)
            {
                _logger.Error(ex, "Health check failed");
                throw ErrorTranslator.CreateRpcException(ex, "Health check failed");
            }
        }
    }
}
