using FastEndpoints;
using FluentValidation;
using Microsoft.EntityFrameworkCore;
using TrackQrApi.Data;
using TrackQrApi.Features.Auth.Common;

namespace TrackQrApi.Features.Auth.Endpoints;

public class ResetPasswordRequest
{
    public string Token { get; set; } = string.Empty;
    public string NewPassword { get; set; } = string.Empty;
}

public class ValidatorResetPassword : Validator<ResetPasswordRequest>
{
    public ValidatorResetPassword()
    {
        RuleFor(x => x.Token)
            .NotEmpty().WithMessage("Token is required");

        RuleFor(x => x.NewPassword)
            .NotEmpty().WithMessage("New password is required")
            .MinimumLength(8).WithMessage("Password must be at least 8 characters")
            .MaximumLength(100).WithMessage("Password must not exceed 100 characters");
    }
}

public class ResetPasswordEndpoint(AppDbContext db)
    : Endpoint<ResetPasswordRequest, MessageResponse>
{
    public override void Configure()
    {
        Post("/auth/reset");
        AllowAnonymous();
        Options(x => x.RequireRateLimiting("auth"));
    }

    public override async Task HandleAsync(ResetPasswordRequest req, CancellationToken ct)
    {
        // Find the token
        var resetToken = await db.PasswordResetTokens
            .Include(t => t.User)
            .FirstOrDefaultAsync(t => t.Token == req.Token, ct);

        if (resetToken == null)
        {
            await HttpContext.Response.SendAsync(
                new MessageResponse("Invalid or expired reset token"),
                400,
                cancellation: ct);
            return;
        }

        // Check if token is expired
        if (resetToken.ExpiresAt < DateTime.UtcNow)
        {
            await HttpContext.Response.SendAsync(
                new MessageResponse("Reset token has expired"),
                400,
                cancellation: ct);
            return;
        }

        // Check if token is already used
        if (resetToken.Used)
        {
            await HttpContext.Response.SendAsync(
                new MessageResponse("Reset token has already been used"),
                400,
                cancellation: ct);
            return;
        }

        // Update the user's password
        resetToken.User.PasswordHash = BCrypt.Net.BCrypt.HashPassword(req.NewPassword);
        resetToken.Used = true;

        await db.SaveChangesAsync(ct);

        Logger.LogInformation("Password reset successful for user: {Email}", resetToken.User.Email);

        await HttpContext.Response.SendAsync(
            new MessageResponse("Password has been reset successfully"),
            cancellation: ct);
    }
}