using ColaFlow.Modules.Identity.Application.Dtos;
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;

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

public class LoginCommandHandler(
    ITenantRepository tenantRepository,
    IUserRepository userRepository,
    IJwtService jwtService,
    IPasswordHasher passwordHasher,
    IRefreshTokenService refreshTokenService,
    IUserTenantRoleRepository userTenantRoleRepository)
    : IRequestHandler<LoginCommand, LoginResponseDto>
{
    public async Task<LoginResponseDto> Handle(LoginCommand request, CancellationToken cancellationToken)
    {
        // 1. Find tenant
        var slug = TenantSlug.Create(request.TenantSlug);
        var tenant = await tenantRepository.GetBySlugAsync(slug, cancellationToken);
        if (tenant == null)
        {
            throw new UnauthorizedAccessException("Invalid credentials");
        }

        // 2. Find user
        var email = Email.Create(request.Email);
        var user = await userRepository.GetByEmailAsync(TenantId.Create(tenant.Id), email, cancellationToken);
        if (user == null)
        {
            throw new UnauthorizedAccessException("Invalid credentials");
        }

        // 3. Verify password
        if (string.IsNullOrEmpty(user.PasswordHash) || !passwordHasher.VerifyPassword(request.Password, user.PasswordHash))
        {
            throw new UnauthorizedAccessException("Invalid credentials");
        }

        // 4. Get user's tenant role
        var userTenantRole = await userTenantRoleRepository.GetByUserAndTenantAsync(
            user.Id,
            tenant.Id,
            cancellationToken);

        if (userTenantRole is null)
        {
            throw new InvalidOperationException($"User {user.Id} has no role assigned for tenant {tenant.Id}");
        }

        // 5. Generate JWT token with role
        var accessToken = jwtService.GenerateToken(user, tenant, userTenantRole.Role);

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

        // 7. Update last login time
        user.RecordLogin();
        await userRepository.UpdateAsync(user, cancellationToken);

        // 8. Return result
        return new LoginResponseDto
        {
            User = new UserDto
            {
                Id = user.Id,
                TenantId = tenant.Id,
                Email = user.Email.Value,
                FullName = user.FullName.Value,
                Status = user.Status.ToString(),
                AuthProvider = user.AuthProvider.ToString(),
                IsEmailVerified = user.EmailVerifiedAt.HasValue,
                LastLoginAt = user.LastLoginAt,
                CreatedAt = user.CreatedAt
            },
            Tenant = new 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
            },
            AccessToken = accessToken,
            RefreshToken = refreshToken
        };
    }
}
