import Testing

@testable import NucleicCore

/// `CommandSummary` turns a non-git command (argv or raw line) into a labeled, categorized,
/// glyphed op for the Control panel's command feed — the non-git analogue of `GitCommandSummary`.
@Suite struct CommandSummaryTests {
    @Test func classifiesCommonReadAndSearchCommands() {
        let grep = CommandSummary.classify(argv: ["grep", "-rn", "TODO", "src"])
        #expect(grep?.command == "grep")
        #expect(grep?.category == "search")
        #expect(grep?.label == #"Search for "TODO""#)
        #expect(grep?.isMutating == false)
        #expect(grep?.recognized == true)

        let cat = CommandSummary.classify(argv: ["cat", "src/main.swift"])
        #expect(cat?.category == "read")
        #expect(cat?.label == "Read src/main.swift")

        let find = CommandSummary.classify(argv: ["find", ".", "-name", "*.swift"])
        #expect(find?.category == "search")
        #expect(find?.label == #"Find files matching "*.swift""#)

        let ls = CommandSummary.classify(argv: ["ls", "-la", "Sources"])
        #expect(ls?.category == "list")
        #expect(ls?.label == "List Sources")
    }

    @Test func classifiesDestructiveAndBuildCommands() {
        // A recursive/forced rm is flagged mutating with its dangerous flags named.
        let rm = CommandSummary.classify(argv: ["rm", "-rf", "build"])
        #expect(rm?.category == "delete")
        #expect(rm?.isMutating == true)
        #expect(rm?.label == "Delete build (recursive, forced)")

        // Build/run tools (covered by the tracer, not shimmed) still classify.
        let npm = CommandSummary.classify(argv: ["npm", "install"])
        #expect(npm?.category == "build")
        #expect(npm?.isMutating == true)

        let py = CommandSummary.classify(argv: ["python3", "tool.py", "--flag"])
        #expect(py?.category == "run")
        #expect(py?.label == "Run tool.py (python3)")
    }

    @Test func resolvesBasenameAndLeadingPrefixes() {
        // An absolute path resolves to its basename, and a leading env-assignment prefix is skipped.
        let abs = CommandSummary.classify(argv: ["/usr/bin/grep", "needle"])
        #expect(abs?.command == "grep")

        let env = CommandSummary.classify(argv: ["FOO=bar", "cat", "f.txt"])
        #expect(env?.command == "cat")
        #expect(env?.category == "read")
    }

    @Test func classifiesFromRawCommandLine() {
        // The tracer reports a raw line; the first command in the pipeline is described.
        let op = CommandSummary.classify(commandLine: "grep -i error log.txt | wc -l")
        #expect(op?.command == "grep")
        #expect(op?.category == "search")
    }

    @Test func unknownCommandGetsGenericFallback() {
        let op = CommandSummary.classify(argv: ["frobnicate", "--xyz", "target"])
        #expect(op?.command == "frobnicate")
        #expect(op?.category == "other")
        #expect(op?.recognized == false)
        #expect(op?.label.hasPrefix("Run frobnicate") == true)

        // Empty argv is the only nil case.
        #expect(CommandSummary.classify(argv: []) == nil)
    }

    @Test func neverReturnsNilForNonEmptyArgv() {
        // The interceptor reports many commands; classify must yield an event for every non-empty
        // argv (recognized or generic fallback), so nothing the agent ran is silently dropped.
        let samples: [[String]] = [
            ["grep", "x"], ["cat"], ["ls"], ["find", "."], ["sed", "-i", "s/a/b/", "f"],
            ["awk", "{print}"], ["wc", "-l"], ["sort"], ["rm", "-rf", "x"], ["npm", "ci"],
            ["python3"], ["curl", "https://x"], ["tar", "xzf", "a.tgz"], ["./configure"],
            ["FOO=bar", "make"], ["sudo", "apt", "update"], ["weird-tool-xyz", "--flag"],
            ["  "], ["x"],
        ]
        for argv in samples {
            #expect(CommandSummary.classify(argv: argv) != nil, "nil for \(argv)")
        }
    }
}
