using Eshop.Application.Auth;
using Eshop.Application.Common;
using Eshop.Infrastructure.Persistence;
using Microsoft.AspNetCore.Identity;

namespace Eshop.Infrastructure.Identity;

public class AspNetIdentityUserAccountService(
    UserManager<UserEntity> users,
    SignInManager<UserEntity> signIn) : IUserAccountService
{
    public async Task<ServiceResult> RegisterAsync(
        string userName,
        string email,
        string password,
        CancellationToken cancellationToken)
    {
        var user = new UserEntity
        {
            UserName = userName.Trim(),
            Email = email.Trim().ToLower(),
        };

        var result = await users.CreateAsync(user, password);
        if (!result.Succeeded)
            return ServiceResult.Failure(result.Errors.Select(e => e.Description));

        await users.AddToRoleAsync(user, "User");
        return ServiceResult.Ok();
    }

    public async Task<UserProfileDto?> GetProfileAsync(string userId, CancellationToken cancellationToken)
    {
        var user = await users.FindByIdAsync(userId);
        if (user is null) return null;

        return new UserProfileDto(user.UserName!, user.Email!);
    }

    public async Task<ServiceResult> UpdateProfileAsync(
        string userId,
        UpdateProfileRequest request,
        CancellationToken cancellationToken)
    {
        var user = await users.FindByIdAsync(userId);
        if (user is null)
            return ServiceResult.Failure("User not found.");

        var errors = new List<string>();

        if (user.UserName != request.UserName.Trim())
        {
            var setName = await users.SetUserNameAsync(user, request.UserName.Trim());
            if (!setName.Succeeded)
                errors.AddRange(setName.Errors.Select(e => e.Description));
        }

        var normalizedEmail = request.Email.Trim().ToLower();
        if (user.Email != normalizedEmail)
        {
            var setEmail = await users.SetEmailAsync(user, normalizedEmail);
            if (!setEmail.Succeeded)
                errors.AddRange(setEmail.Errors.Select(e => e.Description));
        }

        if (!string.IsNullOrWhiteSpace(request.NewPassword))
        {
            if (string.IsNullOrWhiteSpace(request.CurrentPassword))
                return ServiceResult.Failure("Current password is required to set a new password.");

            var changePassword = await users.ChangePasswordAsync(
                user, request.CurrentPassword, request.NewPassword);

            if (!changePassword.Succeeded)
                errors.AddRange(changePassword.Errors.Select(e => e.Description));
        }

        if (errors.Count > 0)
            return ServiceResult.Failure(errors);

        await signIn.RefreshSignInAsync(user);
        return ServiceResult.Ok();
    }

    public async Task<ServiceResult> DeleteAccountAsync(string userId, CancellationToken cancellationToken)
    {
        var user = await users.FindByIdAsync(userId);
        if (user is null)
            return ServiceResult.Failure("User not found.");

        await signIn.SignOutAsync();
        var result = await users.DeleteAsync(user);
        return result.Succeeded
            ? ServiceResult.Ok()
            : ServiceResult.Failure(result.Errors.Select(e => e.Description));
    }

    public async Task<UserDataExportDto?> ExportUserDataAsync(string userId, CancellationToken cancellationToken)
    {
        var user = await users.FindByIdAsync(userId);
        if (user is null)
            return null;

        var roles = await users.GetRolesAsync(user);
        return new UserDataExportDto(
            user.Id,
            user.UserName!,
            user.Email!,
            roles.ToList(),
            DateTime.UtcNow);
    }
}
