import Foundation

/// Heuristic risk classification computed from `toolName` + `input`
/// (BACKEND_PROTOCOL §4). Drives UI emphasis; never decides on its own in
/// interactive mode.
public enum RiskClassifier {
    public static func classify(toolName: String, input: JSONValue) -> Risk {
        switch toolName {
        case "Read", "Glob", "Grep", "LS", "NotebookRead":
            return .readOnly
        case "Edit", "Write", "MultiEdit", "NotebookEdit", "TodoWrite":
            return .write
        case "WebFetch", "WebSearch":
            return .network
        case "Bash", "BashOutput":
            return classifyCommand(input["command"]?.stringValue ?? "")
        case "Task", "Agent", "KillShell":
            return .execute
        default:
            return .unknown
        }
    }

    /// The file path(s) an edit-class tool call targets, for conflict detection. Empty for
    /// non-editing tools. Edit/Write/MultiEdit use `file_path`; NotebookEdit uses `notebook_path`.
    public static func editedPaths(toolName: String, input: JSONValue) -> [String] {
        switch toolName {
        case "Edit", "Write", "MultiEdit":
            return [input["file_path"]?.stringValue].compactMap { $0 }
        case "NotebookEdit":
            return [input["notebook_path"]?.stringValue].compactMap { $0 }
        default:
            return []
        }
    }

    private static func classifyCommand(_ command: String) -> Risk {
        let destructive: [String] = [
            "rm ", "rm\t", "rmdir", "sudo ", "mkfs", "dd ", "shutdown", "reboot",
            "git push --force", "git push -f", "git reset --hard", "git clean -fd",
            "chmod -R 777", "truncate ", ":(){",
        ]
        let network: [String] = [
            "curl ", "wget ", "git push", "git fetch", "git pull", "ssh ", "scp ",
            "npm publish", "npm install", "pip install", "brew install", "gh ",
        ]
        let lowered = command.lowercased()
        if destructive.contains(where: { lowered.contains($0) }) { return .destructive }
        if network.contains(where: { lowered.contains($0) }) { return .network }
        return .execute
    }

    /// Human summary for the approval UI ("Run: rm -rf build/").
    public static func title(toolName: String, input: JSONValue) -> String {
        func truncated(_ s: String, _ max: Int = 80) -> String {
            s.count <= max ? s : String(s.prefix(max - 1)) + "…"
        }
        switch toolName {
        case "Bash":
            if let command = input["command"]?.stringValue {
                // A recognized op reads as a friendly phrase ("Commit: …", "Push to
                // origin/main", "Delete build/ (recursive, forced)") rather than the raw
                // pipeline — the most salient `rm`/`git` op across the chain. Anything else
                // shows verbatim.
                if let summary = ShellCommandSummary.summary(for: command) {
                    return truncated(summary)
                }
                return "Run: \(truncated(command))"
            }
        case "Edit", "MultiEdit":
            if let path = input["file_path"]?.stringValue {
                return "Edit: \(truncated(path))"
            }
        case "Write":
            if let path = input["file_path"]?.stringValue {
                return "Write: \(truncated(path))"
            }
        case "NotebookEdit":
            if let path = input["notebook_path"]?.stringValue {
                return "Edit notebook: \(truncated(path))"
            }
        case "WebFetch":
            if let url = input["url"]?.stringValue {
                return "Fetch: \(truncated(url))"
            }
        case "WebSearch":
            if let query = input["query"]?.stringValue {
                return "Search: \(truncated(query))"
            }
        default:
            break
        }
        return toolName
    }

    /// The full, **untruncated** content of what an approval grants, for the detail
    /// body of the approval UI. Unlike `title` (a one-line summary capped at 80 chars
    /// for compact contexts), this never truncates — the user must be able to see
    /// exactly what they are allowing before they allow it. Falls back to the
    /// pretty-printed input so nothing about an unrecognized tool is hidden.
    public static func detail(toolName: String, input: JSONValue) -> String {
        switch toolName {
        case "Bash":
            if let command = input["command"]?.stringValue { return command }
        case "Edit", "MultiEdit", "Write":
            if let path = input["file_path"]?.stringValue { return path }
        case "NotebookEdit":
            if let path = input["notebook_path"]?.stringValue { return path }
        case "WebFetch":
            if let url = input["url"]?.stringValue { return url }
        case "WebSearch":
            if let query = input["query"]?.stringValue { return query }
        default:
            break
        }
        if case .object(let o) = input, o.isEmpty { return "" }
        if case .null = input { return "" }
        return input.prettyString()
    }
}
