using ColaFlow.Modules.Identity.Application.Services;
using ColaFlow.Modules.Identity.Domain.Aggregates.Invitations;
using ColaFlow.Modules.Identity.Domain.Aggregates.Tenants;
using ColaFlow.Modules.Identity.Domain.Aggregates.Users;
using ColaFlow.Modules.Identity.Domain.Repositories;
using ColaFlow.Modules.Identity.Domain.Services;
using MediatR;
using Microsoft.Extensions.Logging;

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

public class InviteUserCommandHandler : IRequestHandler<InviteUserCommand, Guid>
{
    private readonly IInvitationRepository _invitationRepository;
    private readonly IUserRepository _userRepository;
    private readonly IUserTenantRoleRepository _userTenantRoleRepository;
    private readonly ITenantRepository _tenantRepository;
    private readonly ISecurityTokenService _tokenService;
    private readonly IEmailService _emailService;
    private readonly IEmailTemplateService _templateService;
    private readonly ILogger<InviteUserCommandHandler> _logger;

    public InviteUserCommandHandler(
        IInvitationRepository invitationRepository,
        IUserRepository userRepository,
        IUserTenantRoleRepository userTenantRoleRepository,
        ITenantRepository tenantRepository,
        ISecurityTokenService tokenService,
        IEmailService emailService,
        IEmailTemplateService templateService,
        ILogger<InviteUserCommandHandler> logger)
    {
        _invitationRepository = invitationRepository;
        _userRepository = userRepository;
        _userTenantRoleRepository = userTenantRoleRepository;
        _tenantRepository = tenantRepository;
        _tokenService = tokenService;
        _emailService = emailService;
        _templateService = templateService;
        _logger = logger;
    }

    public async Task<Guid> Handle(InviteUserCommand request, CancellationToken cancellationToken)
    {
        var tenantId = TenantId.Create(request.TenantId);
        var invitedBy = UserId.Create(request.InvitedBy);

        // Validate role
        if (!Enum.TryParse<TenantRole>(request.Role, out var role))
            throw new ArgumentException($"Invalid role: {request.Role}");

        // Check if tenant exists
        var tenant = await _tenantRepository.GetByIdAsync(tenantId, cancellationToken);
        if (tenant == null)
            throw new InvalidOperationException($"Tenant {request.TenantId} not found");

        // Check if inviter exists
        var inviter = await _userRepository.GetByIdAsync(invitedBy, cancellationToken);
        if (inviter == null)
            throw new InvalidOperationException($"Inviter user {request.InvitedBy} not found");

        var email = Email.Create(request.Email);

        // Check if user already exists in this tenant
        var existingUser = await _userRepository.GetByEmailAsync(tenantId, email, cancellationToken);
        if (existingUser != null)
        {
            // Check if user already has a role in this tenant
            var existingRole = await _userTenantRoleRepository.GetByUserAndTenantAsync(
                UserId.Create(existingUser.Id),
                tenantId,
                cancellationToken);

            if (existingRole != null)
                throw new InvalidOperationException($"User with email {request.Email} is already a member of this tenant");
        }

        // Check for existing pending invitation
        var existingInvitation = await _invitationRepository.GetPendingByEmailAndTenantAsync(
            request.Email,
            tenantId,
            cancellationToken);

        if (existingInvitation != null)
            throw new InvalidOperationException($"A pending invitation already exists for {request.Email} in this tenant");

        // Generate secure token
        var token = _tokenService.GenerateToken();
        var tokenHash = _tokenService.HashToken(token);

        // Create invitation
        var invitation = Invitation.Create(
            tenantId,
            request.Email,
            role,
            tokenHash,
            invitedBy);

        await _invitationRepository.AddAsync(invitation, cancellationToken);

        // Send invitation email
        var invitationLink = $"{request.BaseUrl}/accept-invitation?token={token}";
        var htmlBody = _templateService.RenderInvitationEmail(
            recipientName: request.Email.Split('@')[0], // Use email prefix as fallback name
            tenantName: tenant.Name.Value,
            inviterName: inviter.FullName.Value,
            invitationUrl: invitationLink);

        var emailMessage = new EmailMessage(
            To: request.Email,
            Subject: $"You've been invited to join {tenant.Name.Value} on ColaFlow",
            HtmlBody: htmlBody,
            PlainTextBody: $"You've been invited to join {tenant.Name.Value}. Click here to accept: {invitationLink}");

        var emailSuccess = await _emailService.SendEmailAsync(emailMessage, cancellationToken);

        if (!emailSuccess)
        {
            _logger.LogWarning(
                "Failed to send invitation email to {Email} for tenant {TenantId}",
                request.Email,
                request.TenantId);
        }
        else
        {
            _logger.LogInformation(
                "Invitation sent to {Email} for tenant {TenantId} with role {Role}",
                request.Email,
                request.TenantId,
                role);
        }

        return invitation.Id;
    }
}
