using Grpc.Net.Client;
using Hc900.Gateway;
using Microsoft.Extensions.Logging;

namespace Hc900Crawler.Infrastructure.Hc900;

public class ControllerGrpcClientPool : IDisposable
{
    private readonly ControllerProcessManager _procMgr;
    private readonly ILogger<ControllerGrpcClientPool> _logger;
    private readonly Dictionary<string, (GrpcChannel Channel, ModbusGateway.ModbusGatewayClient Client)> _clients = new();
    private readonly object _lock = new();

    public ControllerGrpcClientPool(ControllerProcessManager procMgr, ILogger<ControllerGrpcClientPool> logger)
    {
        _procMgr = procMgr;
        _logger = logger;
    }

    public ModbusGateway.ModbusGatewayClient GetClient(string controllerId)
    {
        lock (_lock)
        {
            if (_clients.TryGetValue(controllerId, out var existing))
                return existing.Client;

            var ctrl = _procMgr.Config.Controllers.FirstOrDefault(c => c.Id == controllerId)
                       ?? throw new InvalidOperationException($"컨트롤러 {controllerId} 설정 없음");

            var address = $"http://localhost:{ctrl.GrpcPort}";
            var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
            {
                MaxReceiveMessageSize = 64 * 1024 * 1024,
            });
            var client = new ModbusGateway.ModbusGatewayClient(channel);
            _clients[controllerId] = (channel, client);
            _logger.LogInformation("[GrpcPool] 채널 생성: {Id} → {Addr}", controllerId, address);
            return client;
        }
    }

    public IEnumerable<string> EnabledControllerIds
        => _procMgr.Config.Controllers.Where(c => c.Enabled).Select(c => c.Id);

    public void RemoveClient(string controllerId)
    {
        lock (_lock)
        {
            if (_clients.TryGetValue(controllerId, out var pair))
            {
                pair.Channel.ShutdownAsync().GetAwaiter().GetResult();
                pair.Channel.Dispose();
                _clients.Remove(controllerId);
            }
        }
    }

    public void Dispose()
    {
        lock (_lock)
        {
            foreach (var (_, pair) in _clients)
            {
                try { pair.Channel.ShutdownAsync().GetAwaiter().GetResult(); pair.Channel.Dispose(); } catch { }
            }
            _clients.Clear();
        }
    }
}
