package hooks

import (
	"encoding/json"
	"strings"
	"testing"
)

func TestEnrichTask_NoBridge_Silent(t *testing.T) {
	result := enrichTask(map[string]any{
		"description": "Audit session hook",
		"prompt":      "Look at how PreCompact works",
	}, 1) // port 1: guaranteed closed
	if result.context != "" {
		t.Errorf("expected silent result when bridge unreachable, got: %q", result.context)
	}
	if result.deny {
		t.Error("enrichTask must never deny")
	}
}

func TestEnrichTask_EmptyTask_Silent(t *testing.T) {
	result := enrichTask(map[string]any{}, 0)
	if result.context != "" || result.deny {
		t.Errorf("expected empty result for empty task, got: %+v", result)
	}
}

func TestEnrichTask_Briefing(t *testing.T) {
	srv := newFakeServer(map[string]string{
		"graph_stats":        `{"total_nodes":610,"total_edges":3519,"by_language":{"go":500,"markdown":50,"yaml":10}}`,
		"smart_context":      "function enrichRead internal/hooks/pretooluse.go:106\nfunction runPreCompact internal/hooks/precompact.go:33",
		"get_symbol_history": "method Server.handleHook internal/mcp/tools.go:2000 (edits=2)",
	})
	defer srv.Close()
	port := portFromURL(t, srv.URL)

	result := enrichTask(map[string]any{
		"description": "Subagent briefing hook",
		"prompt":      "Add a PreToolUse Task matcher that injects graph orientation",
	}, port)

	if result.deny {
		t.Fatal("enrichTask must not deny Task invocations")
	}
	if result.context == "" {
		t.Fatal("expected briefing, got empty")
	}
	if !strings.Contains(result.context, "Subagent briefing") {
		t.Errorf("missing briefing header:\n%s", result.context)
	}
	if !strings.Contains(result.context, "610 nodes, 3519 edges") {
		t.Errorf("missing stats summary:\n%s", result.context)
	}
	if !strings.Contains(result.context, "enrichRead") {
		t.Errorf("missing smart_context symbols:\n%s", result.context)
	}
	if !strings.Contains(result.context, "handleHook") {
		t.Errorf("missing recent modifications:\n%s", result.context)
	}

	// Tool-swap guidance must be inlined because subagents don't see CLAUDE.md.
	for _, needle := range []string{
		"get_symbol_source",
		"get_editing_context",
		"search_symbols",
		"find_usages",
		"smart_context",
	} {
		if !strings.Contains(result.context, needle) {
			t.Errorf("briefing missing Gortex tool guidance for %q:\n%s", needle, result.context)
		}
	}
}

// TestEnrichTask_AlwaysIncludesToolGuidance ensures the tool-swap table is
// present even when smart_context and history return nothing. Subagents must
// not be able to reach a state where they receive stats but no guidance to
// use graph tools over Read/Grep.
func TestEnrichTask_AlwaysIncludesToolGuidance(t *testing.T) {
	srv := newFakeServer(map[string]string{
		"graph_stats": `{"total_nodes":1,"total_edges":0,"by_language":{"go":1}}`,
	})
	defer srv.Close()
	port := portFromURL(t, srv.URL)

	result := enrichTask(map[string]any{
		"description": "x",
		"prompt":      "y",
	}, port)
	if result.context == "" {
		t.Fatal("expected briefing")
	}
	if !strings.Contains(result.context, "Use Gortex MCP tools instead of Read/Grep/Glob") {
		t.Errorf("tool guidance header missing:\n%s", result.context)
	}
}

func TestEnrichTask_OnlyStats_StillBriefs(t *testing.T) {
	// smart_context and get_symbol_history may return nothing early in a session;
	// as long as graph_stats works we should still emit orientation.
	srv := newFakeServer(map[string]string{
		"graph_stats": `{"total_nodes":100,"total_edges":200,"by_language":{"go":100}}`,
	})
	defer srv.Close()
	port := portFromURL(t, srv.URL)

	result := enrichTask(map[string]any{
		"description": "x",
		"prompt":      "y",
	}, port)
	if result.context == "" {
		t.Fatal("expected briefing with just stats, got empty")
	}
	if !strings.Contains(result.context, "100 nodes, 200 edges") {
		t.Errorf("missing stats summary:\n%s", result.context)
	}
}

func TestPreToolUse_RoutesTask(t *testing.T) {
	srv := newFakeServer(map[string]string{
		"graph_stats":   `{"total_nodes":1,"total_edges":0,"by_language":{"go":1}}`,
		"smart_context": "function foo internal/foo.go:1",
	})
	defer srv.Close()
	port := portFromURL(t, srv.URL)

	payload := map[string]any{
		"hook_event_name": "PreToolUse",
		"tool_name":       "Task",
		"tool_input": map[string]any{
			"description": "Refactor auth",
			"prompt":      "Rename SessionStore to AuthStore across the codebase",
		},
	}
	data, _ := json.Marshal(payload)

	out := captureStdout(t, func() { runPreToolUse(data, port, ModeDeny) })
	if out == "" {
		t.Fatal("expected hook output for Task")
	}

	var parsed HookOutput
	if err := json.Unmarshal([]byte(out), &parsed); err != nil {
		t.Fatalf("output is not valid HookOutput JSON: %v\n%s", err, out)
	}
	if parsed.HookSpecificOutput == nil {
		t.Fatal("hookSpecificOutput missing")
	}
	if parsed.HookSpecificOutput.PermissionDecision == "deny" {
		t.Error("Task must never be denied")
	}
	if !strings.Contains(parsed.HookSpecificOutput.AdditionalContext, "Subagent briefing") {
		t.Errorf("missing subagent briefing header:\n%s", parsed.HookSpecificOutput.AdditionalContext)
	}
}
