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

namespace Application.Auth.Commands;

public record ActivateAccountCommand(
    string ActivationCode,
    string Username,
    string Password
) : IRequest<ActivateAccountResult>;

public class ActivateAccountHandler(
    IStudentRepository studentRepository,
    IUserRepository userRepository,
    IPasswordService passwordService,
    IJwtService jwtService,
    IUnitOfWork unitOfWork
) : IRequestHandler<ActivateAccountCommand, ActivateAccountResult>
{
    public async Task<ActivateAccountResult> Handle(ActivateAccountCommand request, CancellationToken ct)
    {
        // Find student with matching activation code
        var students = await studentRepository.GetPendingActivationAsync(ct);
        var student = students.FirstOrDefault(s =>
            s.ActivationCodeHash != null &&
            passwordService.VerifyPassword(request.ActivationCode, s.ActivationCodeHash));

        if (student == null)
            return new ActivateAccountResult(false, null, null, "Codigo de activacion invalido o expirado");

        if (student.IsActivationExpired())
            return new ActivateAccountResult(false, null, null, "El codigo de activacion ha expirado");

        // Check if username already exists
        if (await userRepository.ExistsAsync(request.Username, ct))
            return new ActivateAccountResult(false, null, null, "El nombre de usuario ya existe");

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

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

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

        await userRepository.AddAsync(user, ct);

        // Clear activation code (mark as activated)
        // Need to re-fetch with tracking
        var trackedStudent = await studentRepository.GetByIdAsync(student.Id, ct);
        trackedStudent?.ClearActivationCode();

        await unitOfWork.SaveChangesAsync(ct);

        // Generate JWT token for auto-login
        var token = jwtService.GenerateToken(user);

        return new ActivateAccountResult(
            Success: true,
            Token: token,
            RecoveryCode: recoveryCode,
            Error: null
        );
    }

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