using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System.Drawing.Printing;

namespace MailPrintConfig;

public class MainForm : Form
{
    private const int LabelW = 140;
    private const int CtrlW  = 240;
    private const int RowH   = 28;
    private const int Pad    = 12;

    // ── Allgemein ─────────────────────────────────────────────────
    private TextBox txtInterval = null!, txtSubjectFilter = null!, txtTempDir = null!, txtSumatraPath = null!;
    private CheckBox chkDelete = null!, chkMarkRead = null!;

    // ── Web API ───────────────────────────────────────────────────
    private TextBox txtApiPort = null!, txtApiKey = null!;
    private CheckBox chkBindAll = null!;

    // ── Grids ─────────────────────────────────────────────────────
    private DataGridView gridProfiles = null!, gridAccounts = null!, gridFolders = null!;

    // ── Filter ────────────────────────────────────────────────────
    private TextBox txtGlobalAllowed = null!, txtGlobalBlocked = null!;

    // ── Config / Steuerung ────────────────────────────────────────
    private TextBox txtConfigPath = null!;
    private Button btnLoad = null!, btnSave = null!, btnStartStop = null!, btnInstall = null!, btnUninstall = null!, btnSvcStart = null!, btnSvcStop = null!;
    private Label lblStatus = null!;
    private System.Diagnostics.Process? _proc;
    private System.Windows.Forms.Timer _timer = null!;

    public MainForm()
    {
        Text = "MailPrint Konfiguration";
        Width = 1100; MinimumSize = new Size(900, 700);
        Font = new Font("Segoe UI", 9f);
        StartPosition = FormStartPosition.CenterScreen;

        BuildUI();
        AutoDetectConfigPath();

        Load += (_, _) => { if (File.Exists(txtConfigPath.Text)) LoadConfig(); };

        _timer = new System.Windows.Forms.Timer { Interval = 1500 };
        _timer.Tick += (_, _) => RefreshStartStop();
        _timer.Start();
    }

    // ══════════════════════════════════════════════════════════════
    // UI
    // ══════════════════════════════════════════════════════════════
    private void BuildUI()
    {
        var tabs = new TabControl { Dock = DockStyle.Fill };
        tabs.TabPages.Add(BuildProfilesTab());
        tabs.TabPages.Add(BuildAccountsTab());
        tabs.TabPages.Add(BuildFoldersTab());
        tabs.TabPages.Add(BuildFilterTab());
        tabs.TabPages.Add(BuildApiTab());
        tabs.TabPages.Add(BuildGeneralTab());
        tabs.TabPages.Add(BuildAboutTab());
        Controls.Add(tabs);

        var bottom = new Panel { Dock = DockStyle.Bottom, Height = 84 };
        int x = Pad;

        // Zeile 1: Laden | Speichern | [Pfad…] [____path____]
        btnLoad   = Btn("Laden",     x, 10, 80); x += 86;
        btnSave   = Btn("Speichern", x, 10, 84); x += 90;
        var btnBrowse = Btn("Pfad…", x, 10, 60); x += 66;
        txtConfigPath = new TextBox { Left = x, Top = 12, Width = 320, Anchor = AnchorStyles.Left | AnchorStyles.Top };

        // Zeile 2: EXE starten | EXE stoppen | Dienst installieren | Dienst deinstallieren | Dienst starten | Dienst beenden
        int x2 = Pad;
        btnStartStop  = Btn("▶ EXE starten",        x2, 44, 120, Color.LightGreen);  x2 += 126;
        btnInstall    = Btn("⚙ Dienst installieren", x2, 44, 145, Color.LightBlue);   x2 += 151;
        btnUninstall  = Btn("✖ Dienst deinstall.",   x2, 44, 140, Color.LightSalmon); x2 += 146;
        btnSvcStart   = Btn("▶ Dienst starten",      x2, 44, 125, Color.PaleGreen);   x2 += 131;
        btnSvcStop    = Btn("⏹ Dienst beenden",      x2, 44, 120, Color.LightCoral);

        lblStatus = new Label { Left = Pad, Top = 68, Width = 820, Height = 16, AutoSize = false, ForeColor = Color.DarkGreen };

        btnLoad.Click       += (_, _) => LoadConfig();
        btnSave.Click       += (_, _) => SaveConfig();
        btnStartStop.Click  += (_, _) => _ = ToggleExeAsync();
        btnBrowse.Click     += (_, _) => BrowseConfig();
        btnInstall.Click    += (_, _) => _ = ServiceActionAsync("install");
        btnUninstall.Click  += (_, _) => _ = ServiceActionAsync("uninstall");
        btnSvcStart.Click   += (_, _) => _ = ServiceActionAsync("start");
        btnSvcStop.Click    += (_, _) => _ = ServiceActionAsync("stop");

        bottom.Controls.AddRange([btnLoad, btnSave, btnBrowse, txtConfigPath,
            btnStartStop, btnInstall, btnUninstall, btnSvcStart, btnSvcStop, lblStatus]);
        Controls.Add(bottom);
    }

    // ── Tab: Drucker-Profile ──────────────────────────────────────
    private TabPage BuildProfilesTab()
    {
        var tab = new TabPage("Drucker-Profile");
        tab.Controls.Add(new Label
        {
            Text = "Profil = Drucker + Papierfach + optionale Absender-Filter (überschreibt globale Filter).",
            Left = Pad, Top = Pad, Width = 1060, Height = 18, ForeColor = Color.DimGray
        });

        gridProfiles = new DataGridView
        {
            Left = Pad, Top = 32, Width = 1060, Height = 500,
            Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom,
            AllowUserToAddRows = true, AllowUserToDeleteRows = false,
            AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None,
            ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize,
            EditMode = DataGridViewEditMode.EditOnEnter,
            SelectionMode = DataGridViewSelectionMode.FullRowSelect,
            ScrollBars = ScrollBars.Both
        };

        gridProfiles.Columns.Add(new DataGridViewTextBoxColumn { Name = "PName",   HeaderText = "Profil-Name",       Width = 120 });

        var colPrinter = new DataGridViewComboBoxColumn { Name = "Printer", HeaderText = "Drucker", FlatStyle = FlatStyle.Flat, Width = 280 };
        colPrinter.Items.Add("");
        foreach (string p in PrinterSettings.InstalledPrinters) colPrinter.Items.Add(p);
        gridProfiles.Columns.Add(colPrinter);

        var colSource = new DataGridViewComboBoxColumn { Name = "Source", HeaderText = "Papierfach", FlatStyle = FlatStyle.Flat, Width = 150 };
        colSource.Items.Add("");
        gridProfiles.Columns.Add(colSource);

        gridProfiles.Columns.Add(new DataGridViewTextBoxColumn { Name = "Copies",  HeaderText = "Kopien",   Width = 55 });

        var colDuplex = new DataGridViewComboBoxColumn { Name = "Duplex", HeaderText = "Duplex", FlatStyle = FlatStyle.Flat, Width = 110 };
        colDuplex.Items.AddRange(["Aus", "Lange Seite", "Kurze Seite"]);
        gridProfiles.Columns.Add(colDuplex);

        gridProfiles.Columns.Add(new DataGridViewTextBoxColumn { Name = "Allowed", HeaderText = "E-Mail-Whitelist (Komma)", Width = 220 });
        gridProfiles.Columns.Add(new DataGridViewTextBoxColumn { Name = "Blocked", HeaderText = "E-Mail-Blacklist (Komma)", Width = 220 });

        gridProfiles.DataError += (_, e) => e.ThrowException = false;
        gridProfiles.CellValueChanged += GridProfiles_CellValueChanged;
        gridProfiles.CurrentCellDirtyStateChanged += (_, _) =>
        {
            if (gridProfiles.IsCurrentCellDirty) gridProfiles.CommitEdit(DataGridViewDataErrorContexts.Commit);
        };

        AttachContextMenu(gridProfiles);
        tab.Controls.Add(gridProfiles);
        return tab;
    }

    // ── Tab: Postfächer ───────────────────────────────────────────
    private TabPage BuildAccountsTab()
    {
        var tab = new TabPage("Postfächer");
        tab.Controls.Add(new Label
        {
            Text = "Jedes Postfach wird unabhängig abgerufen und druckt auf das zugeordnete Drucker-Profil.",
            Left = Pad, Top = Pad, Width = 1060, Height = 18, ForeColor = Color.DimGray
        });

        gridAccounts = new DataGridView
        {
            Left = Pad, Top = 32, Width = 1060, Height = 500,
            Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom,
            AllowUserToAddRows = true, AllowUserToDeleteRows = false,
            AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None,
            ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize,
            EditMode = DataGridViewEditMode.EditOnEnter,
            SelectionMode = DataGridViewSelectionMode.FullRowSelect,
            ScrollBars = ScrollBars.Both
        };

        gridAccounts.Columns.Add(new DataGridViewTextBoxColumn { Name = "AName",  HeaderText = "Name",          Width = 100 });

        var colProto = new DataGridViewComboBoxColumn { Name = "Protocol", HeaderText = "Protokoll", FlatStyle = FlatStyle.Flat, Width = 70 };
        colProto.Items.AddRange(["IMAP", "POP3"]);
        gridAccounts.Columns.Add(colProto);

        gridAccounts.Columns.Add(new DataGridViewTextBoxColumn { Name = "Host",   HeaderText = "Host",          Width = 200 });
        gridAccounts.Columns.Add(new DataGridViewTextBoxColumn { Name = "Port",   HeaderText = "Port",          Width = 52 });
        gridAccounts.Columns.Add(new DataGridViewCheckBoxColumn { Name = "Ssl",   HeaderText = "SSL",           Width = 38 });
        gridAccounts.Columns.Add(new DataGridViewTextBoxColumn { Name = "User",   HeaderText = "Benutzername",  Width = 180 });
        var colPass = new DataGridViewTextBoxColumn { Name = "Pass", HeaderText = "Passwort", Width = 180 };
        colPass.DefaultCellStyle.NullValue = null;
        colPass.DefaultCellStyle.Font = new Font("Courier New", 9f);
        gridAccounts.Columns.Add(colPass);
        gridAccounts.Columns.Add(new DataGridViewTextBoxColumn { Name = "Folder", HeaderText = "Ordner",        Width = 80 });

        var colProfile = new DataGridViewComboBoxColumn { Name = "Profile", HeaderText = "Drucker-Profil", FlatStyle = FlatStyle.Flat, Width = 150 };
        colProfile.Items.Add("");
        gridAccounts.Columns.Add(colProfile);

        gridAccounts.DataError += (_, e) => e.ThrowException = false;
        // Passwort maskieren
        gridAccounts.CellFormatting += (_, e) =>
        {
            if (e.RowIndex >= 0 && gridAccounts.Columns[e.ColumnIndex].Name == "Pass" && e.Value is string pw)
                e.Value = new string('●', pw.Length);
        };
        gridAccounts.CellBeginEdit += (_, e) =>
        {
            if (e.RowIndex >= 0 && gridAccounts.Columns[e.ColumnIndex].Name == "Pass")
            {
                var cell = gridAccounts.Rows[e.RowIndex].Cells["Pass"];
                // beim Bearbeiten Klartext zeigen – Value ist bereits der echte Wert
            }
        };
        gridProfiles.CellValueChanged += (_, _) => RefreshProfileDropdowns();

        AttachContextMenu(gridAccounts);
        tab.Controls.Add(gridAccounts);
        return tab;
    }

    // ── Tab: Ordner-Überwachung ────────────────────────────────────
    private TabPage BuildFoldersTab()
    {
        var tab = new TabPage("Ordner-Überwachung");
        tab.Controls.Add(new Label
        {
            Text = "PDFs in diesen Ordnern werden automatisch erkannt und gedruckt.",
            Left = Pad, Top = Pad, Width = 1060, Height = 18, ForeColor = Color.DimGray
        });

        gridFolders = new DataGridView
        {
            Left = Pad, Top = 32, Width = 1060, Height = 500,
            Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Right | AnchorStyles.Bottom,
            AllowUserToAddRows = true, AllowUserToDeleteRows = false,
            AutoSizeColumnsMode = DataGridViewAutoSizeColumnsMode.None,
            ColumnHeadersHeightSizeMode = DataGridViewColumnHeadersHeightSizeMode.AutoSize,
            EditMode = DataGridViewEditMode.EditOnEnter,
            SelectionMode = DataGridViewSelectionMode.FullRowSelect,
            ScrollBars = ScrollBars.Both
        };

        gridFolders.Columns.Add(new DataGridViewTextBoxColumn  { Name = "FName",      HeaderText = "Name",              Width = 120 });
        gridFolders.Columns.Add(new DataGridViewTextBoxColumn  { Name = "FPath",      HeaderText = "Pfad",              Width = 400 });
        gridFolders.Columns.Add(new DataGridViewButtonColumn   { Name = "FBrowse",    HeaderText = "",   Text = "…",    Width = 30 });
        gridFolders.Columns.Add(new DataGridViewCheckBoxColumn { Name = "FSubfolders",HeaderText = "Unterordner",       Width = 90 });
        gridFolders.Columns.Add(new DataGridViewCheckBoxColumn { Name = "FDelete",    HeaderText = "Nach Druck löschen",Width = 120 });

        var colFProfile = new DataGridViewComboBoxColumn { Name = "FProfile", HeaderText = "Drucker-Profil", FlatStyle = FlatStyle.Flat, Width = 180 };
        colFProfile.Items.Add("");
        gridFolders.Columns.Add(colFProfile);

        gridFolders.DataError += (_, e) => e.ThrowException = false;
        gridFolders.CurrentCellDirtyStateChanged += (_, _) =>
        {
            if (gridFolders.IsCurrentCellDirty) gridFolders.CommitEdit(DataGridViewDataErrorContexts.Commit);
        };

        // "…"-Button → Ordner auswählen
        gridFolders.CellClick += (_, e) =>
        {
            if (e.RowIndex < 0 || gridFolders.Columns[e.ColumnIndex].Name != "FBrowse") return;
            var rowIndex = e.RowIndex;
            BeginInvoke(() => BrowseFolder(rowIndex));
        };

        gridProfiles.CellValueChanged += (_, _) => RefreshProfileDropdowns();
        AttachContextMenu(gridFolders);
        tab.Controls.Add(gridFolders);
        return tab;
    }

    // ── Tab: Filter ───────────────────────────────────────────────
    private TabPage BuildFilterTab()
    {
        var tab = new TabPage("E-Mail-Filter (Global)");
        int y = Pad;

        tab.Controls.Add(new Label
        {
            Text = "Globale Listen gelten für alle Profile, sofern das Profil keine eigene Liste definiert.\r\n" +
                   "Format: eine E-Mail-Adresse pro Zeile. Leer = kein Filter.",
            Left = Pad, Top = y, Width = 800, Height = 34, ForeColor = Color.DimGray
        });
        y += 40;

        tab.Controls.Add(new Label { Text = "E-Mail-Whitelist (nur diese Absender drucken):", Left = Pad, Top = y, Width = 390, AutoSize = false });
        tab.Controls.Add(new Label { Text = "E-Mail-Blacklist (diese Absender blockieren):",  Left = Pad + 420, Top = y, Width = 390, AutoSize = false });
        y += 20;

        txtGlobalAllowed = new TextBox
        {
            Left = Pad, Top = y, Width = 390, Height = 400,
            Multiline = true, ScrollBars = ScrollBars.Vertical,
            Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom
        };
        txtGlobalBlocked = new TextBox
        {
            Left = Pad + 420, Top = y, Width = 390, Height = 400,
            Multiline = true, ScrollBars = ScrollBars.Vertical,
            Anchor = AnchorStyles.Left | AnchorStyles.Top | AnchorStyles.Bottom
        };

        tab.Controls.Add(txtGlobalAllowed);
        tab.Controls.Add(txtGlobalBlocked);
        return tab;
    }

    // ── Tab: Web API ──────────────────────────────────────────────
    private TabPage BuildApiTab()
    {
        var tab = new TabPage("Web API");
        int y = Pad;
        txtApiPort = AddText(tab, "Port",                     ref y, "5100");
        chkBindAll = AddCheck(tab, "Alle Interfaces (0.0.0.0)", ref y, false);
        txtApiKey  = AddText(tab, "API-Key",                  ref y);

        var btnGen = Btn("🔑 Generieren", LabelW + Pad, y, 130);
        btnGen.Click += (_, _) => txtApiKey.Text = Guid.NewGuid().ToString("N");
        tab.Controls.Add(btnGen); y += RowH + 4;

        tab.Controls.Add(new Label
        {
            Text = "Header: X-Api-Key: <key>  |  Leer = kein Schutz\r\n\r\n" +
                   "POST /api/print/upload  (multipart: file, printer, paperSource, copies)\r\n" +
                   "POST /api/print/url     { url, printer, paperSource, copies }\r\n" +
                   "GET  /api/print/printers\r\nGET  /api/print/health\r\nGET  /swagger",
            Left = Pad, Top = y, Width = 700, Height = 110, ForeColor = Color.DimGray
        });
        return tab;
    }

    // ── Tab: Allgemein ────────────────────────────────────────────
    private TabPage BuildGeneralTab()
    {
        var tab = new TabPage("Allgemein");
        int y = Pad;
        txtInterval      = AddText(tab, "Intervall (Sek.)",   ref y, "60");
        txtSubjectFilter = AddText(tab, "Betreff-Filter",     ref y);
        chkDelete        = AddCheck(tab, "Nach Druck löschen",       ref y, true);
        chkMarkRead      = AddCheck(tab, "Als gelesen markieren",     ref y, true);
        txtSumatraPath   = AddText(tab, "SumatraPDF Pfad",    ref y);

        var btnS = Btn("…", LabelW + Pad + CtrlW + 4, y - RowH - 2, 28);
        btnS.Click += (_, _) =>
        {
            using var d = new OpenFileDialog { Filter = "SumatraPDF.exe|SumatraPDF.exe|Alle|*.*" };
            if (d.ShowDialog() == DialogResult.OK) txtSumatraPath.Text = d.FileName;
        };
        tab.Controls.Add(btnS);
        txtTempDir = AddText(tab, "Temp-Verzeichnis", ref y, Path.Combine(Path.GetTempPath(), "MailPrint"));
        return tab;
    }

    // ── Tab: Über ─────────────────────────────────────────────────
    private TabPage BuildAboutTab()
    {
        var tab = new TabPage("Über");
        int y = Pad + 10;

        void AddLine(string text, bool link = false, string url = "")
        {
            if (link)
            {
                var lbl = new LinkLabel
                {
                    Text = text, Left = Pad, Top = y, AutoSize = true,
                    Font = new Font("Segoe UI", 10f)
                };
                lbl.LinkClicked += (_, _) => System.Diagnostics.Process.Start(new System.Diagnostics.ProcessStartInfo(url) { UseShellExecute = true });
                tab.Controls.Add(lbl);
            }
            else
            {
                tab.Controls.Add(new Label
                {
                    Text = text, Left = Pad, Top = y, AutoSize = true,
                    Font = new Font("Segoe UI", 10f)
                });
            }
            y += 28;
        }

        var version = System.Reflection.Assembly.GetExecutingAssembly()
            .GetName().Version?.ToString(3) ?? "?";

        AddLine("MailPrint");
        y += 4;
        AddLine($"Version {version}");
        AddLine("Automatischer PDF-Druck per E-Mail und REST API.");
        AddLine("Kostenlos und quelloffen (MIT-Lizenz).");
        y += 16;

        AddLine("Quellcode & Dokumentation:");
        AddLine("https://www.dimedtec.net/dimedtec/MailPrint", link: true, url: "https://www.dimedtec.net/dimedtec/MailPrint");
        y += 16;

        AddLine("Verwendet:");
        AddLine("SumatraPDF – freier PDF-Viewer (GPLv3)");
        AddLine("https://www.sumatrapdfreader.org", link: true, url: "https://www.sumatrapdfreader.org");
        y += 8;
        AddLine("MailKit – IMAP/POP3 Bibliothek (MIT)");
        AddLine("https://github.com/jstedfast/MailKit", link: true, url: "https://github.com/jstedfast/MailKit");
        y += 8;
        AddLine(".NET – Microsoft (MIT)");
        AddLine("https://dotnet.microsoft.com", link: true, url: "https://dotnet.microsoft.com");

        return tab;
    }

    // ── Kontextmenü für Grids ──────────────────────────────────────
    private static void AttachContextMenu(DataGridView grid)
    {
        var menu = new ContextMenuStrip();
        var itemDelete = new ToolStripMenuItem("🗑 Zeile löschen");
        itemDelete.Click += (_, _) =>
        {
            foreach (DataGridViewRow row in grid.SelectedRows)
                if (!row.IsNewRow) grid.Rows.Remove(row);
        };
        menu.Items.Add(itemDelete);

        grid.MouseDown += (_, e) =>
        {
            if (e.Button != MouseButtons.Right) return;
            var hit = grid.HitTest(e.X, e.Y);
            if (hit.RowIndex >= 0)
            {
                grid.ClearSelection();
                grid.Rows[hit.RowIndex].Selected = true;
                menu.Show(grid, e.Location);
            }
        };
    }

    // ══════════════════════════════════════════════════════════════
    // Papierfächer + Profil-Dropdown sync
    // ══════════════════════════════════════════════════════════════
    private void GridProfiles_CellValueChanged(object? sender, DataGridViewCellEventArgs e)
    {
        if (e.RowIndex < 0 || gridProfiles.Columns[e.ColumnIndex].Name != "Printer") return;
        var printerName = gridProfiles.Rows[e.RowIndex].Cells["Printer"].Value?.ToString() ?? "";
        var sc = (DataGridViewComboBoxCell)gridProfiles.Rows[e.RowIndex].Cells["Source"];
        sc.Items.Clear(); sc.Items.Add("");
        if (!string.IsNullOrEmpty(printerName))
        {
            var ps = new PrinterSettings { PrinterName = printerName };
            foreach (PaperSource src in ps.PaperSources) sc.Items.Add(src.SourceName);
        }
    }

    private void RefreshProfileDropdowns()
    {
        var names = gridProfiles.Rows.Cast<DataGridViewRow>()
            .Where(r => !r.IsNewRow)
            .Select(r => r.Cells["PName"].Value?.ToString() ?? "")
            .Where(n => n.Length > 0).ToList();

        // Postfächer
        var colA = (DataGridViewComboBoxColumn)gridAccounts.Columns["Profile"];
        colA.Items.Clear(); colA.Items.Add("");
        foreach (var n in names) colA.Items.Add(n);
        foreach (DataGridViewRow row in gridAccounts.Rows)
        {
            if (row.IsNewRow) continue;
            var val = row.Cells["Profile"].Value?.ToString() ?? "";
            if (!string.IsNullOrEmpty(val) && !colA.Items.Contains(val)) colA.Items.Add(val);
        }

        // Ordner-Überwachung
        var colF = (DataGridViewComboBoxColumn)gridFolders.Columns["FProfile"];
        colF.Items.Clear(); colF.Items.Add("");
        foreach (var n in names) colF.Items.Add(n);
        foreach (DataGridViewRow row in gridFolders.Rows)
        {
            if (row.IsNewRow) continue;
            var val = row.Cells["FProfile"].Value?.ToString() ?? "";
            if (!string.IsNullOrEmpty(val) && !colF.Items.Contains(val)) colF.Items.Add(val);
        }
    }

    // ══════════════════════════════════════════════════════════════
    // Config laden / speichern
    // ══════════════════════════════════════════════════════════════
    private void AutoDetectConfigPath()
    {
        var candidates = new[]
        {
            Path.Combine(AppContext.BaseDirectory, "appsettings.json"),
            Path.Combine(AppContext.BaseDirectory, "..", "publish", "appsettings.json"),
            @"C:\Services\MailPrint\appsettings.json",
        };
        txtConfigPath.Text = candidates.FirstOrDefault(File.Exists)
            ?? Path.Combine(AppContext.BaseDirectory, "appsettings.json");
    }

    private void BrowseFolder(int rowIndex)
    {
        string? result = null;
        var current = gridFolders.Rows[rowIndex].Cells["FPath"].Value?.ToString() ?? "";

        // Eigener STA-Thread verhindert Blockierung durch COM-Dialog
        var t = new Thread(() =>
        {
            using var d = new FolderBrowserDialog
            {
                Description = "Ordner wählen",
                UseDescriptionForTitle = true,
                ShowNewFolderButton = true
            };
            if (Directory.Exists(current)) d.SelectedPath = current;
            result = d.ShowDialog() == DialogResult.OK ? d.SelectedPath : null;
        });
        t.SetApartmentState(ApartmentState.STA);
        t.Start();
        t.Join();

        if (result != null)
            gridFolders.Rows[rowIndex].Cells["FPath"].Value = result;
    }

    private void BrowseConfig()
    {
        using var d = new OpenFileDialog { Filter = "appsettings.json|appsettings.json|JSON|*.json" };
        if (d.ShowDialog() == DialogResult.OK) txtConfigPath.Text = d.FileName;
    }

    private void LoadConfig()
    {
        if (!File.Exists(txtConfigPath.Text))
        { SetStatus($"Nicht gefunden: {txtConfigPath.Text}", Color.Red); return; }

        try
        {
            var mp = (JObject.Parse(File.ReadAllText(txtConfigPath.Text))["MailPrint"] as JObject) ?? new JObject();

            txtInterval.Text      = mp["PollIntervalSeconds"]?.ToString() ?? "60";
            txtSubjectFilter.Text = mp["SubjectFilter"]?.ToString() ?? "";
            chkDelete.Checked     = mp["DeleteAfterPrint"]?.Value<bool>() ?? true;
            chkMarkRead.Checked   = mp["MarkAsRead"]?.Value<bool>() ?? true;
            txtSumatraPath.Text   = mp["SumatraPath"]?.ToString() ?? "";
            txtTempDir.Text       = mp["TempDirectory"]?.ToString() ?? "";

            txtGlobalAllowed.Text = string.Join(Environment.NewLine,
                (mp["AllowedSenders"] as JArray ?? new JArray()).Select(t => t.ToString()));
            txtGlobalBlocked.Text = string.Join(Environment.NewLine,
                (mp["BlockedSenders"] as JArray ?? new JArray()).Select(t => t.ToString()));

            var api = mp["WebApi"] as JObject ?? new JObject();
            txtApiPort.Text    = api["Port"]?.ToString() ?? "5100";
            chkBindAll.Checked = api["BindAllInterfaces"]?.Value<bool>() ?? false;
            txtApiKey.Text     = api["ApiKey"]?.ToString() ?? "";

            // Drucker-Profile
            gridProfiles.Rows.Clear();
            var printerCol = (DataGridViewComboBoxColumn)gridProfiles.Columns["Printer"];
            foreach (var p in mp["PrinterProfiles"] as JArray ?? new JArray())
            {
                var printer = p["PrinterName"]?.ToString() ?? "";
                var source  = p["PaperSource"]?.ToString() ?? "";
                var allowed = string.Join(", ", (p["AllowedSenders"] as JArray ?? new JArray()).Select(t => t.ToString()));
                var blocked = string.Join(", ", (p["BlockedSenders"] as JArray ?? new JArray()).Select(t => t.ToString()));

                if (!printerCol.Items.Contains(printer) && !string.IsNullOrEmpty(printer))
                    printerCol.Items.Add(printer);

                int ri = gridProfiles.Rows.Add(
                    p["Name"]?.ToString() ?? "", printer, "", p["Copies"]?.ToString() ?? "1",
                    DuplexToDisplay(p["Duplex"]?.ToString()), allowed, blocked);

                var sc = (DataGridViewComboBoxCell)gridProfiles.Rows[ri].Cells["Source"];
                sc.Items.Clear(); sc.Items.Add("");
                if (!string.IsNullOrEmpty(printer))
                {
                    var ps = new PrinterSettings { PrinterName = printer };
                    foreach (PaperSource s in ps.PaperSources) sc.Items.Add(s.SourceName);
                }
                if (!sc.Items.Contains(source) && !string.IsNullOrEmpty(source)) sc.Items.Add(source);
                sc.Value = source;
            }

            RefreshProfileDropdowns();

            // Postfächer
            gridAccounts.Rows.Clear();
            foreach (var a in mp["Accounts"] as JArray ?? new JArray())
            {
                var proto    = a["Protocol"]?.ToString() ?? "IMAP";
                var profName = a["PrinterProfileName"]?.ToString() ?? "";

                var protoCol = (DataGridViewComboBoxColumn)gridAccounts.Columns["Protocol"];
                if (!protoCol.Items.Contains(proto)) protoCol.Items.Add(proto);

                var profCol = (DataGridViewComboBoxColumn)gridAccounts.Columns["Profile"];
                if (!string.IsNullOrEmpty(profName) && !profCol.Items.Contains(profName))
                    profCol.Items.Add(profName);

                gridAccounts.Rows.Add(
                    a["Name"]?.ToString() ?? "",
                    proto,
                    a["Host"]?.ToString() ?? "",
                    a["Port"]?.ToString() ?? "993",
                    a["UseSsl"]?.Value<bool>() ?? true,
                    a["Username"]?.ToString() ?? "",
                    a["Password"]?.ToString() ?? "",
                    a["Folder"]?.ToString() ?? "INBOX",
                    profName);
            }

            // Ordner-Überwachung
            gridFolders.Rows.Clear();
            foreach (var f in mp["FolderWatchers"] as JArray ?? new JArray())
            {
                var profName = f["PrinterProfileName"]?.ToString() ?? "";
                var profCol  = (DataGridViewComboBoxColumn)gridFolders.Columns["FProfile"];
                if (!string.IsNullOrEmpty(profName) && !profCol.Items.Contains(profName))
                    profCol.Items.Add(profName);

                gridFolders.Rows.Add(
                    f["Name"]?.ToString() ?? "",
                    f["Path"]?.ToString() ?? "",
                    "…",
                    f["IncludeSubfolders"]?.Value<bool>() ?? false,
                    f["DeleteAfterPrint"]?.Value<bool>() ?? true,
                    profName);
            }

            SetStatus($"Geladen: {txtConfigPath.Text}", Color.DarkGreen);
        }
        catch (Exception ex) { SetStatus($"Fehler: {ex.Message}", Color.Red); }
    }

    private void SaveConfig()
    {
        gridProfiles.EndEdit();
        gridAccounts.EndEdit();
        gridFolders.EndEdit();

        var path = txtConfigPath.Text;
        var root = File.Exists(path) ? JObject.Parse(File.ReadAllText(path)) : new JObject();

        var profiles = new JArray();
        foreach (DataGridViewRow r in gridProfiles.Rows)
        {
            if (r.IsNewRow) continue;
            profiles.Add(new JObject
            {
                ["Name"]        = r.Cells["PName"].Value?.ToString() ?? "",
                ["PrinterName"] = r.Cells["Printer"].Value?.ToString() ?? "",
                ["PaperSource"] = r.Cells["Source"].Value?.ToString() ?? "",
                ["Copies"]      = int.TryParse(r.Cells["Copies"].Value?.ToString(), out int c) ? c : 1,
                ["Duplex"]      = DuplexToJson(r.Cells["Duplex"].Value?.ToString()),
                ["AllowedSenders"] = ToJArray(r.Cells["Allowed"].Value?.ToString()),
                ["BlockedSenders"] = ToJArray(r.Cells["Blocked"].Value?.ToString())
            });
        }

        var accounts = new JArray();
        foreach (DataGridViewRow r in gridAccounts.Rows)
        {
            if (r.IsNewRow) continue;
            accounts.Add(new JObject
            {
                ["Name"]               = r.Cells["AName"].Value?.ToString() ?? "",
                ["Protocol"]           = r.Cells["Protocol"].Value?.ToString() ?? "IMAP",
                ["Host"]               = r.Cells["Host"].Value?.ToString() ?? "",
                ["Port"]               = int.TryParse(r.Cells["Port"].Value?.ToString(), out int p) ? p : 993,
                ["UseSsl"]             = r.Cells["Ssl"].Value is true,
                ["Username"]           = r.Cells["User"].Value?.ToString() ?? "",
                ["Password"]           = r.Cells["Pass"].Value?.ToString() ?? "",
                ["Folder"]             = r.Cells["Folder"].Value?.ToString() ?? "INBOX",
                ["PrinterProfileName"] = r.Cells["Profile"].Value?.ToString() ?? ""
            });
        }

        var folders = new JArray();
        foreach (DataGridViewRow r in gridFolders.Rows)
        {
            if (r.IsNewRow) continue;
            folders.Add(new JObject
            {
                ["Name"]               = r.Cells["FName"].Value?.ToString() ?? "",
                ["Path"]               = r.Cells["FPath"].Value?.ToString() ?? "",
                ["IncludeSubfolders"]  = r.Cells["FSubfolders"].Value is true,
                ["DeleteAfterPrint"]   = r.Cells["FDelete"].Value is true,
                ["PrinterProfileName"] = r.Cells["FProfile"].Value?.ToString() ?? ""
            });
        }

        root["MailPrint"] = new JObject
        {
            ["PollIntervalSeconds"] = int.TryParse(txtInterval.Text, out int iv) ? iv : 60,
            ["SubjectFilter"]       = txtSubjectFilter.Text,
            ["DeleteAfterPrint"]    = chkDelete.Checked,
            ["MarkAsRead"]          = chkMarkRead.Checked,
            ["SumatraPath"]         = txtSumatraPath.Text,
            ["TempDirectory"]       = txtTempDir.Text,
            ["AllowedExtensions"]   = new JArray(".pdf"),
            ["AllowedSenders"]      = ToJArray(txtGlobalAllowed.Text, multiline: true),
            ["BlockedSenders"]      = ToJArray(txtGlobalBlocked.Text, multiline: true),
            ["PrinterProfiles"]     = profiles,
            ["Accounts"]            = accounts,
            ["FolderWatchers"]      = folders,
            ["WebApi"] = new JObject
            {
                ["Port"]              = int.TryParse(txtApiPort.Text, out int ap) ? ap : 5100,
                ["BindAllInterfaces"] = chkBindAll.Checked,
                ["ApiKey"]            = txtApiKey.Text
            }
        };

        if (root["Serilog"] == null)
            root["Serilog"] = JObject.Parse("{\"MinimumLevel\":{\"Default\":\"Information\",\"Override\":{\"Microsoft\":\"Warning\",\"Microsoft.AspNetCore\":\"Warning\"}}}");

        try
        {
            Directory.CreateDirectory(Path.GetDirectoryName(path)!);
            File.WriteAllText(path, root.ToString(Formatting.Indented));
            SetStatus($"Gespeichert: {path}", Color.DarkGreen);
        }
        catch (Exception ex) { SetStatus($"Fehler: {ex.Message}", Color.Red); }
    }

    private static string DuplexToDisplay(string? json) => json switch
    {
        "long"  => "Lange Seite",
        "short" => "Kurze Seite",
        _       => "Aus"
    };

    private static string DuplexToJson(string? display) => display switch
    {
        "Lange Seite" => "long",
        "Kurze Seite" => "short",
        _             => "none"
    };

    private static JArray ToJArray(string? input, bool multiline = false)
    {
        if (string.IsNullOrWhiteSpace(input)) return new JArray();
        var sep = multiline ? new[] { "\r\n", "\n" } : new[] { "," };
        return new JArray(input.Split(sep, StringSplitOptions.RemoveEmptyEntries)
            .Select(s => s.Trim()).Where(s => s.Length > 0));
    }

    // ══════════════════════════════════════════════════════════════
    // EXE starten / stoppen
    // ══════════════════════════════════════════════════════════════
    private async Task ToggleExeAsync()
    {
        btnStartStop.Enabled = false;
        try
        {
            if (_proc is { HasExited: false })
            {
                _proc.Kill(entireProcessTree: true);
                await _proc.WaitForExitAsync();
                _proc = null;
                SetStatus("MailPrint EXE gestoppt.", Color.DarkOrange);
            }
            else
            {
                var exePath = Path.Combine(
                    Path.GetDirectoryName(txtConfigPath.Text) ?? AppContext.BaseDirectory,
                    "MailPrint.exe");
                if (!File.Exists(exePath))
                { SetStatus($"MailPrint.exe nicht gefunden: {exePath}", Color.Red); return; }

                _proc = new System.Diagnostics.Process
                {
                    StartInfo = new System.Diagnostics.ProcessStartInfo(exePath)
                    {
                        UseShellExecute = true,
                        WorkingDirectory = Path.GetDirectoryName(exePath)!
                    },
                    EnableRaisingEvents = true
                };
                _proc.Exited += (_, _) => BeginInvoke(RefreshStartStop);
                _proc.Start();
                SetStatus($"MailPrint EXE gestartet (PID {_proc.Id})", Color.DarkGreen);
            }
        }
        finally { btnStartStop.Enabled = true; RefreshStartStop(); }
    }

    // ── Dienst-Aktionen ───────────────────────────────────────────
    private const string ServiceName = "MailPrint";

    private async Task ServiceActionAsync(string action)
    {
        var publishDir = Path.GetDirectoryName(txtConfigPath.Text) ?? AppContext.BaseDirectory;
        var exePath    = Path.Combine(publishDir, "MailPrint.exe");
        var installPs  = Path.Combine(publishDir, "..", "install-service.ps1");
        var uninstallPs= Path.Combine(publishDir, "..", "uninstall-service.ps1");

        // Bestätigung nur bei Install/Deinstall
        if (action is "install" or "uninstall")
        {
            var msg = action == "install"
                ? "Dienst 'MailPrint' jetzt installieren?"
                : "Dienst 'MailPrint' wirklich deinstallieren?";
            if (MessageBox.Show(msg, "Bestätigung", MessageBoxButtons.YesNo, MessageBoxIcon.Question) != DialogResult.Yes)
                return;
        }

        SetStatus($"Führe aus: {action}…", Color.DarkBlue);
        DisableServiceButtons(true);

        try
        {
            string cmd = action switch
            {
                "install"   => $"-ExecutionPolicy Bypass -File \"{Path.GetFullPath(installPs)}\"",
                "uninstall" => $"-ExecutionPolicy Bypass -File \"{Path.GetFullPath(uninstallPs)}\"",
                "start"     => $"-Command Start-Service -Name '{ServiceName}'",
                "stop"      => $"-Command Stop-Service  -Name '{ServiceName}' -Force",
                _ => throw new ArgumentException(action)
            };

            var psi = new System.Diagnostics.ProcessStartInfo
            {
                FileName        = "powershell",
                Arguments       = $"-NoProfile -NonInteractive {cmd}",
                UseShellExecute = true,
                Verb            = "runas",
                WindowStyle     = System.Diagnostics.ProcessWindowStyle.Hidden
            };

            using var p = System.Diagnostics.Process.Start(psi)!;
            await p.WaitForExitAsync();

            if (p.ExitCode == 0)
            {
                var successMsg = action switch
                {
                    "install"   => "Dienst 'MailPrint' wurde erfolgreich installiert.",
                    "uninstall" => "Dienst 'MailPrint' wurde erfolgreich deinstalliert.",
                    "start"     => "Dienst 'MailPrint' wurde gestartet.",
                    "stop"      => "Dienst 'MailPrint' wurde beendet.",
                    _           => "Aktion erfolgreich."
                };
                SetStatus(successMsg, Color.DarkGreen);
                MessageBox.Show(successMsg, "Erfolgreich", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            else
                SetStatus($"'{action}' fehlgeschlagen (Code {p.ExitCode})", Color.Red);
        }
        catch (Exception ex) { SetStatus($"Fehler: {ex.Message}", Color.Red); }
        finally { DisableServiceButtons(false); RefreshStartStop(); }
    }

    private void DisableServiceButtons(bool disable)
    {
        if (InvokeRequired) { BeginInvoke(() => DisableServiceButtons(disable)); return; }
        btnInstall.Enabled = btnUninstall.Enabled = btnSvcStart.Enabled = btnSvcStop.Enabled = !disable;
    }

    private void RefreshStartStop()
    {
        if (InvokeRequired) { BeginInvoke(RefreshStartStop); return; }
        bool running = _proc is { HasExited: false };
        btnStartStop.Text      = running ? "⏹ EXE stoppen" : "▶ EXE starten";
        btnStartStop.BackColor = running ? Color.LightCoral : Color.LightGreen;
    }

    // ══════════════════════════════════════════════════════════════
    // Hilfsmethoden
    // ══════════════════════════════════════════════════════════════
    private void SetStatus(string msg, Color color)
    {
        if (InvokeRequired) { BeginInvoke(() => SetStatus(msg, color)); return; }
        lblStatus.Text = msg; lblStatus.ForeColor = color;
    }

    private static Button Btn(string text, int x, int y, int w, Color? bg = null)
    {
        var b = new Button { Text = text, Left = x, Top = y, Width = w, Height = 26 };
        if (bg.HasValue) b.BackColor = bg.Value;
        return b;
    }

    private TextBox AddText(Control p, string label, ref int y, string def = "")
    {
        AddLabel(p, label, y);
        var tb = new TextBox { Left = LabelW + Pad, Top = y, Width = CtrlW, Text = def };
        p.Controls.Add(tb); y += RowH; return tb;
    }

    private CheckBox AddCheck(Control p, string label, ref int y, bool def)
    {
        var cb = new CheckBox { Text = label, Left = LabelW + Pad, Top = y, Width = CtrlW + 40, Checked = def };
        p.Controls.Add(cb); y += RowH; return cb;
    }

    private static void AddLabel(Control p, string text, int y)
        => p.Controls.Add(new Label { Text = text + ":", Left = Pad, Top = y + 4, Width = LabelW, AutoSize = false });

    protected override void OnFormClosing(FormClosingEventArgs e)
    {
        _timer.Stop();
        base.OnFormClosing(e);
    }
}
