using Grpc.Net.Client;
using Hc900.Gateway;
using Hc900Crawler.Core.Application.Interfaces;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;

namespace Hc900Crawler.Infrastructure.Hc900;

public class Hc900Options
{
    public string GatewayAddress { get; set; } = "http://localhost:50051";
    public int PollIntervalMs { get; set; } = 1000;
}

public class Hc900GatewayClient : IHc900GatewayService, IDisposable
{
    private readonly string _address;
    private readonly ILogger<Hc900GatewayClient> _logger;
    private GrpcChannel? _channel;
    private ModbusGateway.ModbusGatewayClient? _client;

    private volatile bool _isConnected;
    private long _lastPollCount;

    public bool IsConnected => _isConnected;
    public long LastPollCount => _lastPollCount;

    public Hc900GatewayClient(IOptions<Hc900Options> options, ILogger<Hc900GatewayClient> logger)
    {
        _address = options.Value.GatewayAddress;
        _logger = logger;
    }

    public ModbusGateway.ModbusGatewayClient Client
    {
        get
        {
            EnsureConnected();
            return _client!;
        }
    }

    private void EnsureConnected()
    {
        if (_channel == null)
        {
            _channel = GrpcChannel.ForAddress(_address, new GrpcChannelOptions
            {
                MaxReceiveMessageSize = 64 * 1024 * 1024,
            });
            _client = new ModbusGateway.ModbusGatewayClient(_channel);
            _logger.LogInformation("gRPC channel created: {Address}", _address);
        }
    }

    public async Task<ReadTagsResponse> ReadTagsAsync(IEnumerable<string> tagNames)
    {
        var req = new ReadTagsRequest();
        req.TagNames.AddRange(tagNames);
        return await Client.ReadTagsAsync(req);
    }

    public async Task<ReadTagsResponse> ReadAllAsync()
        => await Client.ReadTagsAsync(new ReadTagsRequest());

    public async Task<WriteTagResponse> WriteTagAsync(string tagName, double value)
        => await Client.WriteTagAsync(new WriteTagRequest { TagName = tagName, Value = value });

    public async Task<HealthCheckResponse> GetHealthAsync()
    {
        var resp = await Client.HealthCheckAsync(new HealthCheckRequest());
        _isConnected = resp.Status == HealthCheckResponse.Types.ServingStatus.Serving;
        _lastPollCount = (long)resp.PollCount;
        return resp;
    }

    public async Task<ListTagsResponse> ListTagsAsync(string filter = "", int limit = 0)
        => await Client.ListTagsAsync(new ListTagsRequest { Filter = filter, Limit = limit });

    public void Dispose()
    {
        _channel?.ShutdownAsync().GetAwaiter().GetResult();
        _channel?.Dispose();
    }
}
