using System.Text.Json;
using Backend.Data;
using Microsoft.EntityFrameworkCore;

namespace Backend.Security;

public static class AdminPermissionCatalog
{
    public const string Dashboard = "dashboard";
    public const string Years = "years";
    public const string Nominations = "nominations";
    public const string Categories = "categories";
    public const string Candidates = "candidates";
    public const string Clips = "clips";
    public const string Risk = "risk";
    public const string Audit = "audit";
    public const string Analytics = "analytics";
    public const string Voting = "voting";
    public const string Winners = "winners";
    public const string Content = "content";
    public const string Settings = "settings";
    public const string Team = "team";

    public static readonly string[] AllPermissionKeys =
    [
        Dashboard,
        Years,
        Nominations,
        Categories,
        Candidates,
        Clips,
        Risk,
        Audit,
        Analytics,
        Voting,
        Winners,
        Content,
        Settings,
        Team,
    ];

    public static readonly string[] SeasonReadPermissionKeys =
    [
        Dashboard,
        Years,
        Nominations,
        Categories,
        Candidates,
        Clips,
        Analytics,
        Voting,
        Winners,
    ];

    public static bool IsKnownPermission(string permissionKey) =>
        AllPermissionKeys.Contains(permissionKey, StringComparer.OrdinalIgnoreCase);

    public static string[] DefaultPermissionKeys(string? role)
    {
        return AdminRoles.Normalize(role) switch
        {
            AdminRoles.Owner => AllPermissionKeys,
            AdminRoles.Creator => AllPermissionKeys,
            AdminRoles.Admin => AllPermissionKeys,
            AdminRoles.Member => [Dashboard, Nominations, Categories, Candidates, Clips, Voting, Content],
            AdminRoles.Reviewer => [Dashboard, Nominations, Clips, Risk, Audit],
            AdminRoles.OrganizationTeam => [Dashboard, Content, Analytics, Voting, Winners, Settings],
            AdminRoles.ContentAdmin => [Content, Settings],
            _ => [],
        };
    }

    public static string[] NormalizePermissionKeys(string? role, IEnumerable<string> permissionKeys)
    {
        var normalizedRole = AdminRoles.Normalize(role);
        var permissions = permissionKeys
            .Where(IsKnownPermission)
            .Distinct(StringComparer.OrdinalIgnoreCase)
            .ToList();

        if (normalizedRole is AdminRoles.Owner or AdminRoles.Creator)
        {
            return AllPermissionKeys;
        }

        if (DefaultPermissionKeys(normalizedRole).Contains(Settings, StringComparer.OrdinalIgnoreCase)
            && !permissions.Contains(Settings, StringComparer.OrdinalIgnoreCase))
        {
            permissions.Add(Settings);
        }

        var hasAnalytics = permissions.Contains(Analytics, StringComparer.OrdinalIgnoreCase);
        var hasWinners = permissions.Contains(Winners, StringComparer.OrdinalIgnoreCase);
        if ((hasAnalytics || hasWinners)
            && !permissions.Contains(Voting, StringComparer.OrdinalIgnoreCase))
        {
            permissions.Add(Voting);
        }

        return permissions
            .OrderBy(item => item)
            .ToArray();
    }

    public static async Task<string[]> GetPermissionKeysAsync(
        AwardsDbContext db,
        string? role,
        CancellationToken cancellationToken = default)
    {
        var normalizedRole = AdminRoles.Normalize(role);
        if (normalizedRole is AdminRoles.Owner or AdminRoles.Creator)
        {
            return AllPermissionKeys;
        }

        var fallback = DefaultPermissionKeys(normalizedRole);
        var overrideJson = await db.TeamRolePermissions
            .AsNoTracking()
            .Where(item => item.Role == normalizedRole)
            .Select(item => item.PermissionsJson)
            .FirstOrDefaultAsync(cancellationToken);

        if (string.IsNullOrWhiteSpace(overrideJson))
        {
            return fallback;
        }

        try
        {
            return NormalizePermissionKeys(normalizedRole, JsonSerializer.Deserialize<string[]>(overrideJson) ?? fallback);
        }
        catch (JsonException)
        {
            return fallback;
        }
    }

    public static async Task<bool> HasPermissionAsync(
        AwardsDbContext db,
        string? role,
        string permissionKey,
        CancellationToken cancellationToken = default)
    {
        var permissions = await GetPermissionKeysAsync(db, role, cancellationToken);
        return permissions.Contains(permissionKey, StringComparer.OrdinalIgnoreCase);
    }

    public static async Task<bool> HasAnyPermissionAsync(
        AwardsDbContext db,
        string? role,
        IEnumerable<string> permissionKeys,
        CancellationToken cancellationToken = default)
    {
        var permissions = await GetPermissionKeysAsync(db, role, cancellationToken);
        return permissionKeys.Any(permission => permissions.Contains(permission, StringComparer.OrdinalIgnoreCase));
    }
}
