using System.ComponentModel;
using System.Diagnostics;
using System.Text.Json;
using ClawdDotNet.Core.Config;
using ClawdDotNet.Core.Engine;
using ClawdDotNet.Core.Scheduling;
using ClawdDotNet.Core.Storage;
using ClawdDotNet.Core.Tools;
using ClawdDotNet.Models;
using ClawdDotNet.Services;
using ClawdDotNet.UI;
using Microsoft.Extensions.Logging;
using Microsoft.Web.WebView2.Core;

namespace ClawdDotNet;

public partial class frm_main : Form
{
    // ─── Abhängigkeiten ───
    private readonly SettingsManager _settingsManager;
    private readonly InstanceConfig _instanceConfig;
    private readonly string _instancePath;
    private readonly string _logDirectory;
    private readonly ILoggerFactory _loggerFactory;
    private readonly ILogger _logger;
    private readonly ToolRegistry _toolRegistry;
    private readonly InstanceDirectoryManager _dirManager;
    private readonly AgentEngine? _agentEngine;

    /// <summary>
    /// Der einzige periodische Treiber (A1). Ersetzt die früheren AgentScheduler/
    /// ToolJobScheduler — geplante Läufe und Tool-Job-Polls sind jetzt Tasks.
    /// </summary>
    private readonly ClawdDotNet.Core.Tasks.TaskScanner? _taskScanner;

    /// <summary>
    /// A2: Freigabe-/Ablehnungs-Dienst. Die Review-Oberfläche (Liste offener Vorschläge,
    /// Freigeben/Ablehnen) ruft <see cref="ClawdDotNet.Core.Staging.StagingService"/> auf —
    /// die Anbindung ist die verbleibende Integration; die Dienst-API steht bereit.
    /// </summary>
    private readonly ClawdDotNet.Core.Staging.StagingService? _stagingService;

    // Der Lizenz-Teil ist mit der Deploymentcenter-Umstellung aus dieser Fassung
    // verschwunden: Startprüfung, laufende Nachprüfung und Notausschalter sitzen jetzt
    // in ClawdDotNet.App (LicenseGate, LicenseWatch) und in der Avalonia-Oberfläche.
    // Diese Datei ist nur noch Vorlage für die Portierung — siehe ClawdDotNet.slnx.

    // ─── Services ───
    private LiveLogViewerService? _logViewer;
    private OpenRouterStatusService? _statusService;
    private JobHistoryService? _jobHistoryService;
    private BackupScheduler? _backupScheduler;

    // ─── Chat ───
    private WebViewBridge? _bridge;
    private string? _selectedAgentId;
    private readonly Dictionary<string, frm_chat> _chatWindows = new();

    // ─── ViewModels ───
    private InstanceSettingsViewModel _instanceSettingsVm;
    private readonly BindingList<AgentListDisplayEntry> _agentListEntries = new();
    private readonly BindingList<JobDisplayEntry> _jobEntries = new();
    private readonly BindingList<ServiceDisplayEntry> _serviceEntries = new();
    private readonly BindingList<AgentToolDisplayEntry> _agentToolEntries = new();
    private readonly BindingList<JobHistoryEntry> _jobHistoryEntries = new();

    // ─── LastRun-Timestamps (thread-safe via UI thread) ───
    private readonly Dictionary<string, DateTime> _jobLastRunTimes = new();
    private bool _suppressAgentSelectionChanged;

    /// <summary>Setzt die Beenden-Rückfrage außer Kraft (Lizenz-Sperre/-Deaktivierung).</summary>
    private bool _forceClose;

    public frm_main(
        SettingsManager settingsManager,
        InstanceConfig instanceConfig,
        string instancePath,
        string logDirectory,
        ILoggerFactory loggerFactory,
        ToolRegistry toolRegistry,
        InstanceDirectoryManager dirManager,
        AgentEngine? agentEngine,
        ClawdDotNet.Core.Tasks.TaskScanner? taskScanner = null,
        ClawdDotNet.Core.Staging.StagingService? stagingService = null)
    {
        _settingsManager = settingsManager;
        _instanceConfig = instanceConfig;
        _instancePath = instancePath;
        _logDirectory = logDirectory;
        _loggerFactory = loggerFactory;
        _logger = loggerFactory.CreateLogger("ClawdDotNet.Host.MainForm");
        _toolRegistry = toolRegistry;
        _dirManager = dirManager;
        _agentEngine = agentEngine;
        _taskScanner = taskScanner;
        _stagingService = stagingService;
        _instanceSettingsVm = new InstanceSettingsViewModel(_instanceConfig);

        InitializeComponent();
        SetupForm();
    }

    // Parameterloser Konstruktor für Designer-Kompatibilität
    public frm_main()
    {
        _settingsManager = new SettingsManager();
        _instanceConfig = new InstanceConfig();
        _instancePath = "";
        _logDirectory = "./Logs";
        _loggerFactory = LoggerFactory.Create(_ => { });
        _logger = _loggerFactory.CreateLogger("ClawdDotNet.Host.MainForm");
        _toolRegistry = new ToolRegistry();
        _dirManager = new InstanceDirectoryManager("./Instances");
        _instanceSettingsVm = new InstanceSettingsViewModel(_instanceConfig);

        InitializeComponent();
    }

    // ═══════════════════════════════════════════════════
    //  SETUP
    // ═══════════════════════════════════════════════════

    private void SetupForm()
    {
        UpdateTitle();
        Shown += OnFormShown;
        SetupLogTab();
        SetupSettingsTab();
        SetupAgentSettingsTab();
        SetupWorkerTab();
        SetupBackupTab();
        SetupStatusStrip();
        SetupBuildInfo();
        WireEvents();

        FormClosing += OnFormClosing;

        _logger.LogInformation("MainForm initialisiert");
    }

    private void UpdateTitle()
    {
        Text = $"ClawdDotNet - {_instanceConfig.InstanceName}";
    }

    // ═══════════════════════════════════════════════════
    //  BACKUP TAB
    // ═══════════════════════════════════════════════════

    private void SetupBackupTab()
    {
        backupPanel.Initialize(
            _instancePath,
            _instanceConfig.InstanceName,
            _settingsManager,
            _loggerFactory.CreateLogger("ClawdDotNet.Backup"));

        _backupScheduler = new BackupScheduler(
            _instancePath,
            _instanceConfig.InstanceName,
            _settingsManager,
            _loggerFactory.CreateLogger("ClawdDotNet.Backup.Scheduler"));

        _backupScheduler.OnBackupCreated += _ => BeginInvoke(() => backupPanel.RefreshFromOutside());
        _backupScheduler.Start();
    }

    /// <summary>
    /// Verdrahtet alle Designer-Controls mit ihren Event-Handlern.
    /// Die Controls selbst werden im Designer erstellt.
    /// </summary>
    private void WireEvents()
    {
        // ─── Engine: Live-Chat-Updates (ToolJobs, AgentComm, etc.) ───
        if (_agentEngine is not null)
        {
            _agentEngine.OnChatEntryAdded += OnEngineChatEntryAdded;
            _agentEngine.OnRunCompleted += OnEngineRunCompleted;
        }

        // ─── Haupt-Toolbar ───
        btn_showInstanceManager.Click += OnShowInstanceManagerClick;
        btn_showInstanceManager_menu.Click += OnShowInstanceManagerClick;

        // ─── Agent-Toolbar ───
        btn_addAgent.Click += OnAddAgentClick;
        btn_removeAgent.Click += OnRemoveAgentClick;
        btn_runAgentNow.Click += OnRunAgentNowClick;
        btn_editIdentity.Click += OnEditIdentityClick;
        btn_editSoul.Click += OnEditSoulClick;
        btn_openAgentFolder.Click += OnOpenAgentFolderClick;

        // ─── Log-Toolbar ───
        btn_logfolder.Click += OnBtnLogFolderClick;

        // ─── Worker-Toolbar (Jobs) ───
        btn_addJob.Click += OnAddJobClick;
        btn_editJob.Click += OnEditJobClick;
        btn_removeJob.Click += OnRemoveJobClick;
        btn_runJob.Click += OnRunJobClick;
        btn_refreshJobs.Click += (_, _) => RefreshJobList();
        dgv_Jobs.CellDoubleClick += (_, _) => OnEditJobClick(null, EventArgs.Empty);

        // ─── Worker-Toolbar (Services) ───
        btn_addService.Click += OnAddServiceClick;
        btn_removeService.Click += OnRemoveServiceClick;
        btn_startService.Click += OnStartServiceClick;
        btn_stopService.Click += OnStopServiceClick;
    }

    private void OnShowInstanceManagerClick(object? sender, EventArgs e)
    {
        var manager = new frm_InstanceManager(_dirManager, _settingsManager);
        manager.ShowDialog(this);
        manager.Dispose();
    }

    // ═══════════════════════════════════════════════════
    //  CHAT TAB (WebView2 + Bridge)
    // ═══════════════════════════════════════════════════

    private void OnFormShown(object? sender, EventArgs e)
    {
        Shown -= OnFormShown;
        BeginInvoke(new Action(async () => await SetupChatTabAsync()));
    }

    private async Task SetupChatTabAsync()
    {
        try
        {
            _logger.LogInformation("WebView2 Initialisierung gestartet...");

            await webView_chat.EnsureCoreWebView2Async();

            var uiPath = EmbeddedUiManager.GetExtractedPath();
            _logger.LogInformation("EmbeddedUI Pfad: {Path}", uiPath);

            webView_chat.CoreWebView2.SetVirtualHostNameToFolderMapping(
                "ui.clwd.internal", uiPath,
                CoreWebView2HostResourceAccessKind.DenyCors);

            _bridge = new WebViewBridge(webView_chat,
                _loggerFactory.CreateLogger("ClawdDotNet.UI.Bridge"));
            _bridge.MessageReceived += OnBridgeMessage;

            webView_chat.CoreWebView2.NavigationCompleted += async (_, args) =>
            {
                if (args.IsSuccess)
                {
                    _logger.LogInformation("WebView2 Navigation abgeschlossen");
                    await PushAgentListAsync();
                }
                else
                {
                    _logger.LogWarning("WebView2 Navigation fehlgeschlagen: {Status}", args.WebErrorStatus);
                }
            };

            webView_chat.CoreWebView2.Navigate("https://ui.clwd.internal/overview.html");
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "WebView2 Initialisierung fehlgeschlagen");
            MessageBox.Show(
                $"WebView2 konnte nicht initialisiert werden:\n{ex.Message}",
                "Chat-Fehler", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }
    }

    private async Task PushAgentListAsync()
    {
        if (_bridge is null) return;

        var agents = _instanceConfig.Agents.Select(a => new
        {
            agentId = a.AgentId,
            displayName = a.DisplayName,
            model = a.Model,
            status = _agentEngine?.IsRunning(a.AgentId) == true ? "running" : "idle"
        }).ToArray();

        await _bridge.SendAsync(new BridgeMessage(
            Type: BridgeTypes.AgentListUpdate,
            Extra: agents));
    }

    private async void OnBridgeMessage(BridgeMessage msg)
    {
        if (InvokeRequired) { Invoke(() => OnBridgeMessage(msg)); return; }

        _logger.LogInformation("OnBridgeMessage: type={Type}, agentId={AgentId}, content={Content}",
            msg.Type, msg.AgentId, msg.Content);

        switch (msg.Type)
        {
            case BridgeTypes.UserMessage:
                var targetAgentId = msg.AgentId ?? _selectedAgentId;
                _logger.LogInformation("UserMessage: targetAgentId={Target}, content={Content}, engineNull={EngineNull}",
                    targetAgentId, msg.Content, _agentEngine is null);
                if (targetAgentId is null || msg.Content is null) break;
                _selectedAgentId = targetAgentId;
                await HandleUserChatMessageAsync(targetAgentId, msg.Content);
                break;

            case BridgeTypes.SelectAgent:
                _selectedAgentId = msg.AgentId;
                if (msg.AgentId is not null)
                    await LoadChatHistoryAsync(msg.AgentId);
                break;

            case BridgeTypes.OpenAgentChat:
                if (msg.AgentId is not null)
                    OpenChatWindow(msg.AgentId);
                break;

            case BridgeTypes.RunNow:
                if (msg.AgentId is not null)
                    await RunAgentFromChatAsync(msg.AgentId);
                break;

            case BridgeTypes.AbortRun:
                if (msg.AgentId is not null)
                    _agentEngine?.AbortChat(msg.AgentId);
                break;
        }
    }

    /// <summary>
    /// Wird vom AgentEngine bei jedem neuen ChatEntry ausgelöst (User + Assistant).
    /// Pusht die Nachricht live an die WebView — funktioniert für alle Quellen:
    /// User-Chat, ToolJob-Wakeups, AgentComm, AgentSpawn.
    /// </summary>
    private void OnEngineRunCompleted(string model, AgentRunResult result)
    {
        // Echte Aufteilung statt 50/50: In Agenten-Loops liegt das Verhältnis eher bei
        // 95:5, und da Ausgabe-Tokens ein Vielfaches kosten, war die alte Schätzung
        // um ein Mehrfaches daneben.
        _statusService?.RecordUsage(model, result.PromptTokens, result.CompletionTokens);

        if (IsDisposed || !IsHandleCreated) return;
        BeginInvoke(() =>
        {
            // Track agent LastRun timestamp
            _jobLastRunTimes[$"agent_{result.AgentId}"] = DateTime.Now;

            // Add to job history
            var agentName = _instanceConfig.Agents.FirstOrDefault(a => a.AgentId == result.AgentId)?.DisplayName ?? result.AgentId;
            var historyEntry = new JobHistoryEntry
            {
                JobName = "Agent Wakeup",
                Agent = agentName,
                Time = DateTime.Now,
                JobDescription = $"Run ({result.StepCount} Steps, {result.TokensUsed:N0} Tokens)",
                Info = $"{result.Status} in {result.Duration:mm\\:ss}",
                Status = result.Status == AgentRunStatus.Completed ? "Success" : "Error"
            };
            _jobHistoryService?.Add(historyEntry);
            _jobHistoryEntries.Insert(0, historyEntry);
            while (_jobHistoryEntries.Count > 200)
                _jobHistoryEntries.RemoveAt(_jobHistoryEntries.Count - 1);

            _dirManager.AppendTokenUsage(_instancePath, new TokenUsageRecord
            {
                AgentId = result.AgentId,
                AgentName = agentName,
                Model = model,
                PromptTokens = result.TokensUsed / 2,
                CompletionTokens = result.TokensUsed / 2,
                TotalTokens = result.TokensUsed,
                Status = result.Status.ToString(),
                StepCount = result.StepCount,
                DurationMs = (long)result.Duration.TotalMilliseconds
            });
        });
    }

    private async void OnEngineChatEntryAdded(string agentId, string role, string content, string? source)
    {
        if (_bridge is null) return;

        try
        {
            await _bridge.SendAsync(new BridgeMessage(
                Type: BridgeTypes.ChatMessage,
                AgentId: agentId,
                Content: content,
                Extra: new { role, timestamp = DateTime.Now, source }));
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Fehler beim Senden der Chat-Nachricht an WebView: Agent={AgentId}", agentId);
        }
    }

    private async Task HandleUserChatMessageAsync(string agentId, string text)
    {
        if (_agentEngine is null || _bridge is null) return;

        var agentConfig = _instanceConfig.Agents.FirstOrDefault(a => a.AgentId == agentId);
        if (agentConfig is null) return;

        // Typing-Indicator anzeigen während der Agent arbeitet
        await _bridge.SendAsync(new BridgeMessage(
            Type: BridgeTypes.ChatTyping, AgentId: agentId));

        // ChatAsync im Hintergrund — User- und Assistant-Nachrichten
        // kommen automatisch über OnChatEntryAdded an die WebView
        _ = Task.Run(async () =>
        {
            try
            {
                await _agentEngine.ChatAsync(
                    agentConfig, text, _instanceConfig.InstanceId, CancellationToken.None,
                    source: ChatSource.WebView);
            }
            catch (Exception ex)
            {
                _logger.LogError(ex, "Chat-Fehler für Agent {AgentId}", agentId);
                await _bridge.SendAsync(new BridgeMessage(
                    Type: BridgeTypes.ChatMessage,
                    AgentId: agentId,
                    Content: $"[Fehler: {ex.Message}]",
                    Extra: new { role = "assistant", timestamp = DateTime.Now }));
            }
        });
    }

    private async Task LoadChatHistoryAsync(string agentId)
    {
        if (_agentEngine is null || _bridge is null) return;

        var history = _agentEngine.GetChatHistory(agentId);
        await _bridge.SendAsync(new BridgeMessage(
            Type: BridgeTypes.ChatHistory,
            AgentId: agentId,
            Extra: history.Select(e => new
            {
                role = e.Role,
                content = e.Content,
                timestamp = e.Timestamp
            }).ToArray()));
    }

    private async Task RunAgentFromChatAsync(string agentId)
    {
        if (_agentEngine is null) return;

        var agentConfig = _instanceConfig.Agents.FirstOrDefault(a => a.AgentId == agentId);
        if (agentConfig is null) return;

        await HandleUserChatMessageAsync(agentId, "Führe deine zugewiesenen Aufgaben aus.");
    }

    private void OpenChatWindow(string agentId)
    {
        if (_chatWindows.TryGetValue(agentId, out var existing) && !existing.IsDisposed)
        {
            existing.BringToFront();
            return;
        }

        var agentConfig = _instanceConfig.Agents.FirstOrDefault(a => a.AgentId == agentId);
        if (agentConfig is null) return;

        var frm = new frm_chat(agentConfig, _agentEngine!, _instanceConfig.InstanceId,
            _loggerFactory);
        frm.FormClosed += (_, _) => _chatWindows.Remove(agentId);
        _chatWindows[agentId] = frm;
        frm.Show(this);
    }

    // ═══════════════════════════════════════════════════
    //  LOG TAB
    // ═══════════════════════════════════════════════════

    private void SetupLogTab()
    {
        rtb_log.ReadOnly = true;
        rtb_log.BackColor = Color.FromArgb(30, 30, 30);
        rtb_log.ForeColor = Color.LightGreen;
        rtb_log.Font = new Font("Consolas", 9f);

        cb_LogLevel.Items.AddRange(["Alle", "Info", "Warn", "Error"]);
        cb_LogLevel.SelectedIndex = 0;
        cb_LogLevel.SelectedIndexChanged += OnLogLevelFilterChanged;

        RefreshLogModuleList();
        cb_LogModule.SelectedIndexChanged += OnLogModuleFilterChanged;

        var refreshInterval = _settingsManager.AppSettings.LogRefreshIntervalMs;
        var maxLines = _settingsManager.AppSettings.MaxLogLinesInUi;
        _logViewer = new LiveLogViewerService(_logDirectory, rtb_log, refreshInterval, maxLines);
        _logViewer.Start();
    }

    private void RefreshLogModuleList()
    {
        cb_LogModule.Items.Clear();

        if (_logViewer is not null)
        {
            foreach (var module in _logViewer.GetAvailableModules())
                cb_LogModule.Items.Add(module);
        }
        else
        {
            cb_LogModule.Items.Add("Alle");
            cb_LogModule.Items.Add("Core");
        }

        cb_LogModule.SelectedIndex = 0;
    }

    private void OnLogModuleFilterChanged(object? sender, EventArgs e)
    {
        var selected = cb_LogModule.SelectedItem?.ToString() ?? "Alle";
        _logViewer?.SetModuleFilter(selected);
    }

    private void OnLogLevelFilterChanged(object? sender, EventArgs e)
    {
        var selected = cb_LogLevel.SelectedItem?.ToString() ?? "Alle";
        _logViewer?.SetLevelFilter(selected);
    }

    private void OnBtnLogFolderClick(object? sender, EventArgs e)
    {
        var fullPath = Path.GetFullPath(_logDirectory);

        if (Directory.Exists(fullPath))
        {
            Process.Start(new ProcessStartInfo
            {
                FileName = fullPath,
                UseShellExecute = true
            });
        }
        else
        {
            MessageBox.Show($"Log-Verzeichnis existiert nicht:\n{fullPath}",
                "Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Information);
        }
    }

    // ═══════════════════════════════════════════════════
    //  SETTINGS TAB
    // ═══════════════════════════════════════════════════

    private void SetupSettingsTab()
    {
        pg_appsettings.SelectedObject = _settingsManager.AppSettings;
        pg_appsettings.PropertyValueChanged += OnAppSettingsChanged;

        pg_instancesettings.SelectedObject = _instanceSettingsVm;
        pg_instancesettings.PropertyValueChanged += OnInstanceSettingsChanged;
    }

    private void OnAppSettingsChanged(object? sender, PropertyValueChangedEventArgs e)
    {
        _settingsManager.Save();
        _logger.LogInformation("App-Settings geändert: {Property}", e.ChangedItem?.Label);
    }

    private void OnInstanceSettingsChanged(object? sender, PropertyValueChangedEventArgs e)
    {
        SaveInstanceConfig();
        UpdateTitle();
        _logger.LogInformation("Instance-Settings geändert: {Property}", e.ChangedItem?.Label);
    }

    // ═══════════════════════════════════════════════════
    //  AGENT SETTINGS TAB
    // ═══════════════════════════════════════════════════

    private void SetupAgentSettingsTab()
    {
        RefreshAgentList();
        dgv_agentlist.DataSource = _agentListEntries;
        dgv_agentlist.SelectionChanged += OnAgentSelectionChanged;

        pg_agentsettings.PropertyValueChanged += OnAgentSettingsChanged;

        dgv_agentTools.DataSource = _agentToolEntries;
        dgv_agentTools.SelectionChanged += OnToolSelectionChanged;
        dgv_agentTools.CurrentCellDirtyStateChanged += (_, _) =>
        {
            if (dgv_agentTools.IsCurrentCellDirty)
                dgv_agentTools.CommitEdit(DataGridViewDataErrorContexts.Commit);
        };
        dgv_agentTools.CellValueChanged += OnToolAssignedToggle;

        if (dgv_agentlist.Rows.Count > 0)
            dgv_agentlist.Rows[0].Selected = true;
    }

    private void RefreshAgentList()
    {
        _agentListEntries.Clear();

        foreach (var agent in _instanceConfig.Agents)
        {
            _agentListEntries.Add(new AgentListDisplayEntry
            {
                AgentId = agent.AgentId,
                DisplayName = agent.DisplayName,
                Model = agent.Model,
                ToolCount = agent.Tools.Count,
                HasIdentity = !string.IsNullOrWhiteSpace(agent.Identity) ? "Ja" : "—",
                HasSoul = !string.IsNullOrWhiteSpace(agent.Soul) ? "Ja" : "—",
                Status = _agentEngine?.IsRunning(agent.AgentId) == true ? "Läuft" : "Bereit"
            });
        }
    }

    private void OnAgentSelectionChanged(object? sender, EventArgs e)
    {
        if (_suppressAgentSelectionChanged) return;

        if (dgv_agentlist.SelectedRows.Count == 0)
        {
            pg_agentsettings.SelectedObject = null;
            pg_agentToolSettings.SelectedObject = null;
            _agentToolEntries.Clear();
            return;
        }

        var index = dgv_agentlist.SelectedRows[0].Index;
        if (index < 0 || index >= _instanceConfig.Agents.Count)
            return;

        var agentConfig = _instanceConfig.Agents[index];
        pg_agentsettings.SelectedObject = new AgentSettingsViewModel(agentConfig);
        RefreshAgentToolList(agentConfig);
    }

    private void RefreshAgentListKeepSelection()
    {
        var selectedIndex = dgv_agentlist.SelectedRows.Count > 0 ? dgv_agentlist.SelectedRows[0].Index : -1;

        _suppressAgentSelectionChanged = true;
        RefreshAgentList();
        if (selectedIndex >= 0 && selectedIndex < dgv_agentlist.Rows.Count)
            dgv_agentlist.Rows[selectedIndex].Selected = true;
        _suppressAgentSelectionChanged = false;
    }

    private void OnAgentSettingsChanged(object? sender, PropertyValueChangedEventArgs e)
    {
        SaveInstanceConfig();
        RefreshAgentListKeepSelection();
        _logger.LogInformation("Agent-Settings geändert: {Property}", e.ChangedItem?.Label);
    }

    private void OnAddAgentClick(object? sender, EventArgs e)
    {
        var name = Microsoft.VisualBasic.Interaction.InputBox(
            "Name des neuen Agenten:", "Agent hinzufügen", "NeuerAgent");

        if (string.IsNullOrWhiteSpace(name))
            return;

        try
        {
            _dirManager.CreateAgent(_instancePath, name);
            ReloadAgentsFromDisk();
            _logger.LogInformation("Agent hinzugefügt: {Name}", name);
        }
        catch (Exception ex)
        {
            MessageBox.Show($"Fehler: {ex.Message}", "Agent erstellen",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void OnRemoveAgentClick(object? sender, EventArgs e)
    {
        if (dgv_agentlist.SelectedRows.Count == 0)
            return;

        var index = dgv_agentlist.SelectedRows[0].Index;
        if (index < 0 || index >= _instanceConfig.Agents.Count)
            return;

        var agent = _instanceConfig.Agents[index];
        var folderName = InstanceDirectoryManager.BuildAgentFolderName(agent.DisplayName);

        var result = MessageBox.Show(
            $"Agent '{agent.DisplayName}' ({agent.AgentId}) wirklich entfernen?\n\n" +
            "Der gesamte Agent-Ordner wird gelöscht (inkl. Workspace, Logs, Soul, Identity)!",
            "Agent entfernen", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);

        if (result != DialogResult.Yes)
            return;

        try
        {
            _dirManager.RemoveAgent(_instancePath, folderName);
            ReloadAgentsFromDisk();
            _logger.LogInformation("Agent entfernt: {AgentId}", agent.AgentId);
        }
        catch (Exception ex)
        {
            MessageBox.Show($"Fehler: {ex.Message}", "Agent entfernen",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void OnEditIdentityClick(object? sender, EventArgs e)
    {
        var (agent, agentDir) = GetSelectedAgentAndDir();
        if (agent is null || agentDir is null) return;

        var identityPath = Path.Combine(agentDir, "Identity.md");
        OpenFileInEditor(identityPath);
    }

    private void OnEditSoulClick(object? sender, EventArgs e)
    {
        var (agent, agentDir) = GetSelectedAgentAndDir();
        if (agent is null || agentDir is null) return;

        var soulPath = Path.Combine(agentDir, "Soul.md");
        OpenFileInEditor(soulPath);
    }

    private void OnOpenAgentFolderClick(object? sender, EventArgs e)
    {
        var (agent, agentDir) = GetSelectedAgentAndDir();
        if (agent is null || agentDir is null) return;

        if (Directory.Exists(agentDir))
        {
            Process.Start(new ProcessStartInfo
            {
                FileName = agentDir,
                UseShellExecute = true
            });
        }
    }

    private (AgentConfig? agent, string? agentDir) GetSelectedAgentAndDir()
    {
        if (dgv_agentlist.SelectedRows.Count == 0)
            return (null, null);

        var index = dgv_agentlist.SelectedRows[0].Index;
        if (index < 0 || index >= _instanceConfig.Agents.Count)
            return (null, null);

        var agent = _instanceConfig.Agents[index];
        return (agent, agent.AgentDir);
    }

    private static void OpenFileInEditor(string filePath)
    {
        if (!File.Exists(filePath))
        {
            MessageBox.Show($"Datei nicht gefunden:\n{filePath}",
                "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return;
        }

        Process.Start(new ProcessStartInfo
        {
            FileName = filePath,
            UseShellExecute = true
        });
    }

    private async void OnRunAgentNowClick(object? sender, EventArgs e)
    {
        if (_agentEngine is null)
        {
            MessageBox.Show("Kein API-Key konfiguriert – Agenten können nicht ausgeführt werden.",
                "Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Warning);
            return;
        }

        if (dgv_agentlist.SelectedRows.Count == 0)
            return;

        var index = dgv_agentlist.SelectedRows[0].Index;
        if (index < 0 || index >= _instanceConfig.Agents.Count)
            return;

        var agentConfig = _instanceConfig.Agents[index];

        // Vor dem Run: Identity/Soul von Disk nachladen
        var freshConfig = _dirManager.LoadAgentConfig(agentConfig.AgentDir);
        agentConfig.Identity = freshConfig.Identity;
        agentConfig.Soul = freshConfig.Soul;

        _logger.LogInformation("Manueller Run gestartet: {AgentId}", agentConfig.AgentId);

        try
        {
            using var cts = new CancellationTokenSource(agentConfig.LoopGuard.Timeout);
            var result = await _agentEngine.RunAsync(
                agentConfig, "Manual execution triggered by user.", _instanceConfig.InstanceId, cts.Token);

            RefreshAgentList();

            MessageBox.Show(
                $"Agent-Run abgeschlossen:\n" +
                $"Status: {result.Status}\n" +
                $"Schritte: {result.StepCount}\n" +
                $"Tokens: {result.TokensUsed}\n" +
                $"Dauer: {result.Duration:hh\\:mm\\:ss\\.fff}",
                "Run Ergebnis", MessageBoxButtons.OK,
                result.Status == AgentRunStatus.Completed ? MessageBoxIcon.Information : MessageBoxIcon.Warning);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Manueller Run fehlgeschlagen: {AgentId}", agentConfig.AgentId);
            MessageBox.Show($"Fehler: {ex.Message}", "Run fehlgeschlagen",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
        }
    }

    private void ReloadAgentsFromDisk()
    {
        var freshConfig = _dirManager.LoadInstanceConfig(_instancePath);

        _instanceConfig.Agents.Clear();
        foreach (var agent in freshConfig.Agents)
            _instanceConfig.Agents.Add(agent);

        RefreshAgentList();
    }

    // ═══════════════════════════════════════════════════
    //  AGENT TOOLS
    // ═══════════════════════════════════════════════════

    private void RefreshAgentToolList(AgentConfig agentConfig)
    {
        _agentToolEntries.Clear();
        pg_agentToolSettings.SelectedObject = null;

        foreach (var tool in _toolRegistry.GetAll())
        {
            _agentToolEntries.Add(new AgentToolDisplayEntry
            {
                Assigned = agentConfig.Tools.ContainsKey(tool.Name),
                ToolName = tool.Name,
                Description = tool.Description
            });
        }
    }

    private void OnToolAssignedToggle(object? sender, DataGridViewCellEventArgs e)
    {
        if (e.RowIndex < 0 || e.ColumnIndex != dgv_agentTools.Columns["col_ToolAssigned2"]?.Index)
            return;

        var agentConfig = GetSelectedAgentConfig();
        if (agentConfig is null) return;

        var entry = _agentToolEntries[e.RowIndex];
        var isAssigned = entry.Assigned;

        if (isAssigned)
        {
            if (!agentConfig.Tools.ContainsKey(entry.ToolName))
                agentConfig.Tools[entry.ToolName] = new Dictionary<string, object?>();
        }
        else
        {
            agentConfig.Tools.Remove(entry.ToolName);
            pg_agentToolSettings.SelectedObject = null;
        }

        SaveInstanceConfig();
        RefreshAgentListKeepSelection();

        _logger.LogInformation("Tool {Tool} für Agent {Agent}: {State}",
            entry.ToolName, agentConfig.DisplayName, isAssigned ? "zugewiesen" : "entfernt");
    }

    private void OnToolSelectionChanged(object? sender, EventArgs e)
    {
        if (dgv_agentTools.SelectedRows.Count == 0)
        {
            pg_agentToolSettings.SelectedObject = null;
            return;
        }

        var agentConfig = GetSelectedAgentConfig();
        if (agentConfig is null) return;

        var toolEntry = _agentToolEntries[dgv_agentTools.SelectedRows[0].Index];

        if (!agentConfig.Tools.TryGetValue(toolEntry.ToolName, out var config))
        {
            pg_agentToolSettings.SelectedObject = null;
            return;
        }

        var viewModel = ToolSettingsFactory.CreateViewModel(toolEntry.ToolName, config);
        pg_agentToolSettings.SelectedObject = viewModel;

        pg_agentToolSettings.PropertyValueChanged -= OnToolSettingsChanged;
        pg_agentToolSettings.PropertyValueChanged += OnToolSettingsChanged;
    }

    private void OnToolSettingsChanged(object? sender, PropertyValueChangedEventArgs e)
    {
        var agentConfig = GetSelectedAgentConfig();
        if (agentConfig is null) return;

        if (dgv_agentTools.SelectedRows.Count == 0) return;
        var toolEntry = _agentToolEntries[dgv_agentTools.SelectedRows[0].Index];

        var newConfig = ToolSettingsFactory.ToConfig(toolEntry.ToolName, pg_agentToolSettings.SelectedObject);
        if (newConfig is not null)
        {
            agentConfig.Tools[toolEntry.ToolName] = newConfig;
            SaveInstanceConfig();
            _logger.LogInformation("Tool-Settings geändert: {Tool}.{Property}", toolEntry.ToolName, e.ChangedItem?.Label);
        }
    }

    private AgentConfig? GetSelectedAgentConfig()
    {
        if (dgv_agentlist.SelectedRows.Count == 0) return null;
        var index = dgv_agentlist.SelectedRows[0].Index;
        return index >= 0 && index < _instanceConfig.Agents.Count ? _instanceConfig.Agents[index] : null;
    }

    // ═══════════════════════════════════════════════════
    //  WORKER TAB (Jobs & Services)
    // ═══════════════════════════════════════════════════

    private void SetupWorkerTab()
    {
        // Columns are now defined in Designer — just bind data sources
        dgv_Jobs.DataSource = _jobEntries;
        dgv_services.DataSource = _serviceEntries;

        // ─── Job History ───
        _jobHistoryService = new JobHistoryService(_instancePath);
        dgv_jobhistory.DataSource = _jobHistoryEntries;
        RefreshJobHistoryGrid();

        // Tool-Job-Ticks laufen jetzt über den Scanner (Tasks); ihre Historie ergibt sich
        // aus dem Audit-Log/den Receipts (A3), nicht mehr aus einem Scheduler-Event.

        EnsureBuiltInServices();
        RefreshJobList();
        RefreshServiceList();
    }

    private void RefreshJobHistoryGrid()
    {
        _jobHistoryEntries.Clear();
        if (_jobHistoryService is null) return;

        foreach (var entry in _jobHistoryService.GetAll().Take(200))
            _jobHistoryEntries.Add(entry);
    }

    private void EnsureBuiltInServices()
    {
        var defaults = BuiltInServices.CreateDefaults();
        var changed = false;

        foreach (var def in defaults)
        {
            if (_instanceConfig.Services.Any(s => s.ServiceId == def.ServiceId))
                continue;

            _instanceConfig.Services.Insert(0, def);
            changed = true;
        }

        if (changed)
            SaveInstanceConfig();
    }

    private void OnAddJobClick(object? sender, EventArgs e)
    {
        using var dlg = new frm_AddJob(_instanceConfig.Agents, _toolRegistry);
        if (dlg.ShowDialog(this) != DialogResult.OK) return;

        var agent = _instanceConfig.Agents.FirstOrDefault(a => a.AgentId == dlg.SelectedAgentId);
        if (agent is null) return;

        if (dlg.IsToolJob)
        {
            agent.ToolJobs.Add(new Core.Config.ToolJobConfig
            {
                ToolName = dlg.SelectedToolName,
                JobTypeId = dlg.SelectedJobTypeId,
                Cron = dlg.CronExpression,
                RunOnStart = dlg.RunOnStart,
                Enabled = true
            });

            _logger.LogInformation("Tool-Job hinzugefügt: Agent={Agent}, Tool={Tool}, Cron={Cron}",
                agent.DisplayName, dlg.SelectedToolName, dlg.CronExpression);
        }
        else
        {
            agent.Scheduler = new Core.Config.SchedulerConfig
            {
                Cron = dlg.CronExpression,
                RunOnStart = dlg.RunOnStart,
                TaskMessage = dlg.TaskMessage
            };

            _logger.LogInformation("Agent-Wakeup-Job hinzugefügt: Agent={Agent}, Cron={Cron}",
                agent.DisplayName, dlg.CronExpression);
        }

        SaveInstanceConfig();
        RefreshJobList();
    }

    private void OnEditJobClick(object? sender, EventArgs e)
    {
        if (dgv_Jobs.CurrentRow?.DataBoundItem is not JobDisplayEntry entry) return;

        var agent = _instanceConfig.Agents.FirstOrDefault(a => a.AgentId == entry.AgentId);
        if (agent is null) return;

        var isToolJob = entry.JobType == "Tool Job";

        using var dlg = new frm_AddJob(
            _instanceConfig.Agents, _toolRegistry,
            agentId: entry.AgentId,
            cron: entry.CronExpression,
            taskMessage: entry.TaskMessage,
            runOnStart: entry.RunOnStart,
            isToolJob: isToolJob,
            toolName: isToolJob ? entry.ToolName : null,
            jobTypeId: isToolJob ? entry.TaskMessage : null); // TaskMessage enthält bei ToolJobs die JobTypeId

        if (dlg.ShowDialog(this) != DialogResult.OK) return;

        if (isToolJob)
        {
            var toolJob = agent.ToolJobs.FirstOrDefault(j => j.JobId == entry.JobId);
            if (toolJob is null) return;

            toolJob.Cron = dlg.CronExpression;
            toolJob.RunOnStart = dlg.RunOnStart;

            _logger.LogInformation("Tool-Job bearbeitet: Agent={Agent}, Job={JobId}, Cron={Cron}",
                agent.DisplayName, toolJob.JobId, dlg.CronExpression);
        }
        else
        {
            if (agent.Scheduler is null) return;

            agent.Scheduler.Cron = dlg.CronExpression;
            agent.Scheduler.TaskMessage = dlg.TaskMessage;
            agent.Scheduler.RunOnStart = dlg.RunOnStart;

            _logger.LogInformation("Agent-Wakeup-Job bearbeitet: Agent={Agent}, Cron={Cron}",
                agent.DisplayName, dlg.CronExpression);
        }

        SaveInstanceConfig();
        RefreshJobList();
    }

    private void OnRemoveJobClick(object? sender, EventArgs e)
    {
        if (dgv_Jobs.CurrentRow?.DataBoundItem is not JobDisplayEntry entry) return;

        var agent = _instanceConfig.Agents.FirstOrDefault(a => a.AgentId == entry.AgentId);
        if (agent is null) return;

        var label = entry.JobType == "Tool Job"
            ? $"Tool-Job '{entry.ToolName}' für Agent '{agent.DisplayName}'"
            : $"Agent-Wakeup-Job für '{agent.DisplayName}'";

        var result = MessageBox.Show(
            $"{label} wirklich entfernen?",
            "Job entfernen", MessageBoxButtons.YesNo, MessageBoxIcon.Question);
        if (result != DialogResult.Yes) return;

        if (entry.JobType == "Tool Job")
        {
            agent.ToolJobs.RemoveAll(j => j.JobId == entry.JobId);
        }
        else
        {
            agent.Scheduler = null;
        }

        SaveInstanceConfig();
        RefreshJobList();
    }

    private async void OnRunJobClick(object? sender, EventArgs e)
    {
        if (dgv_Jobs.CurrentRow?.DataBoundItem is not JobDisplayEntry entry) return;

        var agent = _instanceConfig.Agents.FirstOrDefault(a => a.AgentId == entry.AgentId);
        if (agent is null) return;

        if (entry.JobType == "Tool Job")
        {
            if (_taskScanner is null)
            {
                MessageBox.Show("Scanner ist nicht aktiv.", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            var jobConfig = agent.ToolJobs.FirstOrDefault(j => j.JobId == entry.JobId);
            if (jobConfig is null) return;

            btn_runJob.Enabled = false;
            btn_runJob.Text = "⏳ Läuft...";
            try
            {
                // Poll-Jobs sind jetzt Tasks (Id: tj-<agent>-<job>); der Scanner führt sie aus.
                var ran = await _taskScanner.RunTaskNowAsync(
                    $"tj-{agent.AgentId}-{jobConfig.JobId}", CancellationToken.None);
                _jobLastRunTimes[jobConfig.JobId] = DateTime.Now;
                RefreshJobList();

                var status = ran ? "Ausgeführt" : "Nicht bereit (läuft evtl. schon oder ist deaktiviert)";

                _jobHistoryService?.Add(new JobHistoryEntry
                {
                    JobName = jobConfig.JobTypeId,
                    Agent = agent.DisplayName,
                    Time = DateTime.Now,
                    JobDescription = $"Tool Job: {jobConfig.ToolName}",
                    Info = status,
                    Status = "Manual"
                });
                RefreshJobHistoryGrid();

                MessageBox.Show(
                    $"Job '{jobConfig.JobTypeId}' abgeschlossen.\n\nErgebnis: {status}",
                    "Job ausgeführt", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                _jobHistoryService?.Add(new JobHistoryEntry
                {
                    JobName = jobConfig.JobTypeId,
                    Agent = agent.DisplayName,
                    Time = DateTime.Now,
                    JobDescription = $"Tool Job: {jobConfig.ToolName}",
                    Info = ex.Message,
                    Status = "Error"
                });
                RefreshJobHistoryGrid();

                MessageBox.Show($"Fehler beim Ausführen:\n{ex.Message}", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                btn_runJob.Text = "▶ Jetzt ausführen";
                btn_runJob.Enabled = true;
            }
        }
        else
        {
            // Agent Wakeup Job
            if (_agentEngine is null)
            {
                MessageBox.Show("Agent-Engine ist nicht aktiv.", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Warning);
                return;
            }

            var taskMessage = agent.Scheduler?.TaskMessage ?? "Führe deine zugewiesenen Aufgaben aus.";

            btn_runJob.Enabled = false;
            btn_runJob.Text = "⏳ Läuft...";
            try
            {
                var result = await _agentEngine.RunAsync(agent, taskMessage, _instanceConfig.InstanceId, CancellationToken.None);
                _jobLastRunTimes[$"agent_{agent.AgentId}"] = DateTime.Now;
                RefreshJobList();

                _jobHistoryService?.Add(new JobHistoryEntry
                {
                    JobName = "Agent Wakeup",
                    Agent = agent.DisplayName,
                    Time = DateTime.Now,
                    JobDescription = taskMessage,
                    Info = $"{result.Status} ({result.StepCount} Steps, {result.TokensUsed:N0} Tokens)",
                    Status = "Manual"
                });
                RefreshJobHistoryGrid();

                MessageBox.Show(
                    $"Agent '{agent.DisplayName}' abgeschlossen.\n\nStatus: {result.Status}\nTokens: {result.TokensUsed:N0}",
                    "Job ausgeführt", MessageBoxButtons.OK, MessageBoxIcon.Information);
            }
            catch (Exception ex)
            {
                _jobHistoryService?.Add(new JobHistoryEntry
                {
                    JobName = "Agent Wakeup",
                    Agent = agent.DisplayName,
                    Time = DateTime.Now,
                    JobDescription = taskMessage,
                    Info = ex.Message,
                    Status = "Error"
                });
                RefreshJobHistoryGrid();

                MessageBox.Show($"Fehler beim Ausführen:\n{ex.Message}", "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Error);
            }
            finally
            {
                btn_runJob.Text = "▶ Jetzt ausführen";
                btn_runJob.Enabled = true;
            }
        }
    }

    private void RefreshJobList()
    {
        _jobEntries.Clear();

        foreach (var agent in _instanceConfig.Agents)
        {
            // Agent Wakeup Jobs
            if (agent.Scheduler is not null)
            {
                string nextRun = "—";
                if (!string.IsNullOrWhiteSpace(agent.Scheduler.Cron))
                {
                    try
                    {
                        var cron = CronExpression.Parse(agent.Scheduler.Cron);
                        var next = cron.GetNextOccurrence(DateTime.Now);
                        nextRun = next?.ToString("yyyy-MM-dd HH:mm") ?? "—";
                    }
                    catch
                    {
                        nextRun = "Ungültig";
                    }
                }

                var agentLastRun = _jobLastRunTimes.TryGetValue($"agent_{agent.AgentId}", out var agentLastTime)
                    ? agentLastTime.ToString("yyyy-MM-dd HH:mm:ss")
                    : "—";

                _jobEntries.Add(new JobDisplayEntry
                {
                    JobType = "Agent Wakeup",
                    AgentId = agent.AgentId,
                    AgentName = agent.DisplayName,
                    CronExpression = agent.Scheduler.Cron,
                    TaskMessage = agent.Scheduler.TaskMessage,
                    RunOnStart = agent.Scheduler.RunOnStart,
                    NextRun = nextRun,
                    LastRun = agentLastRun,
                    LastStatus = "—",
                    Status = _taskScanner is not null ? "Aktiv" : "Inaktiv"
                });
            }

            // Tool Jobs
            foreach (var toolJob in agent.ToolJobs)
            {
                string nextRun = "—";
                if (!string.IsNullOrWhiteSpace(toolJob.Cron))
                {
                    try
                    {
                        var cron = CronExpression.Parse(toolJob.Cron);
                        var next = cron.GetNextOccurrence(DateTime.Now);
                        nextRun = next?.ToString("yyyy-MM-dd HH:mm") ?? "—";
                    }
                    catch
                    {
                        nextRun = "Ungültig";
                    }
                }

                var toolLastRun = _jobLastRunTimes.TryGetValue(toolJob.JobId, out var lastTime)
                    ? lastTime.ToString("yyyy-MM-dd HH:mm:ss")
                    : "—";

                _jobEntries.Add(new JobDisplayEntry
                {
                    JobType = "Tool Job",
                    JobId = toolJob.JobId,
                    AgentId = agent.AgentId,
                    AgentName = agent.DisplayName,
                    ToolName = toolJob.ToolName,
                    CronExpression = toolJob.Cron,
                    TaskMessage = toolJob.JobTypeId,
                    RunOnStart = toolJob.RunOnStart,
                    NextRun = nextRun,
                    LastRun = toolLastRun,
                    LastStatus = "—",
                    Status = toolJob.Enabled ? "Aktiv" : "Deaktiviert"
                });
            }
        }
    }

    private void RefreshServiceList()
    {
        _serviceEntries.Clear();

        foreach (var svc in _instanceConfig.Services)
        {
            _serviceEntries.Add(new ServiceDisplayEntry
            {
                ServiceId = svc.ServiceId,
                Name = svc.Name,
                Type = svc.Type,
                Port = svc.Port,
                Status = svc.Enabled ? "Bereit" : "Deaktiviert",
                Description = svc.Description
            });
        }
    }

    private void OnAddServiceClick(object? sender, EventArgs e)
    {
        using var dlg = new frm_AddService();
        if (dlg.ShowDialog(this) != DialogResult.OK)
            return;

        var svcConfig = new ServiceConfig
        {
            Name = dlg.ServiceName,
            Type = dlg.ServiceType,
            Port = dlg.ServicePort,
            Enabled = true,
            Description = dlg.ServiceDescription
        };

        _instanceConfig.Services.Add(svcConfig);
        SaveInstanceConfig();
        RefreshServiceList();

        _logger.LogInformation("Service hinzugefügt: {Name} ({Type}, Port {Port})",
            svcConfig.Name, svcConfig.Type, svcConfig.Port);
    }

    private void OnRemoveServiceClick(object? sender, EventArgs e)
    {
        if (dgv_services.SelectedRows.Count == 0)
            return;

        var index = dgv_services.SelectedRows[0].Index;
        if (index < 0 || index >= _instanceConfig.Services.Count)
            return;

        var svc = _instanceConfig.Services[index];

        if (svc.BuiltIn)
        {
            MessageBox.Show("Eingebaute Services können nicht entfernt werden.",
                "Hinweis", MessageBoxButtons.OK, MessageBoxIcon.Information);
            return;
        }

        var result = MessageBox.Show(
            $"Service '{svc.Name}' ({svc.Type}, Port {svc.Port}) wirklich entfernen?",
            "Service entfernen", MessageBoxButtons.YesNo, MessageBoxIcon.Warning);

        if (result != DialogResult.Yes)
            return;

        _instanceConfig.Services.RemoveAt(index);
        SaveInstanceConfig();
        RefreshServiceList();

        _logger.LogInformation("Service entfernt: {ServiceId}", svc.ServiceId);
    }

    private void OnStartServiceClick(object? sender, EventArgs e)
    {
        if (dgv_services.SelectedRows.Count == 0)
            return;

        var index = dgv_services.SelectedRows[0].Index;
        if (index < 0 || index >= _serviceEntries.Count)
            return;

        var entry = _serviceEntries[index];
        entry.Status = "Läuft";
        entry.StartedAt = DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");
        _serviceEntries.ResetItem(index);

        _logger.LogInformation("Service gestartet: {Name}", entry.Name);
    }

    private void OnStopServiceClick(object? sender, EventArgs e)
    {
        if (dgv_services.SelectedRows.Count == 0)
            return;

        var index = dgv_services.SelectedRows[0].Index;
        if (index < 0 || index >= _serviceEntries.Count)
            return;

        var entry = _serviceEntries[index];
        entry.Status = "Gestoppt";
        entry.StartedAt = "—";
        _serviceEntries.ResetItem(index);

        _logger.LogInformation("Service gestoppt: {Name}", entry.Name);
    }

    // ═══════════════════════════════════════════════════
    //  BUILD INFO
    // ═══════════════════════════════════════════════════

    private void SetupBuildInfo()
    {
        var sb = new System.Text.StringBuilder();

        foreach (var asm in AppDomain.CurrentDomain.GetAssemblies()
            .Where(a => a.GetName().Name?.StartsWith("ClawdDotNet") == true)
            .OrderBy(a => a.GetName().Name))
        {
            var name = asm.GetName().Name!;
            var shortName = name.Replace("ClawdDotNet.", "").Replace("Tools.", "");

            var buildDate = asm.GetCustomAttributes(typeof(System.Reflection.AssemblyMetadataAttribute), false)
                .OfType<System.Reflection.AssemblyMetadataAttribute>()
                .FirstOrDefault(a => a.Key == "BuildDate")?.Value ?? "?";

            var buildInfoType = asm.GetType(asm.GetName().Name + ".BuildInfo");
            var buildNum = buildInfoType?.GetField("Build")?.GetValue(null) as int? ?? 0;
            var changes = buildInfoType?.GetField("Changes")?.GetValue(null) as string ?? "";

            sb.AppendLine($"{shortName}  #B{buildNum}  ({buildDate})  –  {changes}");
        }

        label_Softwareinfo.Text = sb.ToString().TrimEnd();
        label_Softwareinfo.AutoSize = true;
    }

    // ═══════════════════════════════════════════════════
    //  STATUS STRIP
    // ═══════════════════════════════════════════════════

    private void SetupStatusStrip()
    {
        if (string.IsNullOrWhiteSpace(_instanceConfig.OpenRouterApiKey))
        {
            label_openRouterStatus.Text = "| Kein API-Key konfiguriert";
            label_openRouterStatus.ForeColor = Color.Orange;
            label_openRouterCredits.Text = "";
            label_openRouterCreditBalance.Text = "";
            return;
        }

        var checkInterval = _settingsManager.AppSettings.StatusCheckIntervalSeconds;
        _statusService = new OpenRouterStatusService(
            _instanceConfig.OpenRouterApiKey,
            _settingsManager.AppSettings.OpenRouterBaseUrl,
            checkInterval);

        label_openRouterCreditBalance.Text = "Lade...";

        _statusService.OnStatusUpdated += () =>
        {
            if (IsDisposed || !IsHandleCreated) return;

            BeginInvoke(() =>
            {
                label_openRouterStatus.Text = _statusService.StatusText;
                label_openRouterStatus.ForeColor = _statusService.IsApiReachable ? Color.FromArgb(0, 128, 0) : Color.Red;
                label_openRouterCredits.Text = _statusService.CreditsText;
                label_openRouterCredits.ToolTipText = _statusService.CreditsTooltip;

                // Credit Balance anzeigen
                if (_statusService.CreditRemaining.HasValue)
                    label_openRouterCreditBalance.Text = $"Balance: ${_statusService.CreditRemaining.Value:F4}";
                else
                    label_openRouterCreditBalance.Text = "Balance: ---";
            });
        };

        _statusService.Start();

        // Modellpreise einmalig laden. Ohne sie weist die Anzeige alle Kosten als
        // unbekannt aus, statt stillschweigend 0 € zu melden.
        if (ModelTypeConverter.Client is { } client)
            _ = _statusService.LoadPricingAsync(client);
    }

    // ═══════════════════════════════════════════════════
    //  CLOSING / PERSISTENZ
    // ═══════════════════════════════════════════════════

    private void OnFormClosing(object? sender, FormClosingEventArgs e)
    {
        // Erzwungenes Schließen (Lizenz-Sperre/-Deaktivierung) überspringt die Rückfrage.
        if (e.CloseReason == CloseReason.UserClosing && !_forceClose)
        {
            var result = MessageBox.Show(
                "ClawdDotNet ist für den 24/7-Betrieb ausgelegt.\n\n" +
                "Möchten Sie die Anwendung wirklich beenden?\n" +
                "Alle laufenden Agenten werden gestoppt.",
                $"ClawdDotNet – {_instanceConfig.InstanceName} beenden?",
                MessageBoxButtons.YesNo,
                MessageBoxIcon.Question,
                MessageBoxDefaultButton.Button2);

            if (result != DialogResult.Yes)
            {
                e.Cancel = true;
                return;
            }
        }

        _bridge?.Dispose();
        _logViewer?.Dispose();
        _statusService?.Dispose();
        _backupScheduler?.Dispose();
        _logger.LogInformation("MainForm wird geschlossen");
    }

    private void SaveInstanceConfig()
    {
        if (string.IsNullOrWhiteSpace(_instancePath))
            return;

        try
        {
            _dirManager.SaveInstanceConfig(_instancePath, _instanceConfig);

            foreach (var agent in _instanceConfig.Agents)
            {
                if (!string.IsNullOrWhiteSpace(agent.AgentDir) && Directory.Exists(agent.AgentDir))
                    _dirManager.SaveAgentSettings(agent.AgentDir, agent);
            }

            _logger.LogDebug("Instance-Config gespeichert: {Path}", _instancePath);
        }
        catch (Exception ex)
        {
            _logger.LogError(ex, "Fehler beim Speichern der Instance-Config");
            MessageBox.Show($"Config konnte nicht gespeichert werden:\n{ex.Message}",
                "Fehler", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }
    }

    private void btn_openAgentFolder_Click(object sender, EventArgs e)
    {
        SystemShell.OpenFolder(Path.Combine(_instancePath, "Agents"));
    }

    private void btn_showInstanceFolder_Click(object sender, EventArgs e)
    {
        SystemShell.OpenFolder(_instancePath);
    }

    private void btn_ShowLogFolder2_Click(object sender, EventArgs e)
    {
        // _logDirectory statt CurrentDirectory + "/Logs": Das Arbeitsverzeichnis ist
        // nicht zwingend das Programmverzeichnis, und die Instanz kann ein eigenes
        // Log-Ziel haben — geöffnet werden soll der Ordner, in den auch geschrieben wird.
        SystemShell.OpenFolder(Path.GetFullPath(_logDirectory));
    }

    private void dgv_Jobs_CellContentClick(object sender, DataGridViewCellEventArgs e)
    {

    }
}

// ═══════════════════════════════════════════════════
//  Hilfsklasse für die Agent-DataGridView
// ═══════════════════════════════════════════════════

public sealed class AgentListDisplayEntry
{
    public string AgentId { get; set; } = "";
    public string DisplayName { get; set; } = "";
    public string Model { get; set; } = "";
    public int ToolCount { get; set; }
    public string HasIdentity { get; set; } = "—";
    public string HasSoul { get; set; } = "—";
    public string Status { get; set; } = "Bereit";
}
