using System.Net.Http.Json;
using System.Security.Claims;
using ChessCubing.App.Models.Auth;
using Microsoft.AspNetCore.Components.Authorization;

namespace ChessCubing.App.Services;

public sealed class AppAuthenticationStateProvider(HttpClient httpClient) : AuthenticationStateProvider
{
    private static readonly AuthenticationState AnonymousState = new(new ClaimsPrincipal(new ClaimsIdentity()));

    private AuthenticationState? _cachedState;

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        if (_cachedState is not null)
        {
            return _cachedState;
        }

        _cachedState = await LoadStateAsync();
        return _cachedState;
    }

    public async Task RefreshAsync()
    {
        _cachedState = await LoadStateAsync();
        NotifyAuthenticationStateChanged(Task.FromResult(_cachedState));
    }

    public void SetAuthenticated(AuthSessionResponse session)
    {
        _cachedState = new AuthenticationState(BuildPrincipal(session));
        NotifyAuthenticationStateChanged(Task.FromResult(_cachedState));
    }

    public void SetAnonymous()
    {
        _cachedState = AnonymousState;
        NotifyAuthenticationStateChanged(Task.FromResult(_cachedState));
    }

    private async Task<AuthenticationState> LoadStateAsync()
    {
        try
        {
            var session = await httpClient.GetFromJsonAsync<AuthSessionResponse>("api/auth/session");
            if (session is null || !session.IsAuthenticated)
            {
                return AnonymousState;
            }

            return new AuthenticationState(BuildPrincipal(session));
        }
        catch
        {
            return AnonymousState;
        }
    }

    private static ClaimsPrincipal BuildPrincipal(AuthSessionResponse session)
    {
        if (!session.IsAuthenticated)
        {
            return AnonymousState.User;
        }

        var claims = new List<Claim>();

        if (!string.IsNullOrWhiteSpace(session.Subject))
        {
            claims.Add(new Claim("sub", session.Subject));
            claims.Add(new Claim(ClaimTypes.NameIdentifier, session.Subject));
        }

        if (!string.IsNullOrWhiteSpace(session.Username))
        {
            claims.Add(new Claim("preferred_username", session.Username));
            claims.Add(new Claim(ClaimTypes.Name, session.Username));
        }

        if (!string.IsNullOrWhiteSpace(session.Name))
        {
            claims.Add(new Claim("name", session.Name));
        }

        if (!string.IsNullOrWhiteSpace(session.Email))
        {
            claims.Add(new Claim("email", session.Email));
            claims.Add(new Claim(ClaimTypes.Email, session.Email));
        }

        foreach (var role in session.Roles.Where(role => !string.IsNullOrWhiteSpace(role)))
        {
            claims.Add(new Claim("role", role));
            claims.Add(new Claim(ClaimTypes.Role, role));
        }

        return new ClaimsPrincipal(new ClaimsIdentity(claims, "ChessCubingCookie"));
    }
}
