using System.Text.Json;
using ClawdDotNet.Core.Config;
using ClawdDotNet.Core.Security;
using ClawdDotNet.Core.Storage;
using ClawdDotNet.App.Models;

namespace ClawdDotNet.App.Services;

/// <summary>
/// Verwaltet die gesamte Verzeichnisstruktur für Instanzen und Agenten.
///
/// Layout:
/// {InstancesDir}/
/// ├── Instance-{Name}/
/// │   ├── InstanceSettings.json
/// │   ├── TokenUsage.json
/// │   └── Agents/
/// │       ├── AgentList.json
/// │       └── Agent-{Name}/
/// │           ├── AgentSettings.json
/// │           ├── Soul.md
/// │           ├── Identity.md
/// │           ├── Logs/
/// │           └── Workspace/
/// </summary>
public sealed class InstanceDirectoryManager
{
    private static readonly JsonSerializerOptions JsonOpts = new()
    {
        WriteIndented = true,
        PropertyNameCaseInsensitive = true
    };

    private readonly string _instancesDir;
    private readonly Lock _tokenUsageLock = new();

    public InstanceDirectoryManager(string instancesDirectory)
    {
        _instancesDir = Path.GetFullPath(instancesDirectory);
        Directory.CreateDirectory(_instancesDir);
    }

    public string InstancesDirectory => _instancesDir;

    // ═══════════════════════════════════════════════════
    //  INSTANZ-OPERATIONEN
    // ═══════════════════════════════════════════════════

    public static string BuildInstanceFolderName(string instanceName)
        => $"Instance-{SanitizeName(instanceName)}";

    public string GetInstancePath(string instanceName)
        => Path.Combine(_instancesDir, BuildInstanceFolderName(instanceName));

    public List<InstanceInfo> ListInstances()
    {
        var result = new List<InstanceInfo>();

        if (!Directory.Exists(_instancesDir))
            return result;

        foreach (var dir in Directory.GetDirectories(_instancesDir, "Instance-*"))
        {
            var folderName = Path.GetFileName(dir);
            var settingsPath = Path.Combine(dir, "InstanceSettings.json");

            var info = new InstanceInfo
            {
                FolderName = folderName,
                FolderPath = dir,
                InstanceName = folderName.Replace("Instance-", "")
            };

            if (File.Exists(settingsPath))
            {
                try
                {
                    var json = File.ReadAllText(settingsPath);
                    var config = JsonSerializer.Deserialize<InstanceConfig>(json, JsonOpts);
                    if (config is not null)
                    {
                        info.InstanceName = config.InstanceName;
                        info.ApiKeyStatus = string.IsNullOrWhiteSpace(config.OpenRouterApiKey)
                            ? "Fehlt" : "Konfiguriert";
                    }
                }
                catch { /* defekte Config → Standardwerte */ }
            }

            // Agenten zählen
            var agentsDir = Path.Combine(dir, "Agents");
            if (Directory.Exists(agentsDir))
                info.AgentCount = Directory.GetDirectories(agentsDir, "Agent-*").Length;

            result.Add(info);
        }

        return result.OrderBy(i => i.InstanceName).ToList();
    }

    public string CreateInstance(string instanceName)
    {
        var instanceDir = GetInstancePath(instanceName);

        if (Directory.Exists(instanceDir))
            throw new InvalidOperationException($"Instanz '{instanceName}' existiert bereits.");

        // Hauptverzeichnis
        Directory.CreateDirectory(instanceDir);

        // Agents-Unterverzeichnis
        var agentsDir = Path.Combine(instanceDir, "Agents");
        Directory.CreateDirectory(agentsDir);

        // SharedWorkspace-Verzeichnis
        Directory.CreateDirectory(Path.Combine(agentsDir, "SharedWorkspace"));

        // InstanceSettings.json
        var config = new InstanceConfig
        {
            InstanceId = Guid.NewGuid().ToString("N")[..8],
            InstanceName = instanceName,
            LogDirectory = "./Logs",
            WorkingDirectory = instanceDir
        };
        SaveJson(Path.Combine(instanceDir, "InstanceSettings.json"), config);

        // TokenUsage.json (leer)
        var tokenUsage = new TokenUsageFile
        {
            InstanceId = config.InstanceId,
            InstanceName = instanceName
        };
        SaveJson(Path.Combine(instanceDir, "TokenUsage.json"), tokenUsage);

        // AgentList.json (leer)
        SaveJson(Path.Combine(agentsDir, "AgentList.json"), new AgentListFile());

        return instanceDir;
    }

    public InstanceConfig LoadInstanceConfig(string instanceDir)
    {
        var settingsPath = Path.Combine(instanceDir, "InstanceSettings.json");

        if (!File.Exists(settingsPath))
            throw new FileNotFoundException($"InstanceSettings.json nicht gefunden in: {instanceDir}");

        var json = File.ReadAllText(settingsPath);
        var config = JsonSerializer.Deserialize<InstanceConfig>(json, JsonOpts)
            ?? throw new InvalidOperationException("InstanceSettings.json ist leer oder ungültig.");

        // Agenten aus Verzeichnisstruktur laden
        config.Agents.Clear();
        var agentsDir = Path.Combine(instanceDir, "Agents");

        // Sicherstellen, dass SharedWorkspace existiert (Migration)
        Directory.CreateDirectory(Path.Combine(agentsDir, "SharedWorkspace"));

        // AgentList.json für Descriptions laden
        var agentListPath = Path.Combine(agentsDir, "AgentList.json");
        var agentList = File.Exists(agentListPath)
            ? LoadJson<AgentListFile>(agentListPath) ?? new AgentListFile()
            : new AgentListFile();

        if (Directory.Exists(agentsDir))
        {
            foreach (var agentDir in Directory.GetDirectories(agentsDir, "Agent-*").OrderBy(d => d))
            {
                var agent = LoadAgentConfig(agentDir);

                var folderName = Path.GetFileName(agentDir);
                var listEntry = agentList.Agents.FirstOrDefault(a => a.FolderName == folderName);
                if (listEntry is not null && !string.IsNullOrWhiteSpace(listEntry.Description))
                    agent.Description = listEntry.Description;

                config.Agents.Add(agent);
            }
        }

        return config;
    }

    public void SaveInstanceConfig(string instanceDir, InstanceConfig config)
    {
        var settingsPath = Path.Combine(instanceDir, "InstanceSettings.json");

        // Zugangsdaten nur für das Schreiben verschlüsseln — die laufende Instanz
        // braucht sie danach wieder im Klartext.
        ConfigSecrets.Protect(config);
        try
        {
            SaveJson(settingsPath, config);
        }
        finally
        {
            ConfigSecrets.Unprotect(config);
        }
    }

    // ═══════════════════════════════════════════════════
    //  AGENTEN-OPERATIONEN
    // ═══════════════════════════════════════════════════

    public static string BuildAgentFolderName(string agentName)
        => $"Agent-{SanitizeName(agentName)}";

    public string GetAgentPath(string instanceDir, string agentName)
        => Path.Combine(instanceDir, "Agents", BuildAgentFolderName(agentName));

    public string CreateAgent(string instanceDir, string agentName, string description = "")
    {
        var agentDir = GetAgentPath(instanceDir, agentName);

        if (Directory.Exists(agentDir))
            throw new InvalidOperationException($"Agent '{agentName}' existiert bereits in dieser Instanz.");

        // Verzeichnisse anlegen
        Directory.CreateDirectory(agentDir);
        Directory.CreateDirectory(Path.Combine(agentDir, "Logs"));
        Directory.CreateDirectory(Path.Combine(agentDir, "Workspace"));

        // AgentSettings.json
        var agentConfig = new AgentConfig
        {
            AgentId = SanitizeName(agentName).ToLowerInvariant(),
            DisplayName = agentName,
            Model = "anthropic/claude-sonnet-4-5"
        };
        SaveAgentSettings(agentDir, agentConfig);

        // Identity.md
        AtomicFile.WriteAllText(Path.Combine(agentDir, "Identity.md"),
            $"""
            # Identity: {agentName}

            Du bist **{agentName}**, ein spezialisierter KI-Agent im ClawdDotNet-System.

            ## Rolle
            [Beschreibe hier die Rolle und Verantwortlichkeiten des Agenten]

            ## Expertise
            [Beschreibe hier die Fachgebiete und Fähigkeiten]

            ## Kontext
            [Beschreibe hier den Arbeitskontext und die Teamzugehörigkeit]
            """);

        // Soul.md
        AtomicFile.WriteAllText(Path.Combine(agentDir, "Soul.md"),
            $"""
            # Soul: {agentName}

            ## Persönlichkeit
            - Gründlich und zuverlässig
            - Klar und präzise in der Kommunikation
            - Proaktiv bei der Problemerkennung

            ## Arbeitsweise
            - Analysiere Aufgaben sorgfältig bevor du handelst
            - Dokumentiere deine Entscheidungen und Ergebnisse
            - Nutze die dir zugewiesenen Tools effizient

            ## Werte
            - Genauigkeit vor Geschwindigkeit
            - Transparenz in der Entscheidungsfindung
            - Sicherheit und Datenschutz haben Priorität
            """);

        // AgentList.json aktualisieren
        UpdateAgentList(instanceDir, agentName, description, BuildAgentFolderName(agentName));

        return agentDir;
    }

    public AgentConfig LoadAgentConfig(string agentDir)
    {
        // AgentSettings.json laden
        var settingsPath = Path.Combine(agentDir, "AgentSettings.json");
        AgentConfig config;

        if (File.Exists(settingsPath))
        {
            var json = File.ReadAllText(settingsPath);
            config = JsonSerializer.Deserialize<AgentConfig>(json, JsonOpts) ?? new AgentConfig();
            ConfigLoader.Migrate(config);
            ConfigSecrets.Unprotect(config);
        }
        else
        {
            config = new AgentConfig
            {
                AgentId = Path.GetFileName(agentDir).Replace("Agent-", "").ToLowerInvariant(),
                DisplayName = Path.GetFileName(agentDir).Replace("Agent-", "")
            };
        }

        // Identity.md laden
        var identityPath = Path.Combine(agentDir, "Identity.md");
        if (File.Exists(identityPath))
            config.Identity = File.ReadAllText(identityPath);

        // Soul.md laden
        var soulPath = Path.Combine(agentDir, "Soul.md");
        if (File.Exists(soulPath))
            config.Soul = File.ReadAllText(soulPath);

        // Agent-Verzeichnis merken (stabil auch bei DisplayName-Änderungen)
        config.AgentDir = Path.GetFullPath(agentDir);

        // Workspace-Pfad setzen
        config.WorkspacePath = Path.GetFullPath(Path.Combine(agentDir, "Workspace"));

        // SharedWorkspace-Pfad setzen
        var agentsDir = Path.GetDirectoryName(agentDir); // Dies ist der /Agents Ordner
        if (agentsDir != null)
        {
            config.SharedWorkspacePath = Path.GetFullPath(Path.Combine(agentsDir, "SharedWorkspace"));
        }

        return config;
    }

    public void SaveAgentSettings(string agentDir, AgentConfig config)
    {
        var settingsPath = Path.Combine(agentDir, "AgentSettings.json");

        ConfigSecrets.Protect(config);
        try
        {
            SaveJson(settingsPath, config);
        }
        finally
        {
            ConfigSecrets.Unprotect(config);
        }
    }

    public void SaveAgentIdentity(string agentDir, string identity)
    {
        AtomicFile.WriteAllText(Path.Combine(agentDir, "Identity.md"), identity);
    }

    public void SaveAgentSoul(string agentDir, string soul)
    {
        AtomicFile.WriteAllText(Path.Combine(agentDir, "Soul.md"), soul);
    }

    public void RemoveAgent(string instanceDir, string agentFolderName)
    {
        var agentDir = Path.Combine(instanceDir, "Agents", agentFolderName);
        if (Directory.Exists(agentDir))
            Directory.Delete(agentDir, recursive: true);

        // AgentList.json aktualisieren
        var agentListPath = Path.Combine(instanceDir, "Agents", "AgentList.json");
        if (File.Exists(agentListPath))
        {
            var list = LoadJson<AgentListFile>(agentListPath) ?? new AgentListFile();
            list.Agents.RemoveAll(a => a.FolderName == agentFolderName);
            SaveJson(agentListPath, list);
        }
    }

    // ═══════════════════════════════════════════════════
    //  TOKEN USAGE
    // ═══════════════════════════════════════════════════

    public void AppendTokenUsage(string instanceDir, TokenUsageRecord record)
    {
        lock (_tokenUsageLock)
        {
            var path = Path.Combine(instanceDir, "TokenUsage.json");
            TokenUsageFile file;

            if (File.Exists(path))
            {
                try
                {
                    file = LoadJson<TokenUsageFile>(path) ?? new TokenUsageFile();
                }
                catch (JsonException)
                {
                    // Korrupte Datei: Backup erstellen, neu anfangen
                    var backupPath = path + $".corrupt_{DateTime.Now:yyyyMMdd_HHmmss}";
                    try { File.Copy(path, backupPath, overwrite: true); } catch { /* best effort */ }
                    file = new TokenUsageFile();
                }
            }
            else
            {
                file = new TokenUsageFile();
            }

            file.Records.Add(record);
            SaveJson(path, file);
        }
    }

    public TokenUsageFile LoadTokenUsage(string instanceDir)
    {
        var path = Path.Combine(instanceDir, "TokenUsage.json");
        return File.Exists(path)
            ? LoadJson<TokenUsageFile>(path) ?? new TokenUsageFile()
            : new TokenUsageFile();
    }

    // ═══════════════════════════════════════════════════
    //  HILFSMETHODEN
    // ═══════════════════════════════════════════════════

    private void UpdateAgentList(string instanceDir, string agentName, string description, string folderName)
    {
        var agentListPath = Path.Combine(instanceDir, "Agents", "AgentList.json");
        var list = File.Exists(agentListPath)
            ? LoadJson<AgentListFile>(agentListPath) ?? new AgentListFile()
            : new AgentListFile();

        // Duplikat-Check
        if (list.Agents.All(a => a.FolderName != folderName))
        {
            list.Agents.Add(new AgentListItem
            {
                Name = agentName,
                Description = description,
                FolderName = folderName
            });
        }

        SaveJson(agentListPath, list);
    }

    private static void SaveJson<T>(string path, T obj)
    {
        // Atomar: Ein Absturz mitten im Schreiben soll keine halbe Datei hinterlassen.
        // Genau das ist bereits passiert (TokenUsage.json.corrupt_…).
        AtomicFile.WriteAllText(path, JsonSerializer.Serialize(obj, JsonOpts));
    }

    private static T? LoadJson<T>(string path)
    {
        // Lesen ohne den Schreiber zu blockieren — siehe AtomicFile.ReadAllText.
        var json = AtomicFile.ReadAllText(path);
        return JsonSerializer.Deserialize<T>(json, JsonOpts);
    }

    private static string SanitizeName(string name)
    {
        var sanitized = name.Trim();
        foreach (var c in Path.GetInvalidFileNameChars())
            sanitized = sanitized.Replace(c, '_');
        return sanitized.Replace(' ', '_');
    }
}
