using Microsoft.JSInterop;
using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;

namespace Kalaha.Frontend.Security
{
    public class JwtAuthorizationHandler : DelegatingHandler
    {
        private readonly IJSRuntime _jsRuntime;
        private readonly IHttpClientFactory _httpClientFactory;
        private readonly string _baseAddress;

        public JwtAuthorizationHandler(IJSRuntime jsRuntime, IHttpClientFactory httpClientFactory, string baseAddress)
        {
            _jsRuntime = jsRuntime;
            _httpClientFactory = httpClientFactory;
            _baseAddress = baseAddress;
        }

        protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
        {
            // 1. Token aus dem LocalStorage holen
            var token = await _jsRuntime.InvokeAsync<string>("localStorage.getItem", "authToken");

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

            // 2. Den Request absenden
            var response = await base.SendAsync(request, cancellationToken);

            // 3. Falls 401 Unauthorized (Token abgelaufen) -> Refresh versuchen!
            if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
            {
                var refreshToken = await _jsRuntime.InvokeAsync<string>("localStorage.getItem", "refreshToken");

                if (!string.IsNullOrWhiteSpace(refreshToken))
                {
                    // Wir versuchen die Tokens zu erneuern
                    var success = await TryRefreshToken(token, refreshToken);

                    if (success)
                    {
                        // Falls erfolgreich: Neuen Token holen und Request wiederholen!
                        var newToken = await _jsRuntime.InvokeAsync<string>("localStorage.getItem", "authToken");
                        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", newToken);
                        
                        // Den alten Response verwerfen und den neuen senden
                        response.Dispose();
                        return await base.SendAsync(request, cancellationToken);
                    }
                }
            }

            return response;
        }

        private async Task<bool> TryRefreshToken(string? oldAccessToken, string? refreshToken)
        {
            try
            {
                // WICHTIG: Einen Client OHNE diesen Handler nutzen, sonst gibt's eine Endlosschleife!
                var refreshClient = _httpClientFactory.CreateClient();
                refreshClient.BaseAddress = new Uri(_baseAddress);

                var refreshRequest = new
                {
                    AccessToken = oldAccessToken,
                    RefreshToken = refreshToken
                };

                var response = await refreshClient.PostAsJsonAsync("api/auth/refresh", refreshRequest);

                if (response.IsSuccessStatusCode)
                {
                    var result = await response.Content.ReadFromJsonAsync<TokenResult>();
                    if (result != null)
                    {
                        // Neue Tokens im LocalStorage speichern
                        await _jsRuntime.InvokeVoidAsync("localStorage.setItem", "authToken", result.AccessToken);
                        await _jsRuntime.InvokeVoidAsync("localStorage.setItem", "refreshToken", result.RefreshToken);
                        return true;
                    }
                }
            }
            catch (Exception ex)
            {
                Console.WriteLine($"Refresh Token fehlgeschlagen: {ex.Message}");
            }

            // Falls Refresh fehlschlägt: User muss sich neu einloggen
            await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", "authToken");
            await _jsRuntime.InvokeVoidAsync("localStorage.removeItem", "refreshToken");
            return false;
        }

        private class TokenResult
        {
            public string AccessToken { get; set; } = "";
            public string RefreshToken { get; set; } = "";
        }
    }
}
