using ClawdDotNet.Core.Api;
using ClawdDotNet.Core.Config;
using ClawdDotNet.Core.Engine;
using ClawdDotNet.Core.Logging;
using ClawdDotNet.Core.Security;
using ClawdDotNet.Core.Tools;
using ClawdDotNet.Core.State;
using ClawdDotNet.Core.Storage;
using ClawdDotNet.Core.Memory;
using ClawdDotNet.Core.Accounting;
using ClawdDotNet.Services;
using ClawdDotNet.Tools.FileRW;
using ClawdDotNet.Tools.Telegram;
using ClawdDotNet.Tools.Mail;
using ClawdDotNet.Tools.Database;
using ClawdDotNet.Tools.FTP;
using ClawdDotNet.Tools.DirectAPI;
using ClawdDotNet.Tools.WebFetch;
using ClawdDotNet.Tools.WebMonitor;
using ClawdDotNet.Tools.AgentComm;
using ClawdDotNet.Tools.AgentSpawn;
using ClawdDotNet.Tools.SocialMediaManager;
using ClawdDotNet.Tools.AgentEditor;
using ClawdDotNet.Tools.TelegramClient;
using ClawdDotNet.UI;
using Microsoft.Extensions.Logging;

namespace ClawdDotNet;

internal static class Program
{
    [STAThread]
    static void Main(string[] args)
    {
        ApplicationConfiguration.Initialize();

        // ─── 0. Embedded UI extrahieren ───
        EmbeddedUiManager.ExtractToTemp();

        // ─── 1. App-Settings laden ───
        var settingsManager = new SettingsManager();
        settingsManager.Load();
        var appSettings = settingsManager.AppSettings;

        // ─── 2. InstanceDirectoryManager erstellen ───
        var instancesDir = Path.GetFullPath(appSettings.InstancesDirectory);
        var dirManager = new InstanceDirectoryManager(instancesDir);

        // ─── 3. Instanz-Verzeichnis auswählen ───
        string instancePath;

#if DEBUG
        // Im Debug-Modus: Dev-Instanz automatisch erstellen/starten
        const string devInstanceName = "Dev";
        instancePath = dirManager.GetInstancePath(devInstanceName);

        if (!Directory.Exists(instancePath))
        {
            dirManager.CreateInstance(devInstanceName);
        }
#else
        // Im Release-Modus: InstanceManager anzeigen
        var instanceManager = new frm_InstanceManager(dirManager, settingsManager);
        var dialogResult = instanceManager.ShowDialog();

        if (dialogResult != DialogResult.OK || string.IsNullOrWhiteSpace(instanceManager.SelectedInstancePath))
        {
            return; // Benutzer hat abgebrochen
        }

        instancePath = instanceManager.SelectedInstancePath;
        instanceManager.Dispose();
#endif

        // ─── 4. Instanz-Config aus Verzeichnisstruktur laden ───
        InstanceConfig instanceConfig;
        try
        {
            instanceConfig = dirManager.LoadInstanceConfig(instancePath);
        }
        catch (Exception ex)
        {
            MessageBox.Show(
                $"Fehler beim Laden der Instanz:\n{instancePath}\n\n{ex.Message}",
                "ClawdDotNet – Fehler",
                MessageBoxButtons.OK, MessageBoxIcon.Error);
            return;
        }

        // ─── 5. Logging-System initialisieren ───
        var logDir = Path.GetFullPath(
            !string.IsNullOrWhiteSpace(instanceConfig.LogDirectory)
                ? instanceConfig.LogDirectory
                : appSettings.LogDirectory);

        var minLevel = Enum.TryParse<ClawdDotNet.Core.Logging.LogLevel>(
            appSettings.MinimumLogLevel, true, out var ml)
            ? ml
            : ClawdDotNet.Core.Logging.LogLevel.Info;

        var loggerFactory = LoggingExtensions.CreateClawdLoggerFactory(logDir, minLevel);
        var coreLogger = loggerFactory.CreateLogger("ClawdDotNet.Core.Startup");

        coreLogger.LogInformation("ClawdDotNet startet – Instanz: {Instance} ({Id})",
            instanceConfig.InstanceName, instanceConfig.InstanceId);
        coreLogger.LogInformation("Instanz-Verzeichnis: {Path}", instancePath);

        // ─── 5b. Lizenzprüfung ───
        // Entfällt in dieser Fassung. Startprüfung, laufende Nachprüfung und die
        // Deploymentcenter-Anbindung (Watchdog, Updates, Fehler-Stream, Bugtracker)
        // liegen in ClawdDotNet.App (AppHost); Einstiegspunkt ist
        // src/ClawdDotNet.Desktop. Diese Datei ist nur noch Vorlage — siehe
        // ClawdDotNet.slnx.

        // ─── 6. Core-Komponenten erzeugen ───
        var toolRegistry = new ToolRegistry();
        toolRegistry.Register(new FileRWTool());
        toolRegistry.Register(new TelegramTool());
        toolRegistry.Register(new MailTool());
        toolRegistry.Register(new DatabaseTool());
        toolRegistry.Register(new FTPTool());
        toolRegistry.Register(new DirectApiTool());
        toolRegistry.Register(new WebFetchTool());
        toolRegistry.Register(new WebMonitorTool());
        toolRegistry.Register(new AgentCommTool());
        toolRegistry.Register(new SocialMediaManagerTool());
        toolRegistry.Register(new AgentSpawnTool());
        toolRegistry.Register(new AgentEditorTool());
        toolRegistry.Register(new ClawdDotNet.Tools.Memory.MemoryTool());
        toolRegistry.Register(new ClawdDotNet.Tools.Taskboard.TaskboardTool());

        // ─── 6a. TelegramClient (MTProto User-API) ───
        TelegramClientManager? tgClientManager = null;
        if (instanceConfig.TelegramClient is not null)
        {
            tgClientManager = new TelegramClientManager(
                instanceConfig,
                instancePath,
                loggerFactory.CreateLogger("ClawdDotNet.Tools.TelegramClient"));
            toolRegistry.Register(new TelegramClientTool(tgClientManager));
            coreLogger.LogInformation("TelegramClient-Tool registriert");
        }

        var permissionGate = new PermissionGate();

        // TODO: Tools aus tools/ Ordner laden
        // foreach (var toolDll in Directory.GetFiles("./tools", "*.dll"))
        //     LoadToolPlugin(toolDll, toolRegistry);

        OpenRouterClient? openRouterClient = null;
        AgentEngine? agentEngine = null;
        ClawdDotNet.Core.Tasks.TaskScanner? taskScanner = null;
        ClawdDotNet.Core.Staging.StagingService? stagingService = null;
        SqliteUsageRepository? usageRepository = null;

        if (!string.IsNullOrWhiteSpace(instanceConfig.OpenRouterApiKey))
        {
            openRouterClient = new OpenRouterClient(
                instanceConfig.OpenRouterApiKey,
                loggerFactory.CreateLogger("ClawdDotNet.Core.Api.OpenRouterClient"));

            // ModelTypeConverter mit dem Client verbinden für PropertyGrid-Dropdown
            ClawdDotNet.Models.ModelTypeConverter.Client = openRouterClient;

            // ─── 6.1. Speicher initialisieren ───
            // Eine Datenbank je Instanz; StateStore und Gedächtnis teilen sie sich.
            var storage = new SqliteStorage(Path.Combine(instancePath, "state.db"));
            var stateStore = new SqliteStateStore(storage);
            var memoryRepository = new SqliteMemoryRepository(storage);
            var taskRepository = new ClawdDotNet.Core.Tasks.SqliteTaskRepository(storage);
            var auditRepository = new ClawdDotNet.Core.Audit.SqliteAuditRepository(storage);
            var stagingRepository = new ClawdDotNet.Core.Staging.SqliteStagingRepository(storage);
            var stagingGate = new ClawdDotNet.Core.Staging.StagingGate(
                new ClawdDotNet.Core.Staging.StagingPolicy(), stagingRepository);
            usageRepository = new SqliteUsageRepository(storage);

            // Preise fürs Budget: Ohne sie greift nur die Token-Grenze.
            var pricingCatalog = new ModelPricingCatalog();
            _ = Task.Run(async () =>
            {
                try { pricingCatalog.Load(await openRouterClient.GetAvailableModelsAsync()); }
                catch { /* Ohne Preise bleibt die Kostengrenze wirkungslos, die Token-Grenze nicht. */ }
            });

            agentEngine = new AgentEngine(
                openRouterClient, toolRegistry, permissionGate, stateStore, loggerFactory,
                memoryRepository, usageRepository, pricingCatalog, taskRepository, auditRepository, stagingGate)
            {
                InstanceBudget = instanceConfig.Budget
            };
            agentEngine.SetAgentConfigProvider(
                () => instanceConfig.Agents,
                instanceConfig.InstanceId,
                agentId =>
                {
                    var agent = instanceConfig.Agents.FirstOrDefault(a => a.AgentId == agentId);
                    if (agent is null) return null;
                    return string.IsNullOrWhiteSpace(agent.AgentDir) ? null : agent.AgentDir;
                });
            agentEngine.LoadPersistedChats();

            // ─── Taskboard: Dateien in die DB spiegeln (Reconciliation beim Start) ───
            // Verwaiste Claims eines abgestürzten Laufs lösen, dann alle Task-Dateien
            // importieren. Nicht blockierend — der Start soll nicht auf das Dateisystem warten.
            var sharedWorkspace = instanceConfig.Agents
                .Select(a => a.SharedWorkspacePath)
                .FirstOrDefault(p => !string.IsNullOrWhiteSpace(p));
            if (!string.IsNullOrWhiteSpace(sharedWorkspace))
            {
                var board = new ClawdDotNet.Core.Tasks.TaskboardService(
                    taskRepository, Path.Combine(sharedWorkspace, "tasks"));

                // Staging (A2): Freigabe-/Ablehnungs-Dienst für die Review-Oberfläche.
                // Weckt den Agenten nach der Entscheidung über einen Folge-Task (A1).
                stagingService = new ClawdDotNet.Core.Staging.StagingService(
                    stagingRepository, agentEngine, board, loggerFactory, auditRepository);

                // Scanner (A1): der Taktgeber. Startet erst, wenn die Reconciliation durch
                // ist — sonst könnte er einen noch nicht bereinigten Claim antreffen.
                // Der Scanner ist der EINZIGE periodische Treiber (A1) — er ersetzt die
                // früheren AgentScheduler/ToolJobScheduler. Geplante Agent-Läufe wie auch
                // Tool-Job-Polls sind jetzt Tasks.
                var dispatcher = new ClawdDotNet.Core.Tasks.EngineTaskDispatcher(
                    agentEngine, () => instanceConfig.Agents, instanceConfig.InstanceId,
                    toolRegistry, stateStore, loggerFactory);
                taskScanner = new ClawdDotNet.Core.Tasks.TaskScanner(
                    taskRepository, dispatcher, loggerFactory);

                _ = Task.Run(async () =>
                {
                    try
                    {
                        var reset = await taskRepository.ReleaseStaleClaimsAsync(
                            DateTime.UtcNow.AddMinutes(-15), DateTime.UtcNow, CancellationToken.None);

                        // Einmalige Übernahme der alten coordination/task_*-Dateien …
                        var coordMigration = new ClawdDotNet.Core.Tasks.CoordinationMigration(
                            board, Path.Combine(sharedWorkspace, "coordination"), loggerFactory);
                        var migratedCoord = await coordMigration.RunAsync(CancellationToken.None);

                        // … und der alten scheduler/toolJobs-Konfiguration in Tasks.
                        var schedMigration = new ClawdDotNet.Core.Tasks.SchedulerTaskMigration(
                            board, taskRepository, loggerFactory);
                        var migratedSched = await schedMigration.RunAsync(instanceConfig.Agents, CancellationToken.None);

                        var imported = await board.ImportAllAsync(CancellationToken.None);
                        coreLogger.LogInformation(
                            "Taskboard bereit: {Imported} Aufgabe(n), migriert {Coord} coordination + {Sched} scheduler, {Reset} verwaiste Claims zurückgesetzt",
                            imported, migratedCoord, migratedSched, reset);
                        taskScanner.Start();
                    }
                    catch (Exception ex)
                    {
                        coreLogger.LogWarning(ex, "Taskboard-Reconciliation beim Start fehlgeschlagen");
                    }
                });
            }

            coreLogger.LogInformation("AgentEngine und Scanner erstellt, Chat-Verläufe geladen");
        }
        else
        {
            coreLogger.LogWarning("Kein OpenRouter API-Key konfiguriert – Agenten sind deaktiviert");
        }

        // ─── 6b. TelegramClient verbinden (Session oder interaktiver Login) ───
        if (tgClientManager is not null)
        {
            tgClientManager.OnLoginCodeRequired += async (prompt) =>
            {
                string? code = null;
                await Task.Run(() =>
                {
                    code = Microsoft.VisualBasic.Interaction.InputBox(
                        prompt, "Telegram Verifizierung", "");
                });
                return code ?? "";
            };

            tgClientManager.On2FAPasswordRequired += async () =>
            {
                string? pw = null;
                await Task.Run(() =>
                {
                    pw = Microsoft.VisualBasic.Interaction.InputBox(
                        "Bitte 2FA-Passwort eingeben:", "Telegram 2FA", "");
                });
                return pw ?? "";
            };

            try
            {
                tgClientManager.ConnectAsync(CancellationToken.None)
                    .GetAwaiter().GetResult();
            }
            catch (Exception ex)
            {
                coreLogger.LogError(ex, "Telegram: Login fehlgeschlagen");
            }
        }

        // ─── 6c. Instanz-Watchdog ───
        // Entfällt hier ebenfalls. Der Heartbeat ans Deploymentcenter (ein Monitor je
        // Instanz, mit Gesundheitsprüfungen und angekündigtem Ende) steht in
        // ClawdDotNet.App/Services/DeploymentcenterService.cs.

        // ─── 7. MainForm starten ───
        var form = new frm_main(
            settingsManager,
            instanceConfig,
            instancePath,
            logDir,
            loggerFactory,
            toolRegistry,
            dirManager,
            agentEngine,
            taskScanner,
            stagingService);

        Application.Run(form);

        // ─── 8. Aufräumen ───
        coreLogger.LogInformation("ClawdDotNet wird beendet");

        if (taskScanner is not null)
            taskScanner.DisposeAsync().AsTask().GetAwaiter().GetResult();

        if (tgClientManager is not null)
            tgClientManager.DisposeAsync().AsTask().GetAwaiter().GetResult();

        openRouterClient?.Dispose();
        loggerFactory.Dispose();
    }
}
