using System.Net;
using MemberCenter.Application.Abstractions;
using MemberCenter.Infrastructure.Identity;
using Microsoft.AspNetCore.Identity;

namespace MemberCenter.Infrastructure.Services;

public sealed class AccountEmailService : IAccountEmailService
{
    private readonly ISecuritySettingsService _securitySettingsService;
    private readonly IAccountEmailTemplateService _templateService;
    private readonly IAuditLogWriter _auditLogWriter;
    private readonly IEmailSender _emailSender;
    private readonly UserManager<ApplicationUser> _userManager;

    public AccountEmailService(
        ISecuritySettingsService securitySettingsService,
        IAccountEmailTemplateService templateService,
        IAuditLogWriter auditLogWriter,
        IEmailSender emailSender,
        UserManager<ApplicationUser> userManager)
    {
        _securitySettingsService = securitySettingsService;
        _templateService = templateService;
        _auditLogWriter = auditLogWriter;
        _emailSender = emailSender;
        _userManager = userManager;
    }

    public async Task SendVerificationEmailAsync(Guid userId, string? fallbackBaseUrl = null)
    {
        var user = await FindUserAsync(userId);
        var settings = await _securitySettingsService.GetAsync();
        var baseUrl = ResolveBaseUrl(settings.PublicBaseUrl, fallbackBaseUrl);
        var token = await _userManager.GenerateEmailConfirmationTokenAsync(user);
        var link = $"{baseUrl}/account/verifyemail?email={Uri.EscapeDataString(user.Email ?? string.Empty)}&token={Uri.EscapeDataString(token)}";
        var template = _templateService.BuildVerificationEmail(WebUtility.HtmlEncode(link));

        await _emailSender.SendAsync(user.Email ?? string.Empty, template.Subject, template.TextBody, template.HtmlBody);
        await _auditLogWriter.WriteAsync("user", user.Id, "account.verification_email_sent", new
        {
            user_id = user.Id,
            email = user.Email
        });
    }

    public async Task SendPasswordResetEmailAsync(Guid userId, string? fallbackBaseUrl = null)
    {
        var user = await FindUserAsync(userId);
        var settings = await _securitySettingsService.GetAsync();
        var baseUrl = ResolveBaseUrl(settings.PublicBaseUrl, fallbackBaseUrl);
        var token = await _userManager.GeneratePasswordResetTokenAsync(user);
        var link = $"{baseUrl}/account/resetpassword?email={Uri.EscapeDataString(user.Email ?? string.Empty)}&token={Uri.EscapeDataString(token)}";
        var template = _templateService.BuildPasswordResetEmail(WebUtility.HtmlEncode(link));

        await _emailSender.SendAsync(user.Email ?? string.Empty, template.Subject, template.TextBody, template.HtmlBody);
        await _auditLogWriter.WriteAsync("user", user.Id, "account.password_reset_email_sent", new
        {
            user_id = user.Id,
            email = user.Email
        });
    }

    private static string ResolveBaseUrl(string? configuredBaseUrl, string? fallbackBaseUrl)
    {
        var value = !string.IsNullOrWhiteSpace(configuredBaseUrl)
            ? configuredBaseUrl
            : fallbackBaseUrl;

        if (string.IsNullOrWhiteSpace(value))
        {
            throw new InvalidOperationException("Public base URL is not configured.");
        }

        return value.TrimEnd('/');
    }

    private async Task<ApplicationUser> FindUserAsync(Guid userId)
    {
        var user = await _userManager.FindByIdAsync(userId.ToString());
        if (user is null || string.IsNullOrWhiteSpace(user.Email))
        {
            throw new InvalidOperationException("User not found or email is empty.");
        }

        return user;
    }
}
