using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Nexus.Contracts.Devices;
using NexusIdentity.Api.Data;
using NexusIdentity.Api.Models;
using NexusIdentity.Api.Services;
using HealthStatus = Nexus.Contracts.Devices.HealthStatus;

namespace NexusIdentity.Api.Controllers;

[ApiController]
[Route("api/presence")]
public sealed class PresenceController : ControllerBase
{
    private readonly IdentityDbContext _db;
    private readonly DevicePresenceService _presence;

    public PresenceController(IdentityDbContext db, DevicePresenceService presence)
    {
        _db = db;
        _presence = presence;
    }

    [HttpGet]
    public async Task<IActionResult> GetPresence([FromQuery] string? organizationId, CancellationToken ct)
    {
        var now = DateTime.UtcNow;
        var query = _db.Devices.AsNoTracking().AsQueryable();
        if (!string.IsNullOrWhiteSpace(organizationId))
        {
            query = query.Where(d => d.OrganizationId == organizationId);
        }

        var devices = await query
            .Include(d => d.Organization)
            .Include(d => d.Presence)
            .ToListAsync(ct);

        var rows = devices.Select(d => MapDevicePresence(d, now, _presence)).ToList();
        return Ok(BuildSummary(now, rows, _presence.OfflineThresholdSeconds));
    }

    [HttpPost]
    public async Task<IActionResult> UpsertPresence([FromBody] DeviceHeartbeatRequest request, CancellationToken ct)
    {
        if (string.IsNullOrWhiteSpace(request.DeviceId))
        {
            return BadRequest(new { error = "DeviceId is required." });
        }

        var now = DateTime.UtcNow;
        var receivedAt = request.Timestamp?.UtcDateTime ?? now;
        var healthStatus = DevicePresenceService.ParseHealthStatus(request.Health);

        var device = await _db.Devices
            .Include(d => d.Organization)
            .Include(d => d.Presence)
            .FirstOrDefaultAsync(d => d.DeviceId == request.DeviceId, ct);

        if (device is null)
        {
            device = new DeviceEntity
            {
                DeviceId = request.DeviceId,
                FriendlyName = request.DeviceId,
                ComputerName = request.DeviceId,
                Owner = request.User ?? string.Empty,
                AgentVersion = request.AgentVersion,
                ShellVersion = request.ShellVersion,
                EnrollmentStatus = "Enrolled",
                RegisteredAtUtc = now,
            };
            _db.Devices.Add(device);
        }

        device.LastSeenUtc = receivedAt;
        device.Online = request.Online;
        device.LastHealth = DevicePresenceService.ToHealthLabel(healthStatus);
        device.AgentVersion = request.AgentVersion;
        device.ShellVersion = request.ShellVersion;
        if (!string.IsNullOrWhiteSpace(request.User))
        {
            device.Owner = request.User;
        }

        if (device.Presence is null)
        {
            device.Presence = new DevicePresenceEntity { DeviceId = device.DeviceId };
            _db.DevicePresence.Add(device.Presence);
        }

        device.Presence.Online = request.Online;
        device.Presence.HealthStatus = healthStatus;
        device.Presence.LastSeenUtc = receivedAt;
        device.Presence.LastHeartbeatUtc = receivedAt;
        device.Presence.UserName = request.User ?? device.Owner;
        device.Presence.OrganizationName = request.Organization ?? device.Organization?.Name ?? string.Empty;
        device.Presence.AgentVersion = request.AgentVersion;
        device.Presence.ShellVersion = request.ShellVersion;
        device.Presence.UpdatedAtUtc = now;

        _db.DeviceHeartbeats.Add(new DeviceHeartbeatEntity
        {
            Id = Guid.NewGuid(),
            DeviceId = device.DeviceId,
            ReceivedAtUtc = now,
            Online = request.Online,
            HealthStatus = healthStatus,
            UserName = request.User ?? device.Owner,
            OrganizationName = request.Organization ?? device.Organization?.Name ?? string.Empty,
            AgentVersion = request.AgentVersion,
            ShellVersion = request.ShellVersion,
        });

        await _db.SaveChangesAsync(ct);

        var row = MapDevicePresence(device, now, _presence);
        return Ok(new
        {
            acknowledged = true,
            receivedAtUtc = now,
            device = row,
        });
    }

    internal static PresenceDeviceRow MapDevicePresence(DeviceEntity device, DateTime utcNow, DevicePresenceService presence)
    {
        var lastSeen = device.Presence?.LastSeenUtc ?? device.LastSeenUtc;
        var online = presence.IsOnline(lastSeen, utcNow) && (device.Presence?.Online ?? device.Online);
        var health = online
            ? (device.Presence?.HealthStatus ?? DevicePresenceService.ParseHealthStatus(device.LastHealth)).ToString()
            : HealthStatus.Offline.ToString();

        return new PresenceDeviceRow
        {
            DeviceId = device.DeviceId,
            FriendlyName = device.FriendlyName,
            Owner = device.Presence?.UserName ?? device.Owner,
            OrganizationId = device.OrganizationId,
            OrganizationName = device.Presence?.OrganizationName ?? device.Organization?.Name ?? string.Empty,
            Online = online,
            HealthStatus = health,
            AgentVersion = device.Presence?.AgentVersion ?? device.AgentVersion,
            ShellVersion = device.Presence?.ShellVersion ?? device.ShellVersion,
            LastSeenUtc = lastSeen,
            LastHeartbeatUtc = device.Presence?.LastHeartbeatUtc ?? lastSeen,
        };
    }

    internal static PresenceSummaryResponse BuildSummary(DateTime utcNow, IReadOnlyList<PresenceDeviceRow> devices, int offlineThresholdSeconds) =>
        new()
        {
            GeneratedAtUtc = utcNow,
            OfflineThresholdSeconds = offlineThresholdSeconds,
            OnlineCount = devices.Count(d => d.Online),
            OfflineCount = devices.Count(d => !d.Online),
            WarningCount = devices.Count(d => d.Online && d.HealthStatus == HealthStatus.Warning.ToString()),
            CriticalCount = devices.Count(d => d.Online && d.HealthStatus == HealthStatus.Critical.ToString()),
            Devices = devices,
        };
}

public sealed class PresenceSummaryResponse
{
    public DateTime GeneratedAtUtc { get; init; }
    public int OfflineThresholdSeconds { get; init; }
    public int OnlineCount { get; init; }
    public int OfflineCount { get; init; }
    public int WarningCount { get; init; }
    public int CriticalCount { get; init; }
    public IReadOnlyList<PresenceDeviceRow> Devices { get; init; } = [];
}

public sealed class PresenceDeviceRow
{
    public required string DeviceId { get; init; }
    public required string FriendlyName { get; init; }
    public required string Owner { get; init; }
    public string? OrganizationId { get; init; }
    public required string OrganizationName { get; init; }
    public bool Online { get; init; }
    public required string HealthStatus { get; init; }
    public required string AgentVersion { get; init; }
    public required string ShellVersion { get; init; }
    public DateTime? LastSeenUtc { get; init; }
    public DateTime? LastHeartbeatUtc { get; init; }
}
