using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Authorization;

namespace SatelliteProvider.Api.Authentication;

// AZ-488: enforces a required permission from the `permissions` JWT claim.
// The claim may arrive as either:
//   - a JWT array claim (multiple ClaimType="permissions" entries — Microsoft.IdentityModel
//     splits arrays this way), OR
//   - a single JSON-array string ("[\"GPS\",\"FL\"]") when a producer mis-encodes.
// Both shapes are matched so the satellite-provider remains tolerant of upstream
// producers that do not split array claims out of the box.
public sealed class PermissionsRequirement : IAuthorizationRequirement
{
    public PermissionsRequirement(string requiredPermission)
    {
        if (string.IsNullOrWhiteSpace(requiredPermission))
        {
            throw new ArgumentException("Required permission must be a non-empty string.", nameof(requiredPermission));
        }

        RequiredPermission = requiredPermission;
    }

    public string RequiredPermission { get; }
}

public sealed class PermissionsAuthorizationHandler : AuthorizationHandler<PermissionsRequirement>
{
    public const string ClaimType = "permissions";

    protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, PermissionsRequirement requirement)
    {
        ArgumentNullException.ThrowIfNull(context);
        ArgumentNullException.ThrowIfNull(requirement);

        var user = context.User;
        if (user?.Identity?.IsAuthenticated != true)
        {
            return Task.CompletedTask;
        }

        if (UserHasPermission(user, requirement.RequiredPermission))
        {
            context.Succeed(requirement);
        }

        return Task.CompletedTask;
    }

    private static bool UserHasPermission(ClaimsPrincipal user, string requiredPermission)
    {
        foreach (var claim in user.FindAll(ClaimType))
        {
            if (string.Equals(claim.Value, requiredPermission, StringComparison.Ordinal))
            {
                return true;
            }

            if (TryReadJsonArray(claim.Value, out var values))
            {
                foreach (var value in values)
                {
                    if (string.Equals(value, requiredPermission, StringComparison.Ordinal))
                    {
                        return true;
                    }
                }
            }
        }

        return false;
    }

    private static bool TryReadJsonArray(string value, out IReadOnlyList<string> items)
    {
        items = Array.Empty<string>();
        if (string.IsNullOrWhiteSpace(value) || value[0] != '[')
        {
            return false;
        }

        try
        {
            using var document = JsonDocument.Parse(value);
            if (document.RootElement.ValueKind != JsonValueKind.Array)
            {
                return false;
            }

            var result = new List<string>(document.RootElement.GetArrayLength());
            foreach (var element in document.RootElement.EnumerateArray())
            {
                if (element.ValueKind == JsonValueKind.String)
                {
                    var text = element.GetString();
                    if (!string.IsNullOrEmpty(text))
                    {
                        result.Add(text);
                    }
                }
            }

            items = result;
            return result.Count > 0;
        }
        catch (JsonException)
        {
            return false;
        }
    }
}

public static class SatellitePermissions
{
    public const string Gps = "GPS";
    public const string UavUploadPolicy = "RequiresGpsPermission";
}
