using System.Security.Cryptography;
using Application.Auth.DTOs;
using Domain.Entities;
using Domain.Ports.Repositories;
using Domain.ValueObjects;
using MediatR;

namespace Application.Auth.Commands;

public record RegisterCommand(
    string Username,
    string Password,
    string? Name = null,
    string? Email = null
) : IRequest<AuthResponse>;

public class RegisterCommandHandler(
    IUserRepository userRepository,
    IStudentRepository studentRepository,
    IPasswordService passwordService,
    IJwtService jwtService,
    IUnitOfWork unitOfWork
) : IRequestHandler<RegisterCommand, AuthResponse>
{
    public async Task<AuthResponse> Handle(RegisterCommand request, CancellationToken ct)
    {
        // Check if username already exists
        if (await userRepository.ExistsAsync(request.Username, ct))
            return new AuthResponse(false, Error: "El nombre de usuario ya existe");

        // Validate password strength
        if (request.Password.Length < 6)
            return new AuthResponse(false, Error: "La contrasena debe tener al menos 6 caracteres");

        // Create student if name and email are provided
        Student? student = null;
        if (!string.IsNullOrWhiteSpace(request.Name) && !string.IsNullOrWhiteSpace(request.Email))
        {
            try
            {
                var email = Email.Create(request.Email);
                student = new Student(request.Name, email);
                studentRepository.Add(student);
                await unitOfWork.SaveChangesAsync(ct);
            }
            catch (Exception ex)
            {
                return new AuthResponse(false, Error: ex.Message);
            }
        }

        // Generate recovery code (12 chars alphanumeric)
        var recoveryCode = GenerateRecoveryCode();
        var recoveryCodeHash = passwordService.HashPassword(recoveryCode);

        // Create user with recovery code
        var passwordHash = passwordService.HashPassword(request.Password);
        var user = User.Create(
            request.Username,
            passwordHash,
            recoveryCodeHash,
            UserRoles.Student,
            student?.Id
        );

        await userRepository.AddAsync(user, ct);
        await unitOfWork.SaveChangesAsync(ct);

        // Generate token
        var token = jwtService.GenerateToken(user);

        return new AuthResponse(
            Success: true,
            Token: token,
            User: new UserInfo(
                user.Id,
                user.Username,
                user.Role,
                user.StudentId,
                student?.Name
            ),
            RecoveryCode: recoveryCode // Show only once!
        );
    }

    private static string GenerateRecoveryCode()
    {
        const string chars = "ABCDEFGHJKLMNPQRSTUVWXYZ23456789";
        var bytes = RandomNumberGenerator.GetBytes(12);
        return new string(bytes.Select(b => chars[b % chars.Length]).ToArray());
    }
}
