using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.SignalR;
using Haoliang.Models.Device;
using Haoliang.Models.DataCollection;
using Haoliang.Models.Production;
using Haoliang.Models.System;
using Haoliang.Data.Repositories;
using Haoliang.Core.Services;

namespace Haoliang.Core.Services
{
    public interface IRealTimeService
    {
        Task BroadcastDeviceStatusAsync();
        Task BroadcastProductionDataAsync();
        Task BroadcastAlarmDataAsync();
        Task SendDeviceUpdateAsync(int deviceId, DeviceCurrentStatus status);
        Task SendProductionUpdateAsync(int deviceId, ProductionRecord production);
        Task SendAlarmUpdateAsync(Alarm alarm);
        Task BroadcastSystemHealthAsync();
        Task JoinDeviceGroupAsync(string connectionId, int deviceId);
        Task LeaveDeviceGroupAsync(string connectionId, int deviceId);
        Task JoinAllDevicesGroupAsync(string connectionId);
        Task LeaveAllDevicesGroupAsync(string connectionId);
    }

    public class RealTimeService : IRealTimeService
    {
        private readonly IHubContext<RealTimeHub> _hubContext;
        private readonly IDeviceRepository _deviceRepository;
        private readonly IProductionRepository _productionRepository;
        private readonly IAlarmRepository _alarmRepository;
        private readonly ICollectionRepository _collectionRepository;
        private readonly ILoggerService _logger;
        private readonly Dictionary<string, HashSet<int>> _userDeviceGroups = new Dictionary<string, HashSet<int>>();
        private readonly object _lock = new object();

        public RealTimeService(
            IHubContext<RealTimeHub> hubContext,
            IDeviceRepository deviceRepository,
            IProductionRepository productionRepository,
            IAlarmRepository alarmRepository,
            ICollectionRepository collectionRepository,
            ILoggerService logger)
        {
            _hubContext = hubContext;
            _deviceRepository = deviceRepository;
            _productionRepository = productionRepository;
            _alarmRepository = alarmRepository;
            _collectionRepository = collectionRepository;
            _logger = logger;
        }

        public async Task BroadcastDeviceStatusAsync()
        {
            try
            {
                var devices = await _deviceRepository.GetAllAsync();
                var deviceStatuses = new List<DeviceCurrentStatus>();

                foreach (var device in devices)
                {
                    var status = await GetDeviceCurrentStatusAsync(device.Id);
                    deviceStatuses.Add(status);
                }

                await _hubContext.Clients.All.SendAsync("ReceiveDeviceStatusUpdate", deviceStatuses);
                _logger.LogDebug($"Broadcasted device status update for {deviceStatuses.Count} devices");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to broadcast device status update");
            }
        }

        public async Task BroadcastProductionDataAsync()
        {
            try
            {
                var devices = await _deviceRepository.GetAllAsync();
                var productionData = new Dictionary<int, List<ProductionRecord>>();

                foreach (var device in devices)
                {
                    var todayProductions = await _productionRepository.GetByDeviceAndDateAsync(device.Id, DateTime.Today);
                    productionData[device.Id] = todayProductions.ToList();
                }

                await _hubContext.Clients.All.SendAsync("ReceiveProductionUpdate", productionData);
                _logger.LogDebug($"Broadcasted production data update for {devices.Count} devices");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to broadcast production data update");
            }
        }

        public async Task BroadcastAlarmDataAsync()
        {
            try
            {
                var activeAlarms = await _alarmRepository.GetActiveAlarmsAsync();
                var alarmData = activeAlarms.Select(a => new
                {
                    a.AlarmId,
                    a.DeviceCode,
                    a.AlarmType,
                    a.Severity,
                    a.Title,
                    a.Description,
                    a.AlarmStatus,
                    a.CreateTime
                }).ToList();

                await _hubContext.Clients.All.SendAsync("ReceiveAlarmUpdate", alarmData);
                _logger.LogDebug($"Broadcasted alarm data update for {alarmData.Count} alarms");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to broadcast alarm data update");
            }
        }

        public async Task SendDeviceUpdateAsync(int deviceId, DeviceCurrentStatus status)
        {
            try
            {
                await _hubContext.Clients.Group($"device_{deviceId}").SendAsync("ReceiveDeviceUpdate", status);
                _logger.LogDebug($"Sent device update for device {deviceId}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Failed to send device update for device {deviceId}");
            }
        }

        public async Task SendProductionUpdateAsync(int deviceId, ProductionRecord production)
        {
            try
            {
                await _hubContext.Clients.Group($"device_{deviceId}").SendAsync("ReceiveProductionUpdate", production);
                await _hubContext.Clients.All.SendAsync("ReceiveGlobalProductionUpdate", production);
                _logger.LogDebug($"Sent production update for device {deviceId}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Failed to send production update for device {deviceId}");
            }
        }

        public async Task SendAlarmUpdateAsync(Alarm alarm)
        {
            try
            {
                await _hubContext.Clients.Group($"device_{alarm.DeviceId}").SendAsync("ReceiveAlarmUpdate", alarm);
                await _hubContext.Clients.All.SendAsync("ReceiveGlobalAlarmUpdate", alarm);
                _logger.LogDebug($"Sent alarm update for alarm {alarm.AlarmId}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Failed to send alarm update for alarm {alarm.AlarmId}");
            }
        }

        public async Task BroadcastSystemHealthAsync()
        {
            try
            {
                var onlineDevices = await _deviceRepository.CountOnlineDevicesAsync();
                var activeAlarms = await _alarmRepository.CountActiveAlarmsAsync();
                var totalProductions = await _productionRepository.CountTodayProductionsAsync();

                var healthData = new
                {
                    Timestamp = DateTime.Now,
                    OnlineDevices = onlineDevices,
                    ActiveAlarms = activeAlarms,
                    TotalProductions = totalProductions,
                    SystemStatus = activeAlarms > 10 ? "Warning" : "Healthy"
                };

                await _hubContext.Clients.All.SendAsync("ReceiveSystemHealth", healthData);
                _logger.LogDebug($"Broadcasted system health update");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Failed to broadcast system health update");
            }
        }

        public async Task JoinDeviceGroupAsync(string connectionId, int deviceId)
        {
            try
            {
                lock (_lock)
                {
                    if (!_userDeviceGroups.ContainsKey(connectionId))
                    {
                        _userDeviceGroups[connectionId] = new HashSet<int>();
                    }
                    _userDeviceGroups[connectionId].Add(deviceId);
                }

                await _hubContext.Groups.AddToGroupAsync(connectionId, $"device_{deviceId}");
                _logger.LogDebug($"Connection {connectionId} joined device group {deviceId}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Failed to add connection {connectionId} to device group {deviceId}");
            }
        }

        public async Task LeaveDeviceGroupAsync(string connectionId, int deviceId)
        {
            try
            {
                lock (_lock)
                {
                    if (_userDeviceGroups.ContainsKey(connectionId))
                    {
                        _userDeviceGroups[connectionId].Remove(deviceId);
                        if (_userDeviceGroups[connectionId].Count == 0)
                        {
                            _userDeviceGroups.Remove(connectionId);
                        }
                    }
                }

                await _hubContext.Groups.RemoveFromGroupAsync(connectionId, $"device_{deviceId}");
                _logger.LogDebug($"Connection {connectionId} left device group {deviceId}");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Failed to remove connection {connectionId} from device group {deviceId}");
            }
        }

        public async Task JoinAllDevicesGroupAsync(string connectionId)
        {
            try
            {
                await _hubContext.Groups.AddToGroupAsync(connectionId, "all_devices");
                _logger.LogDebug($"Connection {connectionId} joined all devices group");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Failed to add connection {connectionId} to all devices group");
            }
        }

        public async Task LeaveAllDevicesGroupAsync(string connectionId)
        {
            try
            {
                await _hubContext.Groups.RemoveFromGroupAsync(connectionId, "all_devices");
                _logger.LogDebug($"Connection {connectionId} left all devices group");
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, $"Failed to remove connection {connectionId} from all devices group");
            }
        }

        private async Task<DeviceCurrentStatus> GetDeviceCurrentStatusAsync(int deviceId)
        {
            var device = await _deviceRepository.GetByIdAsync(deviceId);
            if (device == null)
                return new DeviceCurrentStatus();

            var latestCollection = await _collectionRepository.GetLatestDeviceStatusAsync(deviceId);
            
            return new DeviceCurrentStatus
            {
                DeviceId = deviceId,
                DeviceCode = device.DeviceCode,
                DeviceName = device.DeviceName,
                Status = latestCollection?.Status ?? "Unknown",
                IsRunning = latestCollection?.IsRunning ?? false,
                NCProgram = latestCollection?.NCProgram ?? "",
                CumulativeCount = latestCollection?.CumulativeCount ?? 0,
                OperatingMode = latestCollection?.OperatingMode ?? "",
                RecordTime = latestCollection?.RecordTime ?? DateTime.Now
            };
        }
    }

    // SignalR Hub for real-time communication
    public class RealTimeHub : Hub
    {
        private readonly IRealTimeService _realTimeService;
        private readonly ILoggerService _logger;

        public RealTimeHub(IRealTimeService realTimeService, ILoggerService logger)
        {
            _realTimeService = realTimeService;
            _logger = logger;
        }

        public override async Task OnConnectedAsync()
        {
            _logger.LogInformation($"Connection {Context.ConnectionId} connected to RealTimeHub");
            
            // Automatically join all devices group for new connections
            await _realTimeService.JoinAllDevicesGroupAsync(Context.ConnectionId);
            
            await base.OnConnectedAsync();
        }

        public override async Task OnDisconnectedAsync(Exception exception)
        {
            _logger.LogInformation($"Connection {Context.ConnectionId} disconnected from RealTimeHub");
            
            // Clean up device group memberships
            // Note: This is a simplified cleanup - in production you'd track which groups each user was in
            await _realTimeService.LeaveAllDevicesGroupAsync(Context.ConnectionId);
            
            await base.OnDisconnectedAsync(exception);
        }

        // Client methods that can be called from frontend
        public async Task JoinDeviceGroup(int deviceId)
        {
            await _realTimeService.JoinDeviceGroupAsync(Context.ConnectionId, deviceId);
            await Clients.Caller.SendAsync("JoinedDeviceGroup", deviceId);
        }

        public async Task LeaveDeviceGroup(int deviceId)
        {
            await _realTimeService.LeaveDeviceGroupAsync(Context.ConnectionId, deviceId);
            await Clients.Caller.SendAsync("LeftDeviceGroup", deviceId);
        }

        public async Task RequestSystemHealth()
        {
            await _realTimeService.BroadcastSystemHealthAsync();
        }

        public async Task RequestDeviceStatus()
        {
            await _realTimeService.BroadcastDeviceStatusAsync();
        }

        public async Task RequestProductionData()
        {
            await _realTimeService.BroadcastProductionDataAsync();
        }

        public async Task RequestAlarmData()
        {
            await _realTimeService.BroadcastAlarmDataAsync();
        }
    }
}