using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components.Authorization;
using System.Data;
using System.IdentityModel.Tokens.Jwt;
using System.Net.Http.Headers;
using System.Security.Claims;

namespace ReAct_PME.WebUI.ServicesUI
{
    /// <summary>
    /// Ajoute automatiquement le token JWT dans chaque requête HTTP
    /// Sécurise les appels API en envoyant le token avec HttpClient
    /// </summary>
    public class AuthorizedHttpClientHandler : DelegatingHandler
    {
        private readonly ILocalStorageService _localStorage;

        public AuthorizedHttpClientHandler(ILocalStorageService localStorage)
        {
            _localStorage = localStorage;
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            // Récupérer dynamiquement le token avant chaque requête
            var token = await _localStorage.GetItemAsync<string>("authToken");

            if (!string.IsNullOrEmpty(token))
            {
                request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);
            }

            return await base.SendAsync(request, cancellationToken);
        }
    }

    /// <summary>
    /// Gère l’état d’authentification et récupère l’utilisateur connecté	
    /// Centralise l’état de connexion et permet aux composants d’accéder aux rôles
    /// </summary>
    public class CustomAuthenticationStateProvider : AuthenticationStateProvider
    {
        public ClaimsPrincipal? User {get; private set;}
        public string? Token { get; private set; }

        public readonly ILocalStorageService _localStorage;
        private ClaimsPrincipal _anonymous = new(new ClaimsIdentity());

        public CustomAuthenticationStateProvider(ILocalStorageService localStorage)
        {
            _localStorage = localStorage;
        }

        public override async Task<AuthenticationState> GetAuthenticationStateAsync()
        {
            var token = await _localStorage.GetItemAsync<string>("authToken");

            if (string.IsNullOrWhiteSpace(token))
            {
                return new AuthenticationState(_anonymous);
            }

            User = GetUser(token);
            Token = token;

            return new AuthenticationState(User);
        }

        public void NotifyUserAuthentication(string token)
        {
            User = GetUser(token);
            NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(User)));
        }

        public void Deconnect()
        {
            _localStorage.RemoveItemsAsync(new List<string> { "authToken", "userInfo" });
            Token = null;
        }					   
        public void NotifyUserLogout()
        {
            NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(_anonymous)));
        }

        private ClaimsPrincipal GetUser(string token)
        {
            var identity = new ClaimsIdentity(ParseClaimsFromJwt(token).Select(c =>
                c.Type == "role" ? new Claim(ClaimTypes.Role, c.Value) : c), "jwt");
            var user = new ClaimsPrincipal(identity);
            return user;
        }

        private IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
        {
            var payload = jwt.Split('.')[1];
            var jsonBytes = Convert.FromBase64String(Base64UrlDecode(payload));
            var keyValues = System.Text.Json.JsonSerializer.Deserialize<Dictionary<string, object>>(jsonBytes);

            var claims = keyValues!.Select(kv => new Claim(kv.Key, kv.Value.ToString())).ToList();

            // Extraire les rôles
            if (keyValues.TryGetValue("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", out var rolesObj))
            {
                try
                {
                    var roles = System.Text.Json.JsonSerializer.Deserialize<List<string>>(rolesObj.ToString());
                    foreach (var role in roles!)
                    {
                        claims.Add(new Claim(ClaimTypes.Role, role));
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine($"Erreur lors de la conversion des rôles : {ex.Message}");
                }
            }

            return claims;
        }

        private string Base64UrlDecode(string input)
        {
            string output = input.Replace('-', '+').Replace('_', '/');
            switch (output.Length % 4)
            {
                case 2: output += "=="; break;
                case 3: output += "="; break;
            }
            return output;
        }
    }


    /// <summary>
    /// Stocke le token JWT --> Permet de savoir si l'utilisateur est connecté
    /// Décode les informations du JWT	--> Extrait FirstName, Roles, Email, etc.
    /// Vérifie les rôles	--> IsAdmin permet de restreindre l'accès à certaines pages
    /// Gère la connexion/déconnexion	--> Supprime les informations utilisateur après logout
    /// </summary>
    public class AuthService
    {
        public string? Token { get; private set; }
        public ClaimsPrincipal? User { get; private set; }

        public string? Username { get; set; }
        public string? FirstName { get; set; }
        public string? LastName { get; set; }
        public string? Email { get; set; }
        public string? ID { get; set; }
        public bool? IsAdmin { get => User?.IsInRole("Admin"); }
        public bool? IsManager { get => User?.IsInRole("Manager"); }
        public bool? IsOnlyUser { get => (IsAdmin == false && IsManager == false); }
        public List<string>? Roles { get; set; } = new();
        //public bool IsAuthenticated => !string.IsNullOrEmpty(Token);

        public void Deconnect()
        {
            Token = null;
            User = null;
            Username = null;
            FirstName = null;
            LastName = null;
            Email = null;
            ID = null;
            Roles = null;
        }

        public void SetToken(string jwt)
        {
            Token = jwt;

            var handler = new JwtSecurityTokenHandler();
            var decodedToken = handler.ReadJwtToken(jwt);
            var identity = new ClaimsIdentity(decodedToken.Claims, "jwt");

            FirstName = decodedToken.Claims.FirstOrDefault(c => c.Type == "FirstName")?.Value;
            LastName = decodedToken.Claims.FirstOrDefault(c => c.Type == "LastName")?.Value;
            Email = decodedToken.Claims.FirstOrDefault(c => c.Type == "Email")?.Value;
            ID = decodedToken.Claims.FirstOrDefault(c => c.Type == "ID")?.Value;

            User = new ClaimsPrincipal(identity);

            Roles = User.FindAll(ClaimTypes.Role).Select(r => r.Value).ToList();
        }
    }

}
                
