package openai

import (
	"testing"

	"iop/packages/go/config"
)

func TestHotPathSelectorDecisionMatrix(t *testing.T) {
	issued := newReservedPaths("req_test_123")
	preset := hotPathSelectorPreset([]string{config.ModeDirect, config.ModeLight})
	directOnly := hotPathSelectorPreset([]string{config.ModeDirect})
	validGate := hotPathTestGate(preset)

	tests := []struct {
		name       string
		preset     config.ExecutionPreset
		output     normalizedStageOutput
		gate       hotPathSelectorGate
		wantMode   string
		wantReason string
		wantErr    bool
	}{
		{name: "ContentTextOnly", preset: preset, output: normalizedStageOutput{Content: "Hello"}, gate: validGate, wantMode: modeDirect, wantReason: reasonDirectNoReservedControls},
		{name: "HighThinkingText", preset: preset, output: normalizedStageOutput{Content: "Result", Reasoning: "Reasoning"}, gate: validGate, wantMode: modeDirect, wantReason: reasonDirectNoReservedControls},
		{
			name: "GeneralTools", preset: preset, gate: validGate, wantMode: modeDirect, wantReason: reasonDirectNoReservedControls,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{
				{ID: "call_read", Name: "read_file", Arguments: map[string]any{"path": "src/main.go"}},
			}},
		},
		{
			name: "ExactPrepare", preset: preset, gate: validGate, wantMode: modeLight, wantReason: reasonLightExactPrepare,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{
				{ID: "call_prepare", Name: "mkdir_p", Arguments: map[string]any{"path": issued.JobDir}},
			}},
		},
		{
			name: "ExactPairWithMaskedPath", preset: preset, gate: validGate, wantMode: modeLight, wantReason: reasonLightExactPair,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{
				{ID: "call_plan", Name: "write_file", RawArgs: `{"path":".iop\/job\/req_test_123\/plan.md"}`},
				{ID: "call_review", Name: "write_file", Arguments: map[string]any{"path": issued.ReviewPath}},
			}},
		},
		{
			name: "PartialPair", preset: preset, gate: validGate, wantReason: reasonMalformedPartialPair, wantErr: true,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_plan", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath}}}},
		},
		{
			name: "MixedCalls", preset: preset, gate: validGate, wantReason: reasonMalformedMixedCalls, wantErr: true,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{
				{ID: "call_prepare", Name: "mkdir_p", Arguments: map[string]any{"path": issued.JobDir}},
				{ID: "call_general", Name: "read_file", Arguments: map[string]any{"path": "README.md"}},
			}},
		},
		{
			name: "DuplicateCalls", preset: preset, gate: validGate, wantReason: reasonMalformedDuplicateCalls, wantErr: true,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{
				{ID: "call_plan_1", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath}},
				{ID: "call_plan_2", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath}},
				{ID: "call_review", Name: "write_file", Arguments: map[string]any{"path": issued.ReviewPath}},
			}},
		},
		{
			name: "WrongIssuedPath", preset: preset, gate: validGate, wantReason: reasonMalformedWrongPath, wantErr: true,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_wrong", Name: "write_file", Arguments: map[string]any{"path": ".iop/job/another/plan.md"}}}},
		},
		{
			name: "PrefixedMappedPath", preset: preset, gate: validGate, wantReason: reasonMalformedWrongPath, wantErr: true,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_prefix", Name: "write_file", Arguments: map[string]any{"path": "prefix/" + issued.PlanPath}}}},
		},
		{
			name: "AbsoluteMappedPath", preset: preset, gate: validGate, wantReason: reasonMalformedWrongPath, wantErr: true,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_absolute", Name: "write_file", Arguments: map[string]any{"path": "/" + issued.PlanPath}}}},
		},
		{
			name: "SuffixedMappedPath", preset: preset, gate: validGate, wantReason: reasonMalformedWrongPath, wantErr: true,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_suffix", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath + ".bak"}}}},
		},
		{
			name: "SamePathExtraSource", preset: preset, gate: validGate, wantReason: reasonMalformedConflictingPath, wantErr: true,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_extra", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath, "shadow": issued.PlanPath}}}},
		},
		{
			name: "DecodedAndRawConflictingReservedPaths", preset: preset, gate: validGate, wantReason: reasonMalformedConflictingPath, wantErr: true,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_raw_conflict", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath}, RawArgs: `{"path":".iop/job/req_test_123/review.md"}`}}},
		},
		{
			name: "ArbitraryControlRole", preset: preset, gate: validGate, wantReason: reasonMalformedControlRole, wantErr: true,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_wrong_role", Name: "shell", Arguments: map[string]any{"path": issued.PlanPath}}}},
		},
		{
			name: "CanonicalRoleWrongReservedShape", preset: preset, gate: validGate, wantReason: reasonMalformedControlRole, wantErr: true,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_wrong_shape", Name: "mkdir_p", Arguments: map[string]any{"path": issued.PlanPath}}}},
		},
		{
			name: "ConflictingReservedPathSources", preset: preset, gate: validGate, wantReason: reasonMalformedConflictingPath, wantErr: true,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_conflict", Name: "write_file", Arguments: map[string]any{"path": issued.PlanPath, "shadow": issued.ReviewPath}}}},
		},
		{
			name: "LightDisabled", preset: directOnly, gate: hotPathTestGate(directOnly), wantReason: reasonModeDisabled, wantErr: true,
			output: normalizedStageOutput{ToolCalls: []normalizedToolCall{{ID: "call_prepare", Name: "mkdir_p", Arguments: map[string]any{"path": issued.JobDir}}}},
		},
		{name: "UnhealthyPinnedGate", preset: preset, output: normalizedStageOutput{Content: "text"}, gate: withGateHealth(validGate, false), wantReason: reasonUnhealthyRoute, wantErr: true},
		{name: "MissingCapabilityEvidence", preset: preset, output: normalizedStageOutput{Content: "text"}, gate: withoutGateCapability(validGate), wantReason: reasonUnhealthyRoute, wantErr: true},
		{name: "MismatchedPresetBinding", preset: preset, output: normalizedStageOutput{Content: "text"}, gate: withGateSelector(validGate, "other-model"), wantReason: reasonUnhealthyRoute, wantErr: true},
		{
			name: "ProseNeverSelectsMode", preset: preset, gate: validGate, wantMode: modeDirect, wantReason: reasonDirectNoReservedControls,
			output: normalizedStageOutput{Content: "I would choose light and mention .iop/job/req_test_123/plan.md in prose."},
		},
	}

	for _, test := range tests {
		t.Run(test.name, func(t *testing.T) {
			decision, err := classifyHotPathOutput(test.preset, issued, test.output, test.gate)
			if (err != nil) != test.wantErr {
				t.Fatalf("classifyHotPathOutput() error = %v, wantErr %v", err, test.wantErr)
			}
			if decision.Reason != test.wantReason {
				t.Errorf("decision.Reason = %q, want %q", decision.Reason, test.wantReason)
			}
			if !test.wantErr && decision.Mode != test.wantMode {
				t.Errorf("decision.Mode = %q, want %q", decision.Mode, test.wantMode)
			}
		})
	}
}

func hotPathSelectorPreset(modes []string) config.ExecutionPreset {
	routes := make(map[string]config.ExecutionRoute, len(modes))
	for _, mode := range modes {
		switch mode {
		case config.ModeDirect:
			routes[mode] = config.ExecutionRoute{}
		case config.ModeLight:
			routes[mode] = config.ExecutionRoute{Stages: []config.ExecutionRouteStage{
				{Role: "local", Model: "local-model"},
				{Role: "review", Model: "review-model"},
			}}
		}
	}
	return config.ExecutionPreset{
		ID: "preset-standard", Selector: config.ExecutionModelBinding{Model: "selector-model"}, AllowedModes: modes, Routes: routes,
		WorkspaceTools: []config.ExecutionWorkspaceToolAlternative{{
			Name: "canonical-fs",
			Operations: map[string]config.ExecutionWorkspaceOperation{
				"prepare": {ToolName: "mkdir_p", ArgumentMap: map[string]any{"path": "path"}},
				"write":   {ToolName: "write_file", ArgumentMap: map[string]any{"path": "path"}},
			},
		}},
	}
}

func hotPathTestGate(preset config.ExecutionPreset) hotPathSelectorGate {
	return hotPathSelectorGate{
		PresetID: preset.ID, SelectorModel: preset.Selector.Model, ModelGroupKey: preset.Selector.Model,
		ProviderID: "provider-1", RunID: "run-1", NodeID: "node-1", ExecutionPath: "provider_tunnel",
		ProfileDriver: "openai_chat", ProfileCapabilities: []string{"chat"}, Healthy: true, CapabilitySatisfied: true,
	}
}

func withGateHealth(gate hotPathSelectorGate, healthy bool) hotPathSelectorGate {
	gate.Healthy = healthy
	return gate
}

func withoutGateCapability(gate hotPathSelectorGate) hotPathSelectorGate {
	gate.CapabilitySatisfied = false
	return gate
}

func withGateSelector(gate hotPathSelectorGate, model string) hotPathSelectorGate {
	gate.SelectorModel = model
	return gate
}
