using foodmarket.Application.Common.Email;
using foodmarket.Infrastructure.Persistence;
using MailKit.Net.Smtp;
using MailKit.Security;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using MimeKit;

namespace foodmarket.Infrastructure.Email;

/// <summary>SMTP-отправка через MailKit. Зарегистрирован Singleton, но на
/// каждую отправку создаёт scope для свежего DbContext'а — конфиг
/// (PlatformSettings) перечитывается на каждой отправке без рестарта.
///
/// Если SMTP не настроен (host пуст / from-email пуст) — кидает
/// EmailNotConfiguredException. Контроллер ловит и возвращает 400/500.</summary>
public class MailKitEmailSender : IEmailSender
{
    private readonly IServiceScopeFactory _scopes;
    private readonly IDataProtectionProvider _dpProvider;
    private readonly ILogger<MailKitEmailSender> _logger;

    public MailKitEmailSender(IServiceScopeFactory scopes, IDataProtectionProvider dpProvider, ILogger<MailKitEmailSender> logger)
    {
        _scopes = scopes; _dpProvider = dpProvider; _logger = logger;
    }

    public async Task SendAsync(string toEmail, string subject, string body, CancellationToken ct = default)
    {
        using var scope = _scopes.CreateScope();
        var db = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        var s = await db.PlatformSettings.AsNoTracking().FirstOrDefaultAsync(ct);

        if (s is null || string.IsNullOrWhiteSpace(s.SmtpHost) || string.IsNullOrWhiteSpace(s.FromEmail))
        {
            throw new EmailNotConfiguredException(
                "SMTP не настроен. Откройте «Системная консоль → Настройки платформы» и заполните SmtpHost, FromEmail, креды.");
        }

        var msg = new MimeMessage();
        msg.From.Add(new MailboxAddress(s.FromName ?? "Food Market", s.FromEmail));
        msg.To.Add(MailboxAddress.Parse(toEmail));
        msg.Subject = subject;
        msg.Body = new TextPart("plain") { Text = body };

        // Implicit TLS (SmtpUseSsl) — обычно 465. STARTTLS (SmtpStartTls) — 587.
        // Если оба false — открытое соединение (SmtpClient.Connect c None).
        var secureOption = s.SmtpUseSsl
            ? SecureSocketOptions.SslOnConnect
            : (s.SmtpStartTls ? SecureSocketOptions.StartTls : SecureSocketOptions.None);
        var port = s.SmtpPort ?? (s.SmtpUseSsl ? 465 : 587);

        using var client = new SmtpClient();
        await client.ConnectAsync(s.SmtpHost, port, secureOption, ct);

        if (!string.IsNullOrEmpty(s.SmtpUsername))
        {
            var password = string.Empty;
            if (!string.IsNullOrEmpty(s.SmtpPasswordEncrypted))
            {
                try
                {
                    var protector = _dpProvider.CreateProtector("foodmarket.smtp");
                    password = protector.Unprotect(s.SmtpPasswordEncrypted);
                }
                catch (Exception ex)
                {
                    throw new EmailNotConfiguredException(
                        "Не удалось расшифровать SMTP-пароль (DataProtection ключ изменился?). " +
                        "Введите пароль заново в настройках платформы. Detail: " + ex.Message);
                }
            }
            await client.AuthenticateAsync(s.SmtpUsername, password, ct);
        }

        try
        {
            await client.SendAsync(msg, ct);
            _logger.LogInformation("Email sent to {To} subject={Subject}", toEmail, subject);
        }
        finally
        {
            await client.DisconnectAsync(true, ct);
        }
    }
}
