using System.Security.Claims;
using System.Text.Json;
using JobTrackerApi.Models;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Policy;
using Microsoft.AspNetCore.Identity;

namespace JobTrackerApi.Services;

public static class ProEntitlement
{
    public const string Policy = "Pro";
    public const string RequiredCode = "pro_required";
    public const string DisabledCode = "ai_disabled";
}

public sealed class ProEntitlementRequirement : IAuthorizationRequirement;

public sealed class ProEntitlementHandler(UserManager<ApplicationUser> users)
    : AuthorizationHandler<ProEntitlementRequirement>
{
    protected override async Task HandleRequirementAsync(
        AuthorizationHandlerContext context,
        ProEntitlementRequirement requirement)
    {
        var userId = context.User.FindFirstValue(ClaimTypes.NameIdentifier)
            ?? context.User.FindFirstValue("sub");
        var user = string.IsNullOrWhiteSpace(userId) ? null : await users.FindByIdAsync(userId);
        if (user is null || !AccountPlans.ForRoles(await users.GetRolesAsync(user)).Ai)
        {
            context.Fail(new AuthorizationFailureReason(this, ProEntitlement.RequiredCode));
            return;
        }

        if (!user.AiEnabled)
        {
            context.Fail(new AuthorizationFailureReason(this, ProEntitlement.DisabledCode));
            return;
        }

        context.Succeed(requirement);
    }
}

public sealed class ProEntitlementAuthorizationResultHandler : IAuthorizationMiddlewareResultHandler
{
    private readonly AuthorizationMiddlewareResultHandler _fallback = new();

    public async Task HandleAsync(
        RequestDelegate next,
        HttpContext context,
        AuthorizationPolicy policy,
        PolicyAuthorizationResult authorizeResult)
    {
        if (authorizeResult.Forbidden
            && policy.Requirements.OfType<ProEntitlementRequirement>().Any())
        {
            var aiDisabled = authorizeResult.AuthorizationFailure?.FailureReasons
                .Any(reason => reason.Message == ProEntitlement.DisabledCode) == true;
            context.Response.StatusCode = StatusCodes.Status403Forbidden;
            context.Response.ContentType = "application/json";
            await JsonSerializer.SerializeAsync(context.Response.Body, new
            {
                code = aiDisabled ? ProEntitlement.DisabledCode : ProEntitlement.RequiredCode,
                message = aiDisabled
                    ? "AI is disabled in your privacy settings."
                    : "This AI feature requires Pro.",
            }, cancellationToken: context.RequestAborted);
            return;
        }

        await _fallback.HandleAsync(next, context, policy, authorizeResult);
    }
}
