using MailKit;
using MailKit.Net.Imap;
using MailKit.Net.Pop3;
using MailKit.Search;
using Microsoft.Extensions.Options;
using MimeKit;

namespace MailPrint.Services;

public class MailFetchService
{
    private readonly ILogger<MailFetchService> _logger;
    private readonly MailPrintOptions _options;

    public MailFetchService(ILogger<MailFetchService> logger, IOptions<MailPrintOptions> options)
    {
        _logger = logger;
        _options = options.Value;
    }

    public async Task<List<PrintJob>> FetchJobsForAccountAsync(MailAccount account, CancellationToken ct)
    {
        return account.Protocol.ToUpper() == "POP3"
            ? await FetchViaPop3Async(account, ct)
            : await FetchViaImapAsync(account, ct);
    }

    private async Task<List<PrintJob>> FetchViaImapAsync(MailAccount account, CancellationToken ct)
    {
        var jobs = new List<PrintJob>();
        using var client = new ImapClient();
        await client.ConnectAsync(account.Host, account.Port, account.UseSsl, ct);
        await client.AuthenticateAsync(account.Username, account.Password, ct);

        var folder = await client.GetFolderAsync(account.Folder, ct);
        await folder.OpenAsync(FolderAccess.ReadWrite, ct);

        var uids = await folder.SearchAsync(SearchQuery.NotSeen, ct);
        _logger.LogInformation("[{Account}] IMAP: {Count} ungelesene Nachrichten", account.Name, uids.Count);

        foreach (var uid in uids)
        {
            if (ct.IsCancellationRequested) break;
            var message = await folder.GetMessageAsync(uid, ct);
            var extracted = ExtractJobs(message, account);
            if (extracted.Count > 0)
            {
                jobs.AddRange(extracted);
                if (_options.MarkAsRead)
                    await folder.AddFlagsAsync(uid, MessageFlags.Seen, true, ct);
                if (_options.DeleteAfterPrint)
                    await folder.AddFlagsAsync(uid, MessageFlags.Deleted, true, ct);
            }
        }

        if (_options.DeleteAfterPrint) await folder.ExpungeAsync(ct);
        await client.DisconnectAsync(true, ct);
        return jobs;
    }

    private async Task<List<PrintJob>> FetchViaPop3Async(MailAccount account, CancellationToken ct)
    {
        var jobs = new List<PrintJob>();
        using var client = new Pop3Client();
        await client.ConnectAsync(account.Host, account.Port, account.UseSsl, ct);
        await client.AuthenticateAsync(account.Username, account.Password, ct);

        int count = await client.GetMessageCountAsync(ct);
        _logger.LogInformation("[{Account}] POP3: {Count} Nachrichten", account.Name, count);

        var toDelete = new List<int>();
        for (int i = 0; i < count; i++)
        {
            if (ct.IsCancellationRequested) break;
            var message = await client.GetMessageAsync(i, ct);
            var extracted = ExtractJobs(message, account);
            if (extracted.Count > 0)
            {
                jobs.AddRange(extracted);
                if (_options.DeleteAfterPrint) toDelete.Add(i);
            }
        }

        foreach (var idx in toDelete) await client.DeleteMessageAsync(idx, ct);
        await client.DisconnectAsync(true, ct);
        return jobs;
    }

    private List<PrintJob> ExtractJobs(MimeMessage message, MailAccount account)
    {
        var jobs = new List<PrintJob>();

        // Profil für Filter-Lookup
        var profile = _options.PrinterProfiles.FirstOrDefault(p =>
            p.Name.Equals(account.PrinterProfileName, StringComparison.OrdinalIgnoreCase));

        var from = message.From.Mailboxes.Select(m => m.Address.ToLower()).ToList();

        // Whitelist: Profil-Liste hat Vorrang, Fallback global
        var allowed = profile?.AllowedSenders.Count > 0
            ? profile.AllowedSenders
            : _options.AllowedSenders;
        if (allowed.Count > 0 && !from.Any(f => allowed.Any(a => a.ToLower() == f)))
        {
            _logger.LogDebug("[{Account}] Absender {From} nicht in Whitelist", account.Name, string.Join(",", from));
            return jobs;
        }

        // Blacklist: Profil-Liste hat Vorrang, Fallback global
        var blocked = profile?.BlockedSenders.Count > 0
            ? profile.BlockedSenders
            : _options.BlockedSenders;
        if (blocked.Count > 0 && from.Any(f => blocked.Any(b => b.ToLower() == f)))
        {
            _logger.LogDebug("[{Account}] Absender {From} in Blacklist", account.Name, string.Join(",", from));
            return jobs;
        }

        if (!string.IsNullOrEmpty(_options.SubjectFilter) &&
            !message.Subject.Contains(_options.SubjectFilter, StringComparison.OrdinalIgnoreCase))
            return jobs;

        Directory.CreateDirectory(_options.TempDirectory);

        foreach (var attachment in message.Attachments.OfType<MimePart>())
        {
            var ext = Path.GetExtension(attachment.FileName ?? "").ToLower();
            if (!_options.AllowedExtensions.Contains(ext)) continue;

            var tempFile = Path.Combine(_options.TempDirectory, $"{Guid.NewGuid()}{ext}");
            using (var stream = File.Create(tempFile))
                attachment.Content.DecodeTo(stream);

            jobs.Add(new PrintJob
            {
                FilePath = tempFile,
                MailSubject = message.Subject,
                MailFrom = message.From.ToString(),
                PrinterProfileName = account.PrinterProfileName
            });

            _logger.LogInformation("[{Account}] Job: {File}", account.Name, tempFile);
        }

        return jobs;
    }
}

public record PrintJob
{
    public string FilePath { get; init; } = "";
    public string MailSubject { get; init; } = "";
    public string MailFrom { get; init; } = "";
    public string PrinterProfileName { get; init; } = "";
    // Direkte Overrides für WebAPI
    public string? PrinterOverride { get; init; }
    public string? PaperSourceOverride { get; init; }
    public int? CopiesOverride { get; init; }
}
