import Testing

@testable import NucleicCore

@Suite struct ShellCommandSummaryTests {
    /// The motivating case: a worktree-setup pipeline surfaces its notable `rm`/`git` ops in
    /// run order, with the leading `rm -rf` flagged destructive — it can't hide behind the
    /// git ops. The `cd`, env assignment, `echo`, and `tail` plumbing are dropped.
    @Test func worktreeSetupPipelineSteps() {
        let command = """
            cd "/repo" && WT=/tmp/nucleic-control-migrate && rm -rf "$WT" \
            && git worktree add --detach "$WT" HEAD 2>&1 | tail -3 && cd "$WT" \
            && git switch -c work/control-migrate 2>&1 | tail -2 && echo "READY at $WT" \
            && git rev-parse HEAD
            """
        let block = ShellCommandSummary.block(for: command)
        #expect(block?.steps.map(\.label) == [
            "Delete $WT (recursive, forced)",
            "Add worktree at $WT",
            "Create branch work/control-migrate",
            "Resolve a git revision",
        ])
        #expect(block?.steps.map(\.destructive) == [true, false, false, false])
    }

    /// The one-liner leads with the scariest op — a recursive/forced delete outranks the git
    /// ops around it.
    @Test func summaryLeadsWithDestructiveRm() {
        let command = """
            cd "/repo" && rm -rf "$WT" && git worktree add --detach "$WT" HEAD \
            && git switch -c work/x && git rev-parse HEAD
            """
        #expect(ShellCommandSummary.summary(for: command) == "Delete $WT (recursive, forced)")
    }

    @Test func removeLabels() {
        #expect(ShellCommandSummary.summary(for: "rm -rf build/") == "Delete build/ (recursive, forced)")
        #expect(ShellCommandSummary.summary(for: "rm file.txt") == "Delete file.txt")
        #expect(ShellCommandSummary.summary(for: "rm -r dir") == "Delete dir (recursive)")
        #expect(ShellCommandSummary.summary(for: "rm -f stale.log") == "Delete stale.log (forced)")
        #expect(ShellCommandSummary.summary(for: "rm a b c") == "Delete 3 paths")
        #expect(ShellCommandSummary.summary(for: "rmdir empty") == "Remove directory empty")
    }

    /// Bundled and end-of-options forms still read the recursive/force flags.
    @Test func removeFlagForms() {
        #expect(ShellCommandSummary.summary(for: "rm -fr build") == "Delete build (recursive, forced)")
        #expect(ShellCommandSummary.summary(for: "rm --recursive --force x") == "Delete x (recursive, forced)")
        #expect(ShellCommandSummary.summary(for: "rm -- -weird-name") == "Delete -weird-name")
    }

    /// An absolute path to the binary is still recognized (`/bin/rm`).
    @Test func absoluteBinaryPath() {
        #expect(ShellCommandSummary.summary(for: "/bin/rm -rf /tmp/x") == "Delete /tmp/x (recursive, forced)")
    }

    /// git ops read identically to the git-only view, including the new `worktree`/`rev-parse`.
    @Test func gitOpsDelegateToGitSummary() {
        #expect(ShellCommandSummary.summary(for: "git status") == "Check git status")
        #expect(ShellCommandSummary.summary(for: "git push origin main") == "Push to origin/main")
        #expect(
            ShellCommandSummary.summary(for: "git worktree add --detach /tmp/x HEAD")
                == "Add worktree at /tmp/x")
        #expect(ShellCommandSummary.summary(for: "git rev-parse HEAD") == "Resolve a git revision")
    }

    /// A git commit still wins over an `rm` in the same pipeline (a commit is the highest
    /// intent), and keeps its heredoc subject.
    @Test func commitOutranksRemove() {
        let command = """
            rm -rf stale && git add -A && git commit -F - <<'EOF'
            Tidy up the worktree
            EOF
            """
        #expect(ShellCommandSummary.summary(for: command) == "Commit: Tidy up the worktree")
    }

    /// A plain `rm` outranks the `git add` that precedes it.
    @Test func removeOutranksStage() {
        #expect(
            ShellCommandSummary.summary(for: "git add -A && rm -rf build")
                == "Delete build (recursive, forced)")
    }

    /// Commands with no recognized `rm`/`git` op are not summarized — the caller shows them
    /// verbatim.
    @Test func unrecognizedReturnsNil() {
        #expect(ShellCommandSummary.summary(for: "ls -la") == nil)
        #expect(ShellCommandSummary.summary(for: "swift build && swift test") == nil)
        #expect(ShellCommandSummary.summary(for: "echo hi && cd /tmp") == nil)
        #expect(ShellCommandSummary.block(for: "swift build") == nil)
        // `rm` named only as an argument (not the command word) isn't a delete.
        #expect(ShellCommandSummary.summary(for: #"echo "rm -rf /""#) == nil)
    }

    /// The approval title routes general commands through the summary, surfacing the `rm`.
    @Test func riskClassifierTitleSurfacesRemove() {
        #expect(
            RiskClassifier.title(toolName: "Bash", input: ["command": "rm -rf build/"])
                == "Delete build/ (recursive, forced)")
        #expect(
            RiskClassifier.title(toolName: "Bash", input: ["command": "ls -la"])
                == "Run: ls -la")
    }
}
