using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using NexusIdentity.Api.Data;
using NexusIdentity.Api.Models;
using NexusIdentity.Api.Services;

namespace NexusIdentity.Api.Controllers;

[ApiController]
[Route("api/enrollment")]
public sealed class EnrollmentController : ControllerBase
{
    private readonly IdentityDbContext _db;
    private readonly PolicyService _policyService;

    public EnrollmentController(IdentityDbContext db, PolicyService policyService)
    {
        _db = db;
        _policyService = policyService;
    }

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

        if (string.IsNullOrWhiteSpace(request.OrganizationId))
        {
            return BadRequest(new { error = "OrganizationId is required." });
        }

        var org = await _db.Organizations.FirstOrDefaultAsync(o => o.Id == request.OrganizationId, ct);
        if (org is null)
        {
            return BadRequest(new { error = "Organization not found." });
        }

        var now = DateTime.UtcNow;
        var existing = await _db.EnrollmentRequests
            .FirstOrDefaultAsync(e => e.DeviceId == request.DeviceId && e.Status == "Pending", ct);

        EnrollmentRequestEntity enrollment;
        if (existing is not null)
        {
            enrollment = existing;
            enrollment.OrganizationId = request.OrganizationId;
            enrollment.UserName = request.UserName ?? string.Empty;
            enrollment.DeviceFriendlyName = request.DeviceFriendlyName ?? request.ComputerName ?? string.Empty;
            enrollment.ComputerName = request.ComputerName ?? string.Empty;
            enrollment.ShellVersion = request.ShellVersion ?? string.Empty;
            enrollment.AgentVersion = request.AgentVersion ?? string.Empty;
            enrollment.RequestedAtUtc = now;
        }
        else
        {
            enrollment = new EnrollmentRequestEntity
            {
                Id = Guid.NewGuid(),
                DeviceId = request.DeviceId,
                OrganizationId = request.OrganizationId,
                UserName = request.UserName ?? string.Empty,
                DeviceFriendlyName = request.DeviceFriendlyName ?? request.ComputerName ?? string.Empty,
                ComputerName = request.ComputerName ?? string.Empty,
                ShellVersion = request.ShellVersion ?? string.Empty,
                AgentVersion = request.AgentVersion ?? string.Empty,
                Status = "Pending",
                RequestedAtUtc = now,
            };
            _db.EnrollmentRequests.Add(enrollment);
        }

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

        if (device is null)
        {
            device = new DeviceEntity
            {
                DeviceId = request.DeviceId,
                OrganizationId = request.OrganizationId,
                ComputerName = request.ComputerName ?? string.Empty,
                FriendlyName = request.DeviceFriendlyName ?? request.ComputerName ?? string.Empty,
                Owner = request.UserName ?? string.Empty,
                Os = request.Os ?? "Windows",
                AgentVersion = request.AgentVersion ?? string.Empty,
                ShellVersion = request.ShellVersion ?? string.Empty,
                EnrollmentStatus = "Pending",
                RegisteredAtUtc = now,
                LastSeenUtc = now,
            };
            _db.Devices.Add(device);
            _db.DeviceTrust.Add(new DeviceTrustEntity
            {
                DeviceId = request.DeviceId,
                Status = "Pending",
                EvaluatedAtUtc = now,
                Reason = "Awaiting admin approval.",
            });
        }
        else
        {
            device.OrganizationId = request.OrganizationId;
            device.Owner = request.UserName ?? device.Owner;
            device.FriendlyName = request.DeviceFriendlyName ?? device.FriendlyName;
            device.EnrollmentStatus = "Pending";
            device.LastSeenUtc = now;

            if (device.Trust is null)
            {
                _db.DeviceTrust.Add(new DeviceTrustEntity
                {
                    DeviceId = request.DeviceId,
                    Status = "Pending",
                    EvaluatedAtUtc = now,
                });
            }
            else
            {
                device.Trust.Status = "Pending";
                device.Trust.EvaluatedAtUtc = now;
                device.Trust.Reason = "Awaiting admin approval.";
            }
        }

        await _db.SaveChangesAsync(ct);

        return Ok(new EnrollmentStatusResponse
        {
            DeviceId = request.DeviceId,
            Status = "Pending",
            RequestId = enrollment.Id.ToString(),
            RequestedAtUtc = enrollment.RequestedAtUtc,
            Message = "Enrollment request submitted. Awaiting admin approval.",
        });
    }

    [HttpGet("status/{deviceId}")]
    public async Task<IActionResult> GetStatus(string deviceId, CancellationToken ct)
    {
        var device = await _db.Devices
            .AsNoTracking()
            .Include(d => d.Trust)
            .FirstOrDefaultAsync(d => d.DeviceId == deviceId, ct);

        if (device is null)
        {
            return NotFound(new { error = "Device not found." });
        }

        var latestRequest = await _db.EnrollmentRequests
            .AsNoTracking()
            .Where(e => e.DeviceId == deviceId)
            .OrderByDescending(e => e.RequestedAtUtc)
            .FirstOrDefaultAsync(ct);

        var trustStatus = device.Trust?.Status ?? "Unknown";
        var evaluation = await _policyService.EvaluateForDeviceAsync(
            deviceId, device.OrganizationId, trustStatus, ct);

        return Ok(new EnrollmentStatusResponse
        {
            DeviceId = deviceId,
            Status = device.EnrollmentStatus,
            TrustStatus = trustStatus,
            RequestId = latestRequest?.Id.ToString(),
            RequestedAtUtc = latestRequest?.RequestedAtUtc,
            ProcessedAtUtc = latestRequest?.ProcessedAtUtc,
            PolicyAllowed = evaluation.OverallAllowed,
            PolicyMessage = evaluation.BlockingReason,
            Message = GetStatusMessage(device.EnrollmentStatus, trustStatus),
        });
    }

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

        var approve = request.Approve ?? true;
        var device = await _db.Devices
            .Include(d => d.Trust)
            .FirstOrDefaultAsync(d => d.DeviceId == request.DeviceId, ct);

        if (device is null)
        {
            return NotFound(new { error = "Device not found." });
        }

        var enrollment = await _db.EnrollmentRequests
            .Where(e => e.DeviceId == request.DeviceId && e.Status == "Pending")
            .OrderByDescending(e => e.RequestedAtUtc)
            .FirstOrDefaultAsync(ct);

        var now = DateTime.UtcNow;

        if (approve)
        {
            device.EnrollmentStatus = "Enrolled";
            if (device.Trust is null)
            {
                _db.DeviceTrust.Add(new DeviceTrustEntity
                {
                    DeviceId = request.DeviceId,
                    Status = "Trusted",
                    EvaluatedAtUtc = now,
                    Reason = request.Reason ?? "Approved by admin (POC).",
                });
            }
            else
            {
                device.Trust.Status = "Trusted";
                device.Trust.EvaluatedAtUtc = now;
                device.Trust.Reason = request.Reason ?? "Approved by admin (POC).";
            }

            if (enrollment is not null)
            {
                enrollment.Status = "Approved";
                enrollment.ProcessedAtUtc = now;
            }
        }
        else
        {
            device.EnrollmentStatus = "Blocked";
            if (device.Trust is not null)
            {
                device.Trust.Status = "Blocked";
                device.Trust.EvaluatedAtUtc = now;
                device.Trust.Reason = request.Reason ?? "Rejected by admin (POC).";
            }

            if (enrollment is not null)
            {
                enrollment.Status = "Rejected";
                enrollment.ProcessedAtUtc = now;
            }
        }

        await _db.SaveChangesAsync(ct);

        return Ok(new EnrollmentStatusResponse
        {
            DeviceId = request.DeviceId,
            Status = device.EnrollmentStatus,
            TrustStatus = device.Trust?.Status ?? "Unknown",
            ProcessedAtUtc = now,
            Message = approve ? "Enrollment approved." : "Enrollment rejected.",
        });
    }

    private static string GetStatusMessage(string enrollmentStatus, string trustStatus) =>
        enrollmentStatus switch
        {
            "Enrolled" when trustStatus == "Trusted" => "Device enrolled and trusted.",
            "Enrolled" => $"Device enrolled; trust: {trustStatus}.",
            "Pending" => "Enrollment pending admin approval.",
            "Blocked" => "Enrollment blocked.",
            _ => $"Status: {enrollmentStatus}.",
        };
}

public sealed class RemoteEnrollmentRequest
{
    public required string DeviceId { get; init; }
    public required string OrganizationId { get; init; }
    public string? UserName { get; init; }
    public string? DeviceFriendlyName { get; init; }
    public string? ComputerName { get; init; }
    public string? Os { get; init; }
    public string? ShellVersion { get; init; }
    public string? AgentVersion { get; init; }
}

public sealed class EnrollmentApprovalRequest
{
    public required string DeviceId { get; init; }
    public bool? Approve { get; init; }
    public string? Reason { get; init; }
}

public sealed class EnrollmentStatusResponse
{
    public required string DeviceId { get; init; }
    public required string Status { get; init; }
    public string? TrustStatus { get; init; }
    public string? RequestId { get; init; }
    public DateTime? RequestedAtUtc { get; init; }
    public DateTime? ProcessedAtUtc { get; init; }
    public bool? PolicyAllowed { get; init; }
    public string? PolicyMessage { get; init; }
    public string? Message { get; init; }
}
