using MediatR;
using Microsoft.AspNetCore.Identity;
using TakeoutSaaS.Application.Identity.Abstractions;
using TakeoutSaaS.Application.Identity.Commands;
using TakeoutSaaS.Domain.Identity.Entities;
using TakeoutSaaS.Domain.Identity.Enums;
using TakeoutSaaS.Domain.Identity.Repositories;
using TakeoutSaaS.Shared.Abstractions.Constants;
using TakeoutSaaS.Shared.Abstractions.Exceptions;

namespace TakeoutSaaS.Application.Identity.Handlers;

/// <summary>
/// 通过重置链接令牌重置管理员密码处理器。
/// </summary>
public sealed class ResetAdminPasswordByTokenCommandHandler(
    IAdminPasswordResetTokenStore tokenStore,
    IIdentityUserRepository userRepository,
    IPasswordHasher<IdentityUser> passwordHasher)
    : IRequestHandler<ResetAdminPasswordByTokenCommand>
{
    /// <inheritdoc />
    public async Task Handle(ResetAdminPasswordByTokenCommand request, CancellationToken cancellationToken)
    {
        // 1. 参数校验
        var token = request.Token?.Trim() ?? string.Empty;
        if (string.IsNullOrWhiteSpace(token))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "重置令牌不能为空");
        }

        var password = request.NewPassword?.Trim() ?? string.Empty;
        if (string.IsNullOrWhiteSpace(password))
        {
            throw new BusinessException(ErrorCodes.BadRequest, "新密码不能为空");
        }

        if (password.Length is < 6 or > 32)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "新密码长度需为 6~32 位");
        }

        // 2. 校验并消费令牌
        var userId = await tokenStore.ConsumeAsync(token, cancellationToken);
        if (!userId.HasValue || userId.Value == 0)
        {
            throw new BusinessException(ErrorCodes.BadRequest, "重置链接无效或已过期");
        }

        // 3. 获取用户（可更新，强制租户隔离）并写入新密码哈希
        var user = await userRepository.GetForUpdateAsync(userId.Value, cancellationToken)
            ?? throw new BusinessException(ErrorCodes.NotFound, "用户不存在");

        user.PasswordHash = passwordHasher.HashPassword(user, password);
        user.MustChangePassword = false;
        user.FailedLoginCount = 0;
        user.LockedUntil = null;
        if (user.Status == IdentityUserStatus.Locked)
        {
            user.Status = IdentityUserStatus.Active;
        }
        await userRepository.SaveChangesAsync(cancellationToken);
    }
}
