namespace ClawdDotNet.Tools.RocketChat;

/// <summary>
/// Löst einen Workspace-Pfad mit den im Projekt üblichen Prefixen auf
/// (<c>personal:</c>, <c>shared:</c>, ohne Prefix = persönlicher Workspace) — gleiche
/// Schreibweise wie beim FileRW- und FTP-Tool, damit ein Agent nicht je Tool umdenken muss.
///
/// Die Prüfung gegen einen Ausbruch aus dem Verzeichnis ist der eigentliche Zweck: Ohne sie
/// könnte ein Agent per <c>../../</c> beliebige Dateien des Rechners in einen Chatraum
/// schieben.
/// </summary>
internal static class WorkspaceFile
{
    public sealed record Resolved(string FullPath, string Label);

    public static (Resolved? File, string? Error) Resolve(
        string path, string? personalRoot, string? sharedRoot)
    {
        string relative;
        string? root;
        string label;

        if (path.StartsWith("shared:", StringComparison.OrdinalIgnoreCase))
        {
            relative = path[7..];
            root = sharedRoot;
            label = "shared";
        }
        else if (path.StartsWith("personal:", StringComparison.OrdinalIgnoreCase))
        {
            relative = path[9..];
            root = personalRoot;
            label = "personal";
        }
        else
        {
            relative = path;
            root = personalRoot;
            label = "personal";
        }

        if (string.IsNullOrWhiteSpace(root))
            return (null, $"Kein {label}-Workspace konfiguriert.");

        if (string.IsNullOrWhiteSpace(relative))
            return (null, "Es wurde kein Dateiname angegeben.");

        // Auf Windows trennt ':' einen NTFS-Alternativdatenstrom ab. Ein Pfad wie
        // "bericht.txt:versteckt" schreibt dann neben die sichtbare Datei, und die
        // Verzeichnisprüfung unten würde ihn trotzdem durchwinken.
        if (relative.Contains(':'))
            return (null, "Doppelpunkte sind im Pfad nicht erlaubt.");

        var basePath = Path.GetFullPath(root);

        string fullPath;
        try
        {
            fullPath = Path.GetFullPath(Path.Combine(basePath, relative));
        }
        catch (Exception ex) when (ex is ArgumentException or NotSupportedException or PathTooLongException)
        {
            return (null, $"Ungültiger Pfad: {path}");
        }

        // Der abschließende Trenner verhindert, dass "…/workspace-geheim" als Treffer
        // für "…/workspace" durchgeht.
        var fence = basePath.EndsWith(Path.DirectorySeparatorChar)
            ? basePath
            : basePath + Path.DirectorySeparatorChar;

        if (!fullPath.StartsWith(fence, StringComparison.OrdinalIgnoreCase))
            return (null, $"Zugriff außerhalb des {label}-Workspace verweigert.");

        return (new Resolved(fullPath, label), null);
    }

    /// <summary>
    /// MIME-Typ nach Dateiendung. Rocket.Chat zeigt Bilder und PDFs nur dann in der
    /// Vorschau, wenn der Typ stimmt — <c>application/octet-stream</c> landet immer als
    /// stummer Download.
    /// </summary>
    public static string GuessContentType(string fileName) =>
        Path.GetExtension(fileName).ToLowerInvariant() switch
        {
            ".txt" or ".log" => "text/plain",
            ".md" => "text/markdown",
            ".csv" => "text/csv",
            ".json" => "application/json",
            ".xml" => "application/xml",
            ".html" or ".htm" => "text/html",
            ".pdf" => "application/pdf",
            ".png" => "image/png",
            ".jpg" or ".jpeg" => "image/jpeg",
            ".gif" => "image/gif",
            ".webp" => "image/webp",
            ".zip" => "application/zip",
            ".docx" => "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
            ".xlsx" => "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
            ".pptx" => "application/vnd.openxmlformats-officedocument.presentationml.presentation",
            ".odt" => "application/vnd.oasis.opendocument.text",
            ".ods" => "application/vnd.oasis.opendocument.spreadsheet",
            _ => "application/octet-stream"
        };
}
