using System.Net.Http.Json;
using Microsoft.AspNetCore.Components.Authorization;
using NexusReader.Application.Abstractions.Services;
using NexusReader.Application.DTOs.User;
using NexusReader.Application.Constants;
using FluentResults;

namespace NexusReader.UI.Shared.Services;

public class IdentityService : IIdentityService
{
    private readonly HttpClient _httpClient;
    private readonly INativeStorageService _storageService;
    private readonly AuthenticationStateProvider? _authStateProvider;
    private const string TokenKey = StorageKeys.AuthToken;
    private const string RefreshTokenKey = StorageKeys.RefreshToken;
    private Task<UserProfileDto?>? _profileTask;
    private UserProfileDto? _cachedProfile;
    private DateTime _lastFetchAttempt = DateTime.MinValue;

    public event Func<Task>? OnStateInvalidated;

    public IdentityService(
        HttpClient httpClient, 
        INativeStorageService storageService,
        AuthenticationStateProvider? authStateProvider = null)
    {
        _httpClient = httpClient;
        _storageService = storageService;
        _authStateProvider = authStateProvider;
    }

    public async Task<Result> RegisterAsync(string email, string password)
    {
        try
        {
            var response = await _httpClient.PostAsJsonAsync("identity/register", new { email, password });
            return response.IsSuccessStatusCode ? Result.Ok() : Result.Fail("Rejestracja nie powiodła się.");
        }
        catch (Exception ex)
        {
            return Result.Fail(new Error("Błąd sieci podczas rejestracji.").CausedBy(ex));
        }
    }

    public async Task<Result> LoginAsync(string email, string password, bool rememberMe = false)
    {
        try
        {
            var response = await _httpClient.PostAsJsonAsync("identity/login", new { email, password });
            
            if (response.IsSuccessStatusCode)
            {
                _cachedProfile = null;
                LoginResponse? result = null;
                try 
                {
                    result = await response.Content.ReadFromJsonAsync<LoginResponse>();
                }
                catch (System.Text.Json.JsonException ex) 
                {
                    return Result.Fail(new Error("Błąd przetwarzania odpowiedzi serwera.").CausedBy(ex));
                }

                if (result != null && !string.IsNullOrEmpty(result.AccessToken))
                {
                    await _storageService.SaveSecureString(TokenKey, result.AccessToken);
                    if (!string.IsNullOrEmpty(result.RefreshToken))
                    {
                        await _storageService.SaveSecureString(RefreshTokenKey, result.RefreshToken);
                    }
                }

                (_authStateProvider as NexusAuthenticationStateProvider)?.ClearCache();
                if (OnStateInvalidated != null) await OnStateInvalidated.Invoke();

                var profileResult = await GetProfileAsync();
                if (profileResult.IsSuccess)
                {
                    var profile = profileResult.Value;
                    await _storageService.SaveSecureString(StorageKeys.UserEmail, profile.Email);
                    await _storageService.SaveSecureString(StorageKeys.UserTenant, profile.TenantId.ToString());
                    
                    var rolesStr = string.Join(",", profile.Roles ?? Array.Empty<string>());
                    await _storageService.SaveSecureString(StorageKeys.UserRoles, rolesStr);
                    
                    (_authStateProvider as NexusAuthenticationStateProvider)?.NotifyUserAuthentication(profile.Email, profile.TenantId.ToString(), rolesStr);
                }
                else
                {
                    (_authStateProvider as NexusAuthenticationStateProvider)?.NotifyUserAuthentication(email, "unknown", "");
                }
                
                return Result.Ok();
            }
            return Result.Fail("Nieprawidłowy e-mail lub hasło.");
        }
        catch (Exception ex)
        {
            return Result.Fail(new Error("Błąd połączenia z serwerem.").CausedBy(ex));
        }
    }

    public async Task<Result> LogoutAsync()
    {
        try
        {
            _cachedProfile = null;
            if (System.OperatingSystem.IsBrowser())
            {
                await _storageService.SaveSecureString(TokenKey, "");
                await _storageService.SaveSecureString(RefreshTokenKey, "");
                await _storageService.SaveSecureString(StorageKeys.UserEmail, "");
                await _storageService.SaveSecureString(StorageKeys.UserTenant, "");
                await _storageService.SaveSecureString(StorageKeys.UserRoles, "");
            }
            
            if (OnStateInvalidated != null) await OnStateInvalidated.Invoke();
            (_authStateProvider as NexusAuthenticationStateProvider)?.ClearCache();
            (_authStateProvider as NexusAuthenticationStateProvider)?.NotifyUserLogout();
            
            return Result.Ok();
        }
        catch (Exception ex)
        {
            return Result.Fail(new Error("Błąd podczas wylogowywania.").CausedBy(ex));
        }
    }
    
    public async Task<Result<UserProfileDto>> GetProfileAsync()
    {
        if (_cachedProfile != null) 
        {
            return Result.Ok(_cachedProfile);
        }
        
        if (_profileTask != null) 
        {
            var p = await _profileTask;
            return p != null ? Result.Ok(p) : Result.Fail("Nie znaleziono profilu.");
        }

        _profileTask = GetProfileInternalAsync();
        var result = await _profileTask;
        return result != null ? Result.Ok(result) : Result.Fail("Błąd podczas pobierania profilu.");
    }



    private async Task<UserProfileDto?> GetProfileInternalAsync()
    {
        if (!System.OperatingSystem.IsBrowser()) 
        {
            return null;
        }

        if (DateTime.UtcNow - _lastFetchAttempt < TimeSpan.FromSeconds(5))
        {
            return null;
        }

        _lastFetchAttempt = DateTime.UtcNow;
        try
        {
            var response = await _httpClient.GetAsync("identity/profile");
            
            if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
            {
                await LogoutAsync();
                return null;
            }

            if (response.IsSuccessStatusCode)
            {
                var profile = await response.Content.ReadFromJsonAsync<UserProfileDto>();
                if (profile != null)
                {
                    _cachedProfile = profile;
                    await _storageService.SaveSecureString(StorageKeys.UserEmail, profile.Email);
                    await _storageService.SaveSecureString(StorageKeys.UserTenant, profile.TenantId.ToString());
                    
                    var rolesStr = string.Join(",", profile.Roles ?? Array.Empty<string>());
                    await _storageService.SaveSecureString(StorageKeys.UserRoles, rolesStr);
                    
                    (_authStateProvider as NexusAuthenticationStateProvider)?.NotifyUserAuthentication(profile.Email, profile.TenantId.ToString(), rolesStr);
                }
                return profile;
            }
            
            return null;
        }
        catch
        {
            return null;
        }
        finally
        {
            _profileTask = null;
        }
    }

    public async Task<Result> RefreshTokenAsync()
    {
        try
        {
            var result = await _storageService.GetSecureString(RefreshTokenKey);
            var refreshToken = result.IsSuccess ? result.Value : null;

            if (string.IsNullOrEmpty(refreshToken)) return Result.Fail("Brak tokena odświeżania.");

            var response = await _httpClient.PostAsJsonAsync("identity/refresh", new { refreshToken });

            if (response.IsSuccessStatusCode)
            {
                var loginResult = await response.Content.ReadFromJsonAsync<LoginResponse>();
                if (loginResult != null && !string.IsNullOrEmpty(loginResult.AccessToken))
                {
                    await _storageService.SaveSecureString(TokenKey, loginResult.AccessToken);
                    if (!string.IsNullOrEmpty(loginResult.RefreshToken))
                    {
                        await _storageService.SaveSecureString(RefreshTokenKey, loginResult.RefreshToken);
                    }

                    _cachedProfile = null;
                    (_authStateProvider as NexusAuthenticationStateProvider)?.ClearCache();
                    if (OnStateInvalidated != null) await OnStateInvalidated.Invoke();

                    var profileResult = await GetProfileAsync();
                    if (profileResult.IsSuccess)
                    {
                        var profile = profileResult.Value;
                        await _storageService.SaveSecureString(StorageKeys.UserEmail, profile.Email);
                        await _storageService.SaveSecureString(StorageKeys.UserTenant, profile.TenantId.ToString());
                        
                        var rolesStr = string.Join(",", profile.Roles ?? Array.Empty<string>());
                        await _storageService.SaveSecureString(StorageKeys.UserRoles, rolesStr);
                        
                        (_authStateProvider as NexusAuthenticationStateProvider)?.NotifyUserAuthentication(profile.Email, profile.TenantId.ToString(), rolesStr);
                    }

                    return Result.Ok();
                }
            }
            return Result.Fail("Sesja wygasła.");
        }
        catch (Exception ex)
        {
            return Result.Fail(new Error("Błąd odświeżania sesji.").CausedBy(ex));
        }
    }

    public void ClearCache()
    {
        _cachedProfile = null;
        if (OnStateInvalidated != null)
        {
            _ = Task.Run(async () =>
            {
                try
                {
                    await OnStateInvalidated.Invoke();
                }
                catch
                {
                    // Ignore exceptions from event handlers
                }
            });
        }
    }

    private class LoginResponse
    {
        public string TokenType { get; set; } = string.Empty;
        public string AccessToken { get; set; } = string.Empty;
        public int ExpiresIn { get; set; }
        public string RefreshToken { get; set; } = string.Empty;
    }
}
