using System.Security.Claims;
using System.Text.Json;
using Microsoft.AspNetCore.Components.Authorization;
using NexusReader.Application.Abstractions.Services;
using NexusReader.Application.Constants;

using Microsoft.AspNetCore.Components;

namespace NexusReader.UI.Shared.Services;

public class NexusAuthenticationStateProvider : AuthenticationStateProvider
{
    private readonly INativeStorageService _storageService;
    private readonly PersistentComponentState _persistentState;
    
    // SECURITY NOTE: We currently store roles in local storage to persist state across refreshes.
    // In a production SaaS environment, consider using ProtectedBrowserStorage (Blazor Server) 
    // or encrypted storage/JWT claims validation to prevent client-side role tampering.
    private const string TokenKey = StorageKeys.AuthToken;

    public NexusAuthenticationStateProvider(INativeStorageService storageService, PersistentComponentState persistentState)
    {
        _storageService = storageService;
        _persistentState = persistentState;
    }

    public void ClearCache()
    {
        _cachedState = null;
        NotifyAuthenticationStateChanged(GetAuthenticationStateAsync());
    }

    private AuthenticationState? _cachedState;

    public override async Task<AuthenticationState> GetAuthenticationStateAsync()
    {
        try
        {
            if (_cachedState != null) return _cachedState;

            // 0. Hydrate state from SSR if available in PersistentComponentState
            if (_persistentState.TryTakeFromJson<UserInfo>("UserInfo", out var userInfo) && userInfo != null)
            {
                // Save to local storage for subsequent client-only transitions/refreshes
                await _storageService.SaveSecureString(StorageKeys.UserEmail, userInfo.Email);
                await _storageService.SaveSecureString(StorageKeys.UserTenant, userInfo.TenantId);
                await _storageService.SaveSecureString(StorageKeys.UserRoles, userInfo.Roles);

                _cachedState = CreateState(userInfo.Email, userInfo.TenantId, "FederatedHydration", userInfo.Roles);
                return _cachedState;
            }

            var tokenResult = await _storageService.GetSecureString(TokenKey);
            var token = tokenResult.IsSuccess ? tokenResult.Value : null;

            // 1. Try Token-based auth
            if (!string.IsNullOrWhiteSpace(token) && !JwtTokenValidator.IsExpired(token))
            {
                var emailResult = await _storageService.GetSecureString(StorageKeys.UserEmail);
                var tenantIdResult = await _storageService.GetSecureString(StorageKeys.UserTenant);
                var rolesResult = await _storageService.GetSecureString(StorageKeys.UserRoles);

                if (emailResult.IsSuccess && !string.IsNullOrEmpty(emailResult.Value))
                {
                    _cachedState = CreateState(
                        emailResult.Value, 
                        tenantIdResult.IsSuccess ? tenantIdResult.Value! : "unknown", 
                        "OpaqueBearer",
                        rolesResult.IsSuccess ? rolesResult.Value! : "");
                    return _cachedState;
                }
            }

            // 2. Try Cookie-based auth indicators
            var storedEmailResult = await _storageService.GetSecureString(StorageKeys.UserEmail);
            if (storedEmailResult.IsSuccess && !string.IsNullOrEmpty(storedEmailResult.Value))
            {
                var tenantIdResult = await _storageService.GetSecureString(StorageKeys.UserTenant);
                var rolesResult = await _storageService.GetSecureString(StorageKeys.UserRoles);
                _cachedState = CreateState(
                    storedEmailResult.Value, 
                    tenantIdResult.IsSuccess ? tenantIdResult.Value! : "unknown", 
                    "CookieAuth",
                    rolesResult.IsSuccess ? rolesResult.Value! : "");
                return _cachedState;
            }

            // 3. Fallback: If we have no local info, we might still have a cookie (e.g. after refresh or Google login).
            // We should return anonymous for now but trigger a background check if we're in WASM.
            // Wait! In WASM, the first GetAuthenticationStateAsync is awaited. 
            // We can do a quick check here if it's the first time.
            return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
        }
        catch (Exception)
        {
            return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
        }
    }

    private AuthenticationState CreateState(string email, string tenantId, string authType, string rolesStr = "")
    {
        var claims = new List<Claim>
        {
            new Claim(ClaimTypes.Name, email),
            new Claim(ClaimTypes.Email, email),
            new Claim("TenantId", tenantId)
        };
        
        if (!string.IsNullOrEmpty(rolesStr))
        {
            var roles = rolesStr.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
            foreach (var role in roles)
            {
                claims.Add(new Claim(ClaimTypes.Role, role.Trim()));
            }
        }
        
        var identity = new ClaimsIdentity(claims, authType);
        return new AuthenticationState(new ClaimsPrincipal(identity));
    }

    public void NotifyUserAuthentication(string email, string tenantId, string rolesStr = "")
    {
        _cachedState = CreateState(email, tenantId, "OpaqueBearer", rolesStr);
        NotifyAuthenticationStateChanged(Task.FromResult(_cachedState));
    }

    public void NotifyUserLogout()
    {
        _cachedState = null;
        var guest = new ClaimsPrincipal(new ClaimsIdentity());
        NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(guest)));
    }
}

public class UserInfo
{
    public string Email { get; set; } = string.Empty;
    public string TenantId { get; set; } = string.Empty;
    public string Roles { get; set; } = string.Empty;
}
