using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Security.Claims;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;
using Auth.Contracts.DTOs.Auth;
using Blazored.LocalStorage;
using Microsoft.AspNetCore.Components.Authorization;

namespace Auth.RCL.Services
{
    public class ApiAuthenticationStateProvider : AuthenticationStateProvider
    {
        private readonly HttpClient _httpClient;
        private readonly ILocalStorageService _localStorage;

        public ApiAuthenticationStateProvider(
            HttpClient httpClient,
            ILocalStorageService localStorage
        )
        {
            _httpClient = httpClient;
            _localStorage = localStorage;
        }

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

            if (string.IsNullOrWhiteSpace(accessToken))
                return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));

            if (DateTime.Now.AddMinutes(4) > DateTime.Parse(expiryDate))
            {
                var tokenModel = JsonSerializer.Serialize(
                    new TokenModel()
                    {
                        RefreshToken = refreshToken,
                        AccessToken = accessToken,
                        Expiry = new DateTime(),
                    }
                );
                var response = await _httpClient.PostAsync(
                    "auth/refresh",
                    new StringContent(tokenModel, Encoding.UTF8, "application/json")
                );

                if (!response.IsSuccessStatusCode)
                {
                    _httpClient.DefaultRequestHeaders.Authorization = null;
                    MarkUserAsLoggedOut();
                    return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
                }

                var loginResult = JsonSerializer.Deserialize<LoginResult>(
                    await response.Content.ReadAsStringAsync(),
                    new JsonSerializerOptions { PropertyNameCaseInsensitive = true }
                );

                await _localStorage.SetItemAsync("authToken", loginResult.AccessToken);
                await _localStorage.SetItemAsync("refreshToken", loginResult.RefreshToken);
                await _localStorage.SetItemAsync("expiryDate", loginResult.Expiry);

                accessToken = loginResult.AccessToken;
            }

            // if (string.IsNullOrWhiteSpace(savedToken) || DateTime.Now > DateTime.Parse(expiryDate))
            // {
            //     return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));
            // }


            _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
                "Bearer",
                accessToken
            );

            var claims = ParseClaimsFromJwt(accessToken);
            if (claims is null)
                return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity()));

            return new AuthenticationState(new ClaimsPrincipal(new ClaimsIdentity(claims, "jwt")));
        }

        public async void MarkUserAsAuthenticated(string jwtToken)
        {
            var claims = ParseClaimsFromJwt(jwtToken);

            var authenticatedUser = new ClaimsPrincipal(new ClaimsIdentity(claims, "apiauth"));
            var authState = Task.FromResult(new AuthenticationState(authenticatedUser));
            NotifyAuthenticationStateChanged(authState);

            // var userId = authenticatedUser.Claims.FirstOrDefault(x => x.Type == "uid").Value;
        }

        public async Task MarkUserAsLoggedOut()
        {
            var anonymousUser = new ClaimsPrincipal(new ClaimsIdentity());
            var authState = Task.FromResult(new AuthenticationState(anonymousUser));
            NotifyAuthenticationStateChanged(authState);

            _httpClient.DefaultRequestHeaders.Authorization = null;
        }

        private IEnumerable<Claim> ParseClaimsFromJwt(string jwt)
        {
            try
            {
                var claims = new List<Claim>();
                var payload = jwt.Split('.')[1];
                var jsonBytes = ParseBase64WithoutPadding(payload);
                var keyValuePairs = JsonSerializer.Deserialize<Dictionary<string, object>>(
                    jsonBytes
                );

                keyValuePairs.TryGetValue(ClaimTypes.Role, out object roles);

                if (roles != null)
                {
                    if (roles.ToString().Trim().StartsWith("["))
                    {
                        var parsedRoles = JsonSerializer.Deserialize<string[]>(roles.ToString());

                        foreach (var parsedRole in parsedRoles)
                        {
                            claims.Add(new Claim(ClaimTypes.Role, parsedRole));
                        }
                    }
                    else
                    {
                        claims.Add(new Claim(ClaimTypes.Role, roles.ToString()));
                    }

                    keyValuePairs.Remove(ClaimTypes.Role);
                }

                claims.AddRange(
                    keyValuePairs.Select(kvp => new Claim(kvp.Key, kvp.Value.ToString()))
                );

                return claims;
            }
            catch
            {
                return null;
            }
        }

        private byte[] ParseBase64WithoutPadding(string base64)
        {
            switch (base64.Length % 4)
            {
                case 2:
                    base64 += "==";
                    break;
                case 3:
                    base64 += "=";
                    break;
            }
            return Convert.FromBase64String(base64);
        }
    }
}
