using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Security.Cryptography;
using System.Text;
using FastEndpoints;
using FluentValidation;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
using TrackQrApi.Data;
using TrackQrApi.Features.Auth.Common;
using TrackQrApi.Features.Auth.Settings;
using TrackQrApi.Features.Email.Services;
using TrackQrApi.Models;

namespace TrackQrApi.Features.Auth.Endpoints;

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

public class RegisterValidator : Validator<RegisterRequest>
{
    public RegisterValidator()
    {
        RuleFor(x => x.Email)
            .NotEmpty().WithMessage("Email is required")
            .EmailAddress().WithMessage("Invalid email format")
            .MaximumLength(255).WithMessage("Email must not exceed 255 characters");

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

public class RegisterEndpoint(AppDbContext db, IOptions<JwtSettings> jwtSettings, IEmailService emailService)
    : Endpoint<RegisterRequest, AuthResponse>
{
    private readonly JwtSettings _jwtSettings = jwtSettings.Value;

    public override void Configure()
    {
        Post("/auth/register");
        AllowAnonymous();
        Options(x => x.RequireRateLimiting("auth"));
    }

    public override async Task HandleAsync(RegisterRequest req, CancellationToken ct)
    {
        var normalizedEmail = req.Email.ToLowerInvariant();

        if (await db.Users.AnyAsync(u => u.Email == normalizedEmail, ct))
        {
            await HttpContext.Response.SendAsync(new MessageResponse("Email already registered"), 409,
                cancellation: ct);
            return;
        }

        var user = new User
        {
            Id = Guid.NewGuid(),
            Email = normalizedEmail,
            PasswordHash = BCrypt.Net.BCrypt.HashPassword(req.Password),
            CreatedAt = DateTime.UtcNow
        };

        db.Users.Add(user);

        var workspace = new Workspace
        {
            Id = Guid.NewGuid(),
            OwnerUserId = user.Id,
            Name = "My Workspace",
            Plan = WorkspacePlan.Free,
            CreatedAt = DateTime.UtcNow
        };

        db.Workspaces.Add(workspace);

        // Create email verification token
        var tokenBytes = RandomNumberGenerator.GetBytes(32);
        var tokenString = Convert.ToHexString(tokenBytes).ToLowerInvariant();

        var verificationToken = new EmailVerificationToken
        {
            Id = Guid.NewGuid(),
            UserId = user.Id,
            Token = tokenString,
            ExpiresAt = DateTime.UtcNow.AddHours(24),
            CreatedAt = DateTime.UtcNow
        };

        db.EmailVerificationTokens.Add(verificationToken);
        await db.SaveChangesAsync(ct);

        // Send verification email (fire and forget)
        _ = emailService.SendEmailVerificationAsync(normalizedEmail, tokenString, ct);

        // Send welcome email
        _ = emailService.SendWelcomeEmailAsync(normalizedEmail, normalizedEmail.Split('@')[0], ct);

        Logger.LogInformation("User registered: {Email}", normalizedEmail);

        var response = GenerateAuthResponse(user);
        await HttpContext.Response.SendAsync(response, 201, cancellation: ct);
    }

    private AuthResponse GenerateAuthResponse(User user)
    {
        var expiresAt = DateTime.UtcNow.AddMinutes(_jwtSettings.ExpirationMinutes);
        var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Secret));
        var credentials = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);

        var claims = new[]
        {
            new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
            new Claim(JwtRegisteredClaimNames.Email, user.Email),
            new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
        };

        var token = new JwtSecurityToken(
            _jwtSettings.Issuer,
            _jwtSettings.Audience,
            claims,
            expires: expiresAt,
            signingCredentials: credentials
        );

        return new AuthResponse(
            new JwtSecurityTokenHandler().WriteToken(token),
            expiresAt,
            new UserInfo(user.Id, user.Email, user.VerifiedAt.HasValue)
        );
    }
}