using Microsoft.Extensions.Options;
using System.Diagnostics;
using System.Drawing.Printing;

namespace MailPrint.Services;

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

    private static readonly string[] SumatraCandidates =
    [
        @"C:\Program Files\SumatraPDF\SumatraPDF.exe",
        @"C:\Program Files (x86)\SumatraPDF\SumatraPDF.exe",
        Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "SumatraPDF", "SumatraPDF.exe"),
        Path.Combine(AppContext.BaseDirectory, "SumatraPDF.exe"),
    ];

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

    public void PrintPdf(PrintJob job)
    {
        // Profil auflösen: direkte Overrides > benanntes Profil > erstes Profil > Defaults
        var profile = ResolveProfile(job);
        var printerName = !string.IsNullOrEmpty(job.PrinterOverride) ? job.PrinterOverride : profile.PrinterName;
        var paperSource = !string.IsNullOrEmpty(job.PaperSourceOverride) ? job.PaperSourceOverride : profile.PaperSource;
        var copies = job.CopiesOverride ?? profile.Copies;

        if (string.IsNullOrEmpty(printerName))
            printerName = GetDefaultPrinter();

        _logger.LogInformation("Drucke '{Subject}' → {Printer} | Fach: {Fach} | {Copies}x",
            job.MailSubject, printerName, string.IsNullOrEmpty(paperSource) ? "Standard" : paperSource, copies);

        try
        {
            for (int i = 0; i < copies; i++)
                PrintOnce(job.FilePath, printerName, paperSource, profile.Duplex);
            _logger.LogInformation("Druck OK: {File}", job.FilePath);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Druckfehler: {File}", job.FilePath);
            throw;
        }
        finally
        {
            TryDelete(job.FilePath);
        }
    }

    private PrinterProfile ResolveProfile(PrintJob job)
    {
        if (!string.IsNullOrEmpty(job.PrinterProfileName))
        {
            var p = _options.PrinterProfiles.FirstOrDefault(x =>
                x.Name.Equals(job.PrinterProfileName, StringComparison.OrdinalIgnoreCase));
            if (p != null) return p;
        }
        return _options.PrinterProfiles.FirstOrDefault() ?? new PrinterProfile();
    }

    private void PrintOnce(string pdfPath, string printerName, string paperSource, string duplex = "none")
    {
        var sumatra = ResolveSumatra();
        if (sumatra != null)
        {
            var settings = BuildPrintSettings(paperSource, duplex);
            RunAndWait(sumatra, $"-print-to \"{printerName}\" -print-settings \"{settings}\" -silent \"{pdfPath}\"");
            return;
        }

        // Fallback Shell-Print
        _logger.LogWarning("Kein SumatraPDF – Shell-Print (kein Papierfach-Support)");
        var psi = new ProcessStartInfo { FileName = pdfPath, Verb = "print", UseShellExecute = true, WindowStyle = ProcessWindowStyle.Hidden };
        using var p = Process.Start(psi)!;
        p.WaitForExit(30_000);
    }

    private static string BuildPrintSettings(string paperSource, string duplex)
    {
        var parts = new List<string> { "noscale" };
        if (!string.IsNullOrEmpty(paperSource)) parts.Add($"bin={paperSource}");
        if (!string.IsNullOrEmpty(duplex) && duplex != "none") parts.Add($"duplex{duplex}");
        return string.Join(",", parts);
    }

    private string? ResolveSumatra()
    {
        if (!string.IsNullOrEmpty(_options.SumatraPath) && File.Exists(_options.SumatraPath))
            return _options.SumatraPath;
        return Array.Find(SumatraCandidates, File.Exists);
    }

    private void RunAndWait(string exe, string args)
    {
        _logger.LogDebug("Exec: {Exe} {Args}", exe, args);
        var psi = new ProcessStartInfo(exe, args) { UseShellExecute = false, CreateNoWindow = true };
        using var p = Process.Start(psi) ?? throw new InvalidOperationException($"Nicht startbar: {exe}");
        if (!p.WaitForExit(300_000)) { p.Kill(); throw new TimeoutException($"Timeout: {exe}"); }
        if (p.ExitCode != 0) _logger.LogWarning("ExitCode {Code}: {Exe}", p.ExitCode, exe);
    }

    public List<PrinterInfo> GetPrinterInfos()
    {
        var result = new List<PrinterInfo>();
        foreach (string name in PrinterSettings.InstalledPrinters)
        {
            var ps = new PrinterSettings { PrinterName = name };
            var sources = new List<string>();
            foreach (PaperSource src in ps.PaperSources)
                sources.Add(src.SourceName);
            result.Add(new PrinterInfo(name, sources));
        }
        return result;
    }

    public IEnumerable<string> GetAvailablePrinters() => GetPrinterInfos().Select(p => p.Name);
    private static string GetDefaultPrinter() => new PrinterSettings().PrinterName;
    private void TryDelete(string path)
    {
        try { if (File.Exists(path)) File.Delete(path); }
        catch (Exception ex) { _logger.LogWarning(ex, "Temp nicht löschbar: {Path}", path); }
    }
}

public record PrinterInfo(string Name, List<string> PaperSources);
