using System.Security.Cryptography;
using FastEndpoints;
using FluentValidation;
using Microsoft.EntityFrameworkCore;
using TrackQrApi.Data;
using TrackQrApi.Features.Auth.Common;
using TrackQrApi.Features.Email.Services;
using TrackQrApi.Models;

namespace TrackQrApi.Features.Auth.Endpoints;

public class ForgotPasswordRequest
{
    public string Email { get; set; } = string.Empty;
}

public class ForgotPasswordValidator : Validator<ForgotPasswordRequest>
{
    public ForgotPasswordValidator()
    {
        RuleFor(x => x.Email)
            .NotEmpty().WithMessage("Email is required")
            .EmailAddress().WithMessage("Invalid email format");
    }
}

public class ForgotPasswordEndpoint(AppDbContext db, IEmailService emailService)
    : Endpoint<ForgotPasswordRequest, MessageResponse>
{
    public override void Configure()
    {
        Post("/auth/forgot");
        AllowAnonymous();
        Options(x => x.RequireRateLimiting("auth"));
    }

    public override async Task HandleAsync(ForgotPasswordRequest req, CancellationToken ct)
    {
        var normalizedEmail = req.Email.ToLowerInvariant();
        var user = await db.Users.FirstOrDefaultAsync(u => u.Email == normalizedEmail, ct);

        if (user != null)
        {
            // Invalidate any existing tokens for this user
            var existingTokens = await db.PasswordResetTokens
                .Where(t => t.UserId == user.Id && !t.Used)
                .ToListAsync(ct);

            foreach (var token in existingTokens) token.Used = true;

            // Generate new token
            var resetToken = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32))
                .Replace("+", "-").Replace("/", "_").TrimEnd('=');

            var passwordResetToken = new PasswordResetToken
            {
                Id = Guid.NewGuid(),
                UserId = user.Id,
                Token = resetToken,
                ExpiresAt = DateTime.UtcNow.AddHours(1),
                Used = false,
                CreatedAt = DateTime.UtcNow
            };

            db.PasswordResetTokens.Add(passwordResetToken);
            await db.SaveChangesAsync(ct);

            // Send password reset email
            try
            {
                await emailService.SendPasswordResetEmailAsync(normalizedEmail, resetToken, ct);
            }
            catch (Exception ex)
            {
                Logger.LogError(ex, "Failed to send password reset email to {Email}", normalizedEmail);
                // Don't fail the request - still return success to prevent email enumeration
            }
        }
        else
        {
            Logger.LogInformation("Password reset requested for non-existent email: {Email}", normalizedEmail);
        }

        // Always return success to prevent email enumeration
        await HttpContext.Response.SendAsync(
            new MessageResponse("If the email exists, a reset link will be sent"),
            cancellation: ct);
    }
}