using ColaFlow.Modules.Identity.Application.Commands.SendVerificationEmail;
using ColaFlow.Modules.Identity.Application.Services;
using ColaFlow.Modules.Identity.Domain.Aggregates.Tenants;
using ColaFlow.Modules.Identity.Domain.Aggregates.Users;
using ColaFlow.Modules.Identity.Domain.Repositories;
using MediatR;
using Microsoft.Extensions.Configuration;

namespace ColaFlow.Modules.Identity.Application.Commands.RegisterTenant;

public class RegisterTenantCommandHandler(
    ITenantRepository tenantRepository,
    IUserRepository userRepository,
    IJwtService jwtService,
    IPasswordHasher passwordHasher,
    IRefreshTokenService refreshTokenService,
    IUserTenantRoleRepository userTenantRoleRepository,
    IMediator mediator,
    IConfiguration configuration)
    : IRequestHandler<RegisterTenantCommand, RegisterTenantResult>
{
    public async Task<RegisterTenantResult> Handle(
        RegisterTenantCommand request,
        CancellationToken cancellationToken)
    {
        // 1. Validate slug uniqueness
        var slug = TenantSlug.Create(request.TenantSlug);
        var slugExists = await tenantRepository.ExistsBySlugAsync(slug, cancellationToken);
        if (slugExists)
        {
            throw new InvalidOperationException($"Tenant slug '{request.TenantSlug}' is already taken");
        }

        // 2. Create tenant
        var plan = Enum.Parse<SubscriptionPlan>(request.SubscriptionPlan);
        var tenant = Tenant.Create(
            TenantName.Create(request.TenantName),
            slug,
            plan);

        await tenantRepository.AddAsync(tenant, cancellationToken);

        // 3. Create admin user with hashed password
        var hashedPassword = passwordHasher.HashPassword(request.AdminPassword);
        var adminUser = User.CreateLocal(
            TenantId.Create(tenant.Id),
            Email.Create(request.AdminEmail),
            hashedPassword,
            FullName.Create(request.AdminFullName));

        await userRepository.AddAsync(adminUser, cancellationToken);

        // 4. Assign TenantOwner role to admin user
        var tenantOwnerRole = UserTenantRole.Create(
            UserId.Create(adminUser.Id),
            TenantId.Create(tenant.Id),
            TenantRole.TenantOwner);

        await userTenantRoleRepository.AddAsync(tenantOwnerRole, cancellationToken);

        // 5. Generate JWT token with role
        var accessToken = jwtService.GenerateToken(adminUser, tenant, TenantRole.TenantOwner);

        // 6. Generate refresh token
        var refreshToken = await refreshTokenService.GenerateRefreshTokenAsync(
            adminUser,
            ipAddress: null,
            userAgent: null,
            cancellationToken);

        // 7. Send verification email (non-blocking)
        var baseUrl = configuration["App:BaseUrl"] ?? "http://localhost:3000";
        var sendEmailCommand = new SendVerificationEmailCommand(
            adminUser.Id,
            request.AdminEmail,
            baseUrl);

        // Fire and forget - don't wait for email to send
        _ = mediator.Send(sendEmailCommand, cancellationToken);

        // 8. Return result
        return new RegisterTenantResult(
            new Dtos.TenantDto
            {
                Id = tenant.Id,
                Name = tenant.Name.Value,
                Slug = tenant.Slug.Value,
                Status = tenant.Status.ToString(),
                Plan = tenant.Plan.ToString(),
                SsoEnabled = tenant.SsoConfig != null,
                SsoProvider = tenant.SsoConfig?.Provider.ToString(),
                CreatedAt = tenant.CreatedAt,
                UpdatedAt = tenant.UpdatedAt ?? tenant.CreatedAt
            },
            new Dtos.UserDto
            {
                Id = adminUser.Id,
                TenantId = tenant.Id,
                Email = adminUser.Email.Value,
                FullName = adminUser.FullName.Value,
                Status = adminUser.Status.ToString(),
                AuthProvider = adminUser.AuthProvider.ToString(),
                IsEmailVerified = adminUser.EmailVerifiedAt.HasValue,
                CreatedAt = adminUser.CreatedAt
            },
            accessToken,
            refreshToken);
    }
}
