using System.Text.Json;
using Microsoft.EntityFrameworkCore;
using NexusIdentity.Api.Data;
using NexusIdentity.Api.Models;

namespace NexusIdentity.Api.Services;

public sealed class PolicyService
{
    private readonly IdentityDbContext _db;

    public PolicyService(IdentityDbContext db)
    {
        _db = db;
    }

    public async Task<IReadOnlyList<PolicyEntity>> GetPoliciesAsync(string? organizationId, CancellationToken ct = default)
    {
        var query = _db.Policies.AsNoTracking().Where(p => p.IsEnabled);

        if (!string.IsNullOrWhiteSpace(organizationId))
        {
            query = query.Where(p => p.OrganizationId == null || p.OrganizationId == organizationId);
        }
        else
        {
            query = query.Where(p => p.OrganizationId == null);
        }

        return await query.OrderBy(p => p.Scope).ThenBy(p => p.Name).ToListAsync(ct);
    }

    public async Task<PolicyEvaluationResult> EvaluateForDeviceAsync(
        string deviceId,
        string? organizationId,
        string trustStatus,
        CancellationToken ct = default)
    {
        var policies = await GetPoliciesAsync(organizationId, ct);
        var results = new List<PolicyRuleResult>();

        foreach (var policy in policies)
        {
            var ruleResult = EvaluatePolicy(policy, trustStatus);
            results.Add(ruleResult);
        }

        var blocking = results.FirstOrDefault(r => !r.Passed && r.IsBlocking);
        return new PolicyEvaluationResult
        {
            DeviceId = deviceId,
            OrganizationId = organizationId,
            TrustStatus = trustStatus,
            OverallAllowed = blocking is null,
            BlockingReason = blocking?.Message,
            Results = results,
        };
    }

    private static PolicyRuleResult EvaluatePolicy(PolicyEntity policy, string trustStatus)
    {
        try
        {
            using var doc = JsonDocument.Parse(policy.RulesJson);
            var root = doc.RootElement;

            if (policy.Id == "policy-device-trust" &&
                root.TryGetProperty("requiredTrustStatus", out var requiredTrust))
            {
                var required = requiredTrust.GetString() ?? "Trusted";
                var passed = string.Equals(trustStatus, required, StringComparison.OrdinalIgnoreCase);
                return new PolicyRuleResult
                {
                    PolicyId = policy.Id,
                    PolicyName = policy.Name,
                    Passed = passed,
                    IsBlocking = true,
                    Message = passed ? null : $"Device trust is {trustStatus}; required: {required}.",
                };
            }

            if (policy.Id == "policy-enrollment-approval" &&
                root.TryGetProperty("requireAdminApproval", out var requireApproval) &&
                requireApproval.GetBoolean())
            {
                return new PolicyRuleResult
                {
                    PolicyId = policy.Id,
                    PolicyName = policy.Name,
                    Passed = true,
                    IsBlocking = false,
                    Message = "Enrollment approval enforced at registration time.",
                };
            }

            return new PolicyRuleResult
            {
                PolicyId = policy.Id,
                PolicyName = policy.Name,
                Passed = true,
                IsBlocking = false,
            };
        }
        catch
        {
            return new PolicyRuleResult
            {
                PolicyId = policy.Id,
                PolicyName = policy.Name,
                Passed = true,
                IsBlocking = false,
                Message = "Policy rules could not be parsed (POC fallback: allow).",
            };
        }
    }
}

public sealed class PolicyEvaluationResult
{
    public required string DeviceId { get; init; }
    public string? OrganizationId { get; init; }
    public required string TrustStatus { get; init; }
    public bool OverallAllowed { get; init; }
    public string? BlockingReason { get; init; }
    public IReadOnlyList<PolicyRuleResult> Results { get; init; } = [];
}

public sealed class PolicyRuleResult
{
    public required string PolicyId { get; init; }
    public required string PolicyName { get; init; }
    public bool Passed { get; init; }
    public bool IsBlocking { get; init; }
    public string? Message { get; init; }
}
