package cmd

import (
	"bytes"
	"path/filepath"
	"strings"
	"testing"
)

func TestRunKnownCommandsStayCallable(t *testing.T) {
	for name := range commandRegistry() {
		if name == "help" {
			continue
		}

		t.Run(name, func(t *testing.T) {
			var stdout bytes.Buffer
			var stderr bytes.Buffer

			args := []string{name}
			expectBootstrapStatus := true

			run := func() int {
				return Run(args, &stdout, &stderr)
			}

			if name == "serve" {
				args = []string{name, "--help"}
				expectBootstrapStatus = false
				exitCode := run()
				assertKnownCommandResult(t, name, commandDescription(name), exitCode, stdout.String(), stderr.String(), expectBootstrapStatus)
				return
			}

			if name == "canonicalize" || name == "discover" || name == "init" || name == "new" || name == "migrate-config" || name == "index" || name == "status" || name == "version" || name == "preview-sources" || name == "explain-file" || name == "search-specs" || name == "check-overlap" || name == "compare-specs" || name == "analyze-impact" || name == "check-terminology" || name == "check-compliance" || name == "check-doc-drift" || name == "check-spec-freshness" || name == "fix" || name == "compile" || name == "review-spec" || name == "schema" {
				repoRoot := writeSearchWorkspace(t)
				if name == "discover" || name == "init" || name == "canonicalize" {
					repoRoot = writeDiscoveryWorkspace(t)
				}
				if name == "compile" {
					repoRoot = writeCompileWorkspaceCmd(t)
				}
				if name == "canonicalize" {
					args = []string{name, "--path", "rfcs/service-sla.md"}
					expectBootstrapStatus = false
					exitCode := withWorkingDir(t, repoRoot, run)
					assertKnownCommandResult(t, name, commandDescription(name), exitCode, stdout.String(), stderr.String(), expectBootstrapStatus)
					return
				}
				if name == "discover" {
					args = []string{name, "--path", "."}
					expectBootstrapStatus = false
					exitCode := withWorkingDir(t, repoRoot, run)
					assertKnownCommandResult(t, name, commandDescription(name), exitCode, stdout.String(), stderr.String(), expectBootstrapStatus)
					return
				}
				if name == "init" {
					args = []string{name, "--path", "."}
					expectBootstrapStatus = false
					exitCode := withWorkingDir(t, repoRoot, run)
					assertKnownCommandResult(t, name, commandDescription(name), exitCode, stdout.String(), stderr.String(), expectBootstrapStatus)
					return
				}
				if name == "new" {
					args = []string{name, "--title", "Kernel locality", "--domain", "core"}
					expectBootstrapStatus = false
					exitCode := withWorkingDir(t, repoRoot, run)
					assertKnownCommandResult(t, name, commandDescription(name), exitCode, stdout.String(), stderr.String(), expectBootstrapStatus)
					return
				}
				if name == "migrate-config" {
					repoRoot = t.TempDir()
					mustWriteFileCmd(t, filepath.Join(repoRoot, "pituitary.toml"), `
[project]
id = "ccd"
name = "Continuous Context Development"
specs_dir = "specs"
`)
					mustMkdirAllCmd(t, filepath.Join(repoRoot, "specs"))
					args = []string{name}
					expectBootstrapStatus = false
					exitCode := withWorkingDir(t, repoRoot, run)
					assertKnownCommandResult(t, name, commandDescription(name), exitCode, stdout.String(), stderr.String(), expectBootstrapStatus)
					return
				}
				if name == "index" {
					args = []string{name, "--rebuild"}
				} else if name == "status" {
					args = []string{name}
				} else if name == "version" {
					args = []string{name}
				} else if name == "preview-sources" {
					args = []string{name}
				} else if name == "explain-file" {
					args = []string{name, "docs/guides/api-rate-limits.md"}
				} else if name == "schema" {
					args = []string{name}
				} else if name == "search-specs" {
					indexStdout := bytes.Buffer{}
					indexStderr := bytes.Buffer{}
					exitCode := withWorkingDir(t, repoRoot, func() int {
						return runIndex([]string{"--rebuild"}, &indexStdout, &indexStderr)
					})
					if exitCode != 0 {
						t.Fatalf("runIndex() exit code = %d, want 0 (stderr: %q)", exitCode, indexStderr.String())
					}
					args = []string{name, "--query", "rate limiting"}
				} else {
					indexStdout := bytes.Buffer{}
					indexStderr := bytes.Buffer{}
					if name == "check-compliance" {
						writeComplianceSourceFile(t, repoRoot, "src/api/middleware/ratelimiter.go", `
package middleware

// Apply limits per tenant rather than per API key.
// Enforce a default limit of 200 requests per minute.
// Use a sliding-window limiter.
func buildLimiter() {}
`)
					}
					exitCode := withWorkingDir(t, repoRoot, func() int {
						return runIndex([]string{"--rebuild"}, &indexStdout, &indexStderr)
					})
					if exitCode != 0 {
						t.Fatalf("runIndex() exit code = %d, want 0 (stderr: %q)", exitCode, indexStderr.String())
					}
					if name == "check-overlap" {
						args = []string{name, "--spec-ref", "SPEC-042"}
					} else if name == "compare-specs" {
						args = []string{name, "--spec-ref", "SPEC-008", "--spec-ref", "SPEC-042"}
					} else if name == "analyze-impact" {
						args = []string{name, "--spec-ref", "SPEC-042"}
					} else if name == "check-terminology" {
						args = []string{name, "--term", "tenant"}
					} else if name == "check-compliance" {
						args = []string{name, "--path", "src/api/middleware/ratelimiter.go"}
					} else if name == "fix" {
						args = []string{name, "--path", "docs/guides/api-rate-limits.md", "--dry-run"}
					} else if name == "compile" {
						args = []string{name, "--scope", "all", "--dry-run"}
					} else if name == "review-spec" {
						args = []string{name, "--spec-ref", "SPEC-042"}
					} else {
						args = []string{name, "--scope", "all"}
					}
				}
				expectBootstrapStatus = false
				exitCode := withWorkingDir(t, repoRoot, run)
				assertKnownCommandResult(t, name, commandDescription(name), exitCode, stdout.String(), stderr.String(), expectBootstrapStatus)
				return
			}

			exitCode := run()
			assertKnownCommandResult(t, name, commandDescription(name), exitCode, stdout.String(), stderr.String(), expectBootstrapStatus)
		})
	}
}

func TestCommandsRegistryHasRunners(t *testing.T) {
	t.Parallel()

	for name, command := range commandRegistry() {
		if strings.TrimSpace(command.Description) == "" {
			t.Fatalf("command %q description is empty", name)
		}
		if len(command.Formats) == 0 {
			t.Fatalf("command %q formats are empty", name)
		}
		if _, ok := command.Formats[commandFormatText]; !ok {
			t.Fatalf("command %q does not support text format", name)
		}
		if _, ok := command.Formats[commandFormatJSON]; !ok {
			t.Fatalf("command %q does not support json format", name)
		}
		if command.Run == nil {
			t.Fatalf("command %q has no runner", name)
		}
	}
}

func assertKnownCommandResult(t *testing.T, name, description string, exitCode int, stdout, stderr string, expectBootstrapStatus bool) {
	t.Helper()

	if exitCode != 0 {
		t.Fatalf("Run(%q) exit code = %d, want 0", name, exitCode)
	}
	if stderr != "" && name != "index" && name != "init" {
		t.Fatalf("Run(%q) wrote unexpected stderr: %q", name, stderr)
	}
	if name == "index" && stderr != "" && !strings.Contains(stderr, "pituitary index: chunking") {
		t.Fatalf("Run(%q) stderr %q does not contain rebuild progress", name, stderr)
	}
	if name == "init" && stderr != "" && !strings.Contains(stderr, "pituitary init: chunking") {
		t.Fatalf("Run(%q) stderr %q does not contain init progress", name, stderr)
	}

	if usesSemanticTextRendering(name) {
		if !strings.Contains(stdout, name) {
			t.Fatalf("Run(%q) output %q does not contain command header %q", name, stdout, name)
		}
	} else if !strings.Contains(stdout, description) {
		t.Fatalf("Run(%q) output %q does not contain description %q", name, stdout, description)
	}
	if expectBootstrapStatus && !strings.Contains(stdout, "status: bootstrap only, not implemented yet") {
		t.Fatalf("Run(%q) output %q does not contain bootstrap status", name, stdout)
	}
}

func TestRunHelpIncludesCommandSurface(t *testing.T) {
	var stdout bytes.Buffer
	var stderr bytes.Buffer

	exitCode := Run([]string{"help"}, &stdout, &stderr)
	if exitCode != 0 {
		t.Fatalf("Run(help) exit code = %d, want 0", exitCode)
	}
	if stderr.Len() != 0 {
		t.Fatalf("Run(help) wrote unexpected stderr: %q", stderr.String())
	}

	out := stdout.String()
	for name := range commandRegistry() {
		if name == "help" {
			continue
		}
		if !strings.Contains(out, name) {
			t.Fatalf("help output %q does not include command %q", out, name)
		}
	}
}

func TestRunHelpIncludesColorGlobalOption(t *testing.T) {
	t.Parallel()

	var stdout bytes.Buffer
	var stderr bytes.Buffer

	exitCode := Run([]string{"help"}, &stdout, &stderr)
	if exitCode != 0 {
		t.Fatalf("Run(help) exit code = %d, want 0", exitCode)
	}
	if !strings.Contains(stdout.String(), "--color MODE") {
		t.Fatalf("help output %q does not include --color global option", stdout.String())
	}
}

func TestRunHelpIncludesLogLevelGlobalOption(t *testing.T) {
	t.Parallel()

	var stdout bytes.Buffer
	var stderr bytes.Buffer

	exitCode := Run([]string{"help"}, &stdout, &stderr)
	if exitCode != 0 {
		t.Fatalf("Run(help) exit code = %d, want 0", exitCode)
	}
	if !strings.Contains(stdout.String(), "--log-level LEVEL") {
		t.Fatalf("help output %q does not include --log-level global option", stdout.String())
	}
}
