using Nexus.Contracts.Devices;
using NexusShell.Services;

namespace NexusShell.Agent;

public sealed class NexusHeartbeatService : IDisposable
{
    private readonly NexusApiClient _apiClient;
    private readonly NexusAgentConfig _config;
    private readonly NexusHealthService _health;
    private readonly ClientVersionInfo _shellVersion;
    private readonly Timer _timer;
    private int _heartbeatBusy;

    public NexusHeartbeatService(
        NexusApiClient apiClient,
        NexusAgentConfig config,
        NexusHealthService health,
        ClientVersionInfo shellVersion)
    {
        _apiClient = apiClient;
        _config = config;
        _health = health;
        _shellVersion = shellVersion;
        var interval = TimeSpan.FromSeconds(Math.Max(15, _config.HeartbeatIntervalSeconds));
        _timer = new Timer(static state => _ = ((NexusHeartbeatService)state!).TickAsync(), this, interval, interval);
    }

    public DateTimeOffset? LastHeartbeatUtc => _config.LastHeartbeatUtc;

    public async Task SendNowAsync(CancellationToken cancellationToken = default)
    {
        if (Interlocked.CompareExchange(ref _heartbeatBusy, 1, 0) != 0)
        {
            return;
        }

        try
        {
            var health = _health.CaptureOverallLabel();
            var request = new DeviceHeartbeatRequest
            {
                DeviceId = _config.DeviceId,
                Online = true,
                Health = health,
                AgentVersion = NexusAgentConfig.AgentVersion,
                ShellVersion = _shellVersion.Version,
            };

            await _apiClient.SendHeartbeatAsync(request, cancellationToken);
            _config.LastHeartbeatUtc = DateTimeOffset.UtcNow;
            _config.Save();
            NexusAgentLogger.Info("heartbeat", $"Heartbeat sent — health {health}");
        }
        finally
        {
            Interlocked.Exchange(ref _heartbeatBusy, 0);
        }
    }

    private async Task TickAsync()
    {
        try
        {
            await SendNowAsync();
        }
        catch (Exception ex)
        {
            NexusAgentLogger.Error("heartbeat", "Heartbeat tick failed", ex);
        }
    }

    public void Dispose() => _timer.Dispose();
}
