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

namespace NexusShell.Agent;

/// <summary>
/// Dispatches device heartbeats every 30 seconds to the platform API.
/// Payload: DeviceId, User, Organization, AgentVersion, ShellVersion, Health, Online, Timestamp.
/// </summary>
public sealed class NexusHeartbeatDispatcher : IDisposable
{
    public const int DefaultIntervalSeconds = 30;

    private readonly NexusApiClient _apiClient;
    private readonly NexusAgentConfig _config;
    private readonly NexusPresenceService _presence;
    private readonly NexusDeviceStatusService _deviceStatus;
    private readonly Timer _timer;
    private int _dispatchBusy;

    public NexusHeartbeatDispatcher(
        NexusApiClient apiClient,
        NexusAgentConfig config,
        NexusPresenceService presence,
        NexusDeviceStatusService deviceStatus)
    {
        _apiClient = apiClient;
        _config = config;
        _presence = presence;
        _deviceStatus = deviceStatus;

        var intervalSeconds = Math.Max(15, _config.HeartbeatIntervalSeconds > 0 ? _config.HeartbeatIntervalSeconds : DefaultIntervalSeconds);
        var interval = TimeSpan.FromSeconds(intervalSeconds);
        _timer = new Timer(static state => _ = ((NexusHeartbeatDispatcher)state!).TickAsync(), this, interval, interval);
    }

    public DateTimeOffset? LastHeartbeatUtc => _presence.LastHeartbeatUtc;

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

        try
        {
            var status = _deviceStatus.CaptureSnapshot();
            var timestamp = DateTimeOffset.UtcNow;
            var request = new DeviceHeartbeatRequest
            {
                DeviceId = status.DeviceId,
                User = status.User,
                Organization = status.Organization,
                Online = status.Online,
                Health = status.Health,
                AgentVersion = status.AgentVersion,
                ShellVersion = status.ShellVersion,
                Timestamp = timestamp,
            };

            var sent = await _apiClient.SendHeartbeatAsync(request, cancellationToken);
            if (sent)
            {
                _presence.RecordHeartbeat(timestamp, online: true);
                _config.LastHeartbeatUtc = timestamp;
                _config.Save();
                NexusAgentLogger.Info("heartbeat", $"Heartbeat dispatched — {status.Health} · {status.User} @ {status.Organization}");
            }
            else
            {
                _presence.SetOffline();
                NexusAgentLogger.Warn("heartbeat", "Heartbeat dispatch failed — backend unavailable");
            }
        }
        finally
        {
            Interlocked.Exchange(ref _dispatchBusy, 0);
        }
    }

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

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