using System.Security.Claims;
using FluentResults;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using NexusReader.Application.DTOs.User;
using NexusReader.Data.Persistence;
using NexusReader.Domain.Entities;
using NexusReader.Application.Queries.User;
using MediatR;
using NexusReader.Application.Constants;
using NexusReader.Application.Abstractions.Services;
using Microsoft.AspNetCore.Components.Authorization;

namespace NexusReader.Web.Services;

public class ServerIdentityService : IIdentityService
{
    private readonly UserManager<NexusUser> _userManager;
    private readonly SignInManager<NexusUser> _signInManager;
    private readonly IHttpContextAccessor _httpContextAccessor;
    private readonly IMediator _mediator;
    private readonly INativeStorageService _storageService;
    private readonly AuthenticationStateProvider _authStateProvider;

    public event Func<Task>? OnStateInvalidated;

    public ServerIdentityService(
        UserManager<NexusUser> userManager,
        SignInManager<NexusUser> signInManager,
        IHttpContextAccessor httpContextAccessor,
        IMediator mediator,
        INativeStorageService storageService,
        AuthenticationStateProvider authStateProvider)
    {
        _userManager = userManager;
        _signInManager = signInManager;
        _httpContextAccessor = httpContextAccessor;
        _mediator = mediator;
        _storageService = storageService;
        _authStateProvider = authStateProvider;
    }

    public async Task<Result> LoginAsync(string email, string password, bool rememberMe = false) 
    {
        try
        {
            var user = await _userManager.FindByEmailAsync(email);
            if (user == null) return Result.Fail("Nieprawidłowy e-mail lub hasło.");

            // Check if account is locked
            if (await _userManager.IsLockedOutAsync(user)) return Result.Fail("Konto zostało zablokowane.");

            // Check password
            var isCorrect = await _userManager.CheckPasswordAsync(user, password);
            if (!isCorrect)
            {
                await _userManager.AccessFailedAsync(user);
                return Result.Fail("Nieprawidłowy e-mail lub hasło.");
            }

            // Reset access failed count on success
            await _userManager.ResetAccessFailedCountAsync(user);

            // In Blazor Interactive Server, we cannot use PasswordSignInAsync directly 
            // because headers are read-only once the circuit is established.
            // We return success here to indicate credentials are valid.
            // The UI will then perform a POST redirect to /account/login-form to set cookies.
            return Result.Ok();
        }
        catch (Exception ex)
        {
            return Result.Fail(new Error($"Błąd podczas weryfikacji poświadczeń: {ex.Message}").CausedBy(ex));
        }
    }

    public async Task<Result> LogoutAsync()
    {
        // Logout via SignalR is also problematic for cookie clearing.
        // The UI should redirect to /account/logout-form
        return Result.Ok();
    }

    public async Task<Result> RegisterAsync(string email, string password) 
    {
        try
        {
            var user = new NexusUser 
            { 
                UserName = email, 
                Email = email,
                SubscriptionPlanId = SubscriptionPlan.FreeId,
                TenantId = "global"
            };
            var result = await _userManager.CreateAsync(user, password);
            
            if (result.Succeeded)
            {
                // Similar to Login, we return success but don't sign in here.
                return Result.Ok();
            }
            
            return Result.Fail(result.Errors.Select(e => e.Description).FirstOrDefault() ?? "Rejestracja nie powiodła się.");
        }
        catch (Exception ex)
        {
            return Result.Fail(new Error($"Błąd podczas rejestracji na serwerze: {ex.Message}").CausedBy(ex));
        }
    }

    public Task<Result> RefreshTokenAsync() => Task.FromResult(Result.Ok());

    public async Task<Result<UserProfileDto>> GetProfileAsync()
    {
        var authState = await _authStateProvider.GetAuthenticationStateAsync();
        var user = authState.User;
        
        if (user == null || user.Identity?.IsAuthenticated != true)
        {
            user = _httpContextAccessor.HttpContext?.User;
        }

        if (user == null || user.Identity?.IsAuthenticated != true) return Result.Fail("Not authenticated.");

        var userId = user.FindFirstValue(ClaimTypes.NameIdentifier);
        if (userId == null) return Result.Fail("User ID not found.");

        var result = await _mediator.Send(new GetUserProfileQuery(userId));
        if (result.IsFailed) return Result.Fail(result.Errors);

        return Result.Ok(result.Value);
    }

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