using System.Security.Claims;
using Jiaowu.Api.Domain.Identity;

namespace Jiaowu.Api.Infrastructure.Auth;

public interface ICurrentUserDataScope
{
    CurrentUserScope Current { get; }
}

public sealed record CurrentUserScope(
    Guid UserId,
    string? DisplayName,
    Guid? CollegeId,
    DataScope Scope,
    IReadOnlySet<string> Roles)
{
    public bool IsInRole(string role) => Roles.Contains(role);

    public bool CanAccessCollege(Guid collegeId) =>
        Scope == DataScope.All ||
        Scope == DataScope.College && CollegeId == collegeId;

    public Guid? RestrictedCollegeId => Scope switch
    {
        DataScope.All => null,
        DataScope.College => CollegeId ?? Guid.Empty,
        _ => Guid.Empty
    };
}

public sealed class CurrentUserDataScope(IHttpContextAccessor httpContextAccessor)
    : ICurrentUserDataScope
{
    public CurrentUserScope Current
    {
        get
        {
            var principal = httpContextAccessor.HttpContext?.User;
            var roles = new HashSet<string>(
                principal?.FindAll(ClaimTypes.Role).Select(x => x.Value) ?? [],
                StringComparer.OrdinalIgnoreCase);

            return new CurrentUserScope(
                ParseGuid(principal?.FindFirstValue(ClaimTypes.NameIdentifier)),
                principal?.FindFirstValue("display_name") ??
                    principal?.FindFirstValue(ClaimTypes.Name),
                ParseNullableGuid(principal?.FindFirstValue("college_id")),
                EffectiveDataScopeResolver.Resolve(roles),
                roles);
        }
    }

    private static Guid ParseGuid(string? value) =>
        Guid.TryParse(value, out var id) ? id : Guid.Empty;

    private static Guid? ParseNullableGuid(string? value) =>
        Guid.TryParse(value, out var id) ? id : null;
}

public static class EffectiveDataScopeResolver
{
    private static readonly IReadOnlyDictionary<string, DataScope> RoleScopes =
        new Dictionary<string, DataScope>(StringComparer.OrdinalIgnoreCase)
        {
            [SystemRoles.SuperAdmin] = DataScope.All,
            [SystemRoles.AcademicAdmin] = DataScope.All,
            [SystemRoles.Leader] = DataScope.All,
            [SystemRoles.CollegeAdmin] = DataScope.College,
            [SystemRoles.Counselor] = DataScope.Class,
            [SystemRoles.Teacher] = DataScope.Self,
            [SystemRoles.Student] = DataScope.Self
        };

    public static DataScope Resolve(IEnumerable<string> roles)
    {
        var effectiveScope = DataScope.Self;
        foreach (var role in roles)
        {
            if (RoleScopes.TryGetValue(role, out var roleScope) &&
                roleScope > effectiveScope)
            {
                effectiveScope = roleScope;
            }
        }

        return effectiveScope;
    }
}
