package cli

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

func TestCollectorCmdRegistered(t *testing.T) {
	found := false
	for _, cmd := range rootCmd.Commands() {
		if cmd.Name() == "collector" {
			found = true
			break
		}
	}
	if !found {
		t.Fatal("collector command not registered on root")
	}
}

func TestCollectorSubcommandsRegistered(t *testing.T) {
	want := map[string]bool{"start": false, "stop": false, "status": false}
	for _, cmd := range collectorCmd.Commands() {
		if _, ok := want[cmd.Name()]; ok {
			want[cmd.Name()] = true
		}
	}
	for name, found := range want {
		if !found {
			t.Errorf("collector subcommand %q not registered", name)
		}
	}
}

func runCollectorCmd(t *testing.T, root string, dryRun bool, args ...string) (string, error) {
	t.Helper()
	resetRootFlags(t)
	full := append([]string{"collector"}, args...)
	if root != "" {
		full = append(full, "--root", root)
	}
	if dryRun && (len(args) > 0 && (args[0] == "start" || args[0] == "stop")) {
		full = append(full, "--dry-run")
	}
	var buf bytes.Buffer
	rootCmd.SetOut(&buf)
	rootCmd.SetErr(&buf)
	rootCmd.SetArgs(full)
	err := rootCmd.Execute()
	return buf.String(), err
}

func TestCollectorStartEmitsArtifacts(t *testing.T) {
	root := t.TempDir()
	out, err := runCollectorCmd(t, root, true, "start")
	if err != nil {
		t.Fatalf("orca collector start: %v\n%s", err, out)
	}
	wantFiles := []string{
		filepath.Join(root, collectorScriptDir, collectorAggregateSh),
		filepath.Join(root, collectorScriptDir, collectorWatchdogSh),
		filepath.Join(root, collectorUnitDir, collectorAggregateSvc),
		filepath.Join(root, collectorUnitDir, collectorAggregateTmr),
		filepath.Join(root, collectorUnitDir, collectorWatchdogSvc),
		filepath.Join(root, collectorUnitDir, collectorWatchdogTmr),
	}
	for _, p := range wantFiles {
		if _, err := os.Stat(p); err != nil {
			t.Errorf("expected emitted file %s: %v", p, err)
		}
	}
	for _, p := range []string{
		filepath.Join(root, collectorScriptDir, collectorAggregateSh),
		filepath.Join(root, collectorScriptDir, collectorWatchdogSh),
	} {
		info, err := os.Stat(p)
		if err != nil {
			t.Fatalf("stat %s: %v", p, err)
		}
		if perm := info.Mode().Perm(); perm&0o111 == 0 {
			t.Errorf("expected executable bit on %s, got %o", p, perm)
		}
	}
}

func TestCollectorStartStatusRunning(t *testing.T) {
	root := t.TempDir()
	if _, err := runCollectorCmd(t, root, true, "start"); err != nil {
		t.Fatalf("start: %v", err)
	}
	agg, wd := collectorRunning(root)
	if !agg || !wd {
		t.Errorf("expected both running, got agg=%t wd=%t", agg, wd)
	}
	out, err := runCollectorCmd(t, root, true, "status")
	if err != nil {
		t.Fatalf("status: %v", err)
	}
	if !contains(out, "running") {
		t.Errorf("status output should say running, got %q", out)
	}
}

func TestCollectorStopRemovesArtifacts(t *testing.T) {
	root := t.TempDir()
	if _, err := runCollectorCmd(t, root, true, "start"); err != nil {
		t.Fatalf("start: %v", err)
	}
	out, err := runCollectorCmd(t, root, true, "stop")
	if err != nil {
		t.Fatalf("stop: %v\n%s", err, out)
	}
	wantFiles := []string{
		filepath.Join(root, collectorScriptDir, collectorAggregateSh),
		filepath.Join(root, collectorScriptDir, collectorWatchdogSh),
		filepath.Join(root, collectorUnitDir, collectorAggregateSvc),
		filepath.Join(root, collectorUnitDir, collectorAggregateTmr),
		filepath.Join(root, collectorUnitDir, collectorWatchdogSvc),
		filepath.Join(root, collectorUnitDir, collectorWatchdogTmr),
	}
	for _, p := range wantFiles {
		if _, err := os.Stat(p); !os.IsNotExist(err) {
			t.Errorf("expected %s removed, got %v", p, err)
		}
	}
}

func TestCollectorStatusWhenStopped(t *testing.T) {
	root := t.TempDir()
	out, err := runCollectorCmd(t, root, true, "status")
	if err != nil {
		t.Fatalf("status: %v", err)
	}
	if !contains(out, "stopped") {
		t.Errorf("expected stopped, got %q", out)
	}
	agg, wd := collectorRunning(root)
	if agg || wd {
		t.Errorf("expected neither running, got agg=%t wd=%t", agg, wd)
	}
}

func TestCollectorResolveRootRejectsRelative(t *testing.T) {
	collectorRoot = "tmp/relative"
	_, err := collectorResolveRoot()
	if err == nil {
		t.Error("expected error for relative root")
	}
	collectorRoot = "/"
	r, err := collectorResolveRoot()
	if err != nil || r != "/" {
		t.Errorf("expected / for default, got %q err=%v", r, err)
	}
}

func contains(haystack, needle string) bool {
	return bytes.Contains([]byte(haystack), []byte(needle))
}
