package sdlc

import "testing"

func makeTestFeature(phase FeaturePhase) *Feature {
	f := &Feature{
		Slug:  "auth",
		Title: "Auth",
		Phase: phase,
		Artifacts: map[ArtifactType]*Artifact{
			ArtifactSpec:      NewArtifact(ArtifactSpec),
			ArtifactDesign:    NewArtifact(ArtifactDesign),
			ArtifactTasks:     NewArtifact(ArtifactTasks),
			ArtifactQAPlan:    NewArtifact(ArtifactQAPlan),
			ArtifactReview:    NewArtifact(ArtifactReview),
			ArtifactAudit:     NewArtifact(ArtifactAudit),
			ArtifactQAResults: NewArtifact(ArtifactQAResults),
		},
	}
	return f
}

func TestClassifyDraftNeedsSpec(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseDraft)

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionCreateSpec {
		t.Errorf("Action = %q, want CREATE_SPEC", cl.Action)
	}
	if cl.RuleMatched != "needs-spec" {
		t.Errorf("RuleMatched = %q, want needs-spec", cl.RuleMatched)
	}
}

func TestClassifyDraftSpecDraftNeedsApproval(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseDraft)
	f.GetArtifact(ArtifactSpec).MarkDraft()

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionAwaitApproval {
		t.Errorf("Action = %q, want AWAIT_APPROVAL", cl.Action)
	}
	if cl.RuleMatched != "spec-needs-approval" {
		t.Errorf("RuleMatched = %q, want spec-needs-approval", cl.RuleMatched)
	}
}

func TestClassifyDraftSpecApprovedTransition(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseDraft)
	f.GetArtifact(ArtifactSpec).Approve("user")

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionTransition {
		t.Errorf("Action = %q, want TRANSITION", cl.Action)
	}
	if cl.TransitionTo != PhaseSpecified {
		t.Errorf("TransitionTo = %q, want specified", cl.TransitionTo)
	}
}

func TestClassifySpecifiedNeedsDesign(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseSpecified)

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionCreateDesign {
		t.Errorf("Action = %q, want CREATE_DESIGN", cl.Action)
	}
}

func TestClassifySpecifiedNeedsTasks(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseSpecified)
	f.GetArtifact(ArtifactDesign).Approve("user")

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionCreateTasks {
		t.Errorf("Action = %q, want CREATE_TASKS", cl.Action)
	}
}

func TestClassifySpecifiedNeedsQAPlan(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseSpecified)
	f.GetArtifact(ArtifactDesign).Approve("user")
	f.GetArtifact(ArtifactTasks).Approve("user")

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionCreateQAPlan {
		t.Errorf("Action = %q, want CREATE_QA_PLAN", cl.Action)
	}
}

func TestClassifySpecifiedPlanningComplete(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseSpecified)
	f.GetArtifact(ArtifactDesign).Approve("user")
	f.GetArtifact(ArtifactTasks).Approve("user")
	f.GetArtifact(ArtifactQAPlan).Approve("user")

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionTransition {
		t.Errorf("Action = %q, want TRANSITION", cl.Action)
	}
	if cl.TransitionTo != PhasePlanned {
		t.Errorf("TransitionTo = %q, want planned", cl.TransitionTo)
	}
}

func TestClassifyPlannedNeedsBranch(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhasePlanned)
	cfg := DefaultConfig("test")
	// DefaultConfig has RequireBranch: true, feature has no branch

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  cfg,
	})

	if cl.Action != ActionCreateBranch {
		t.Errorf("Action = %q, want CREATE_BRANCH", cl.Action)
	}
	if cl.RuleMatched != "needs-branch" {
		t.Errorf("RuleMatched = %q, want needs-branch", cl.RuleMatched)
	}
}

func TestClassifyPlannedBranchNotRequired(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhasePlanned)
	cfg := DefaultConfig("test")
	cfg.Compliance.RequireBranch = false

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  cfg,
	})

	// Should skip branch rule and go to ready-to-implement
	if cl.Action != ActionTransition {
		t.Errorf("Action = %q, want TRANSITION", cl.Action)
	}
	if cl.TransitionTo != PhaseReady {
		t.Errorf("TransitionTo = %q, want ready", cl.TransitionTo)
	}
}

func TestClassifyPlannedBranchAlreadyExists(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhasePlanned)
	f.Branch = "feature/auth" // Branch already set

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	// Should skip branch rule and go to ready-to-implement
	if cl.Action != ActionTransition {
		t.Errorf("Action = %q, want TRANSITION", cl.Action)
	}
	if cl.TransitionTo != PhaseReady {
		t.Errorf("TransitionTo = %q, want ready", cl.TransitionTo)
	}
}

func TestClassifyPlannedTransitionsToReady(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhasePlanned)
	f.Branch = "feature/auth" // Branch exists, so needs-branch rule won't fire

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionTransition {
		t.Errorf("Action = %q, want TRANSITION", cl.Action)
	}
	if cl.TransitionTo != PhaseReady {
		t.Errorf("TransitionTo = %q, want ready", cl.TransitionTo)
	}
}

func TestClassifyImplementationNextTask(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseImplementation)
	f.Tasks = AddTask(nil, "Task 1")
	f.Tasks = AddTask(f.Tasks, "Task 2")

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionImplementTask {
		t.Errorf("Action = %q, want IMPLEMENT_TASK", cl.Action)
	}
	if cl.TaskID != "task-001" {
		t.Errorf("TaskID = %q, want task-001", cl.TaskID)
	}
}

func TestClassifyImplementationComplete(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseImplementation)
	f.Tasks = AddTask(nil, "Task 1")
	f.Tasks, _ = StartTask(f.Tasks, "task-001")
	f.Tasks, _ = CompleteTask(f.Tasks, "task-001")

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionTransition {
		t.Errorf("Action = %q, want TRANSITION", cl.Action)
	}
	if cl.TransitionTo != PhaseReview {
		t.Errorf("TransitionTo = %q, want review", cl.TransitionTo)
	}
}

func TestClassifyReviewNeeded(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseReview)

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionReviewCode {
		t.Errorf("Action = %q, want REVIEW_CODE", cl.Action)
	}
}

func TestClassifyReviewNeedsFix(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseReview)
	f.GetArtifact(ArtifactReview).MarkNeedsFix()

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionFixReviewIssues {
		t.Errorf("Action = %q, want FIX_REVIEW_ISSUES", cl.Action)
	}
}

func TestClassifyReviewPassed(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseReview)
	f.GetArtifact(ArtifactReview).MarkPassed()

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionTransition {
		t.Errorf("Action = %q, want TRANSITION", cl.Action)
	}
	if cl.TransitionTo != PhaseAudit {
		t.Errorf("TransitionTo = %q, want audit", cl.TransitionTo)
	}
}

func TestClassifyAuditNeeded(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseAudit)

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionAuditCode {
		t.Errorf("Action = %q, want AUDIT_CODE", cl.Action)
	}
}

func TestClassifyAuditPassed(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseAudit)
	f.GetArtifact(ArtifactAudit).MarkPassed()

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionTransition {
		t.Errorf("Action = %q, want TRANSITION", cl.Action)
	}
	if cl.TransitionTo != PhaseQA {
		t.Errorf("TransitionTo = %q, want qa", cl.TransitionTo)
	}
}

func TestClassifyQANeeded(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseQA)

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionRunQA {
		t.Errorf("Action = %q, want RUN_QA", cl.Action)
	}
}

func TestClassifyQAPassed(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseQA)
	f.GetArtifact(ArtifactQAResults).MarkPassed()

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionTransition {
		t.Errorf("Action = %q, want TRANSITION", cl.Action)
	}
	if cl.TransitionTo != PhaseMerge {
		t.Errorf("TransitionTo = %q, want merge", cl.TransitionTo)
	}
}

func TestClassifyMerge(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseMerge)

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionMergeFeature {
		t.Errorf("Action = %q, want MERGE_FEATURE", cl.Action)
	}
}

func TestClassifyArchive(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseReleased)

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionArchive {
		t.Errorf("Action = %q, want ARCHIVE", cl.Action)
	}
}

func TestClassifyBlocked(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseDraft)
	f.AddBlocker("depends on payments")

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionBlocked {
		t.Errorf("Action = %q, want BLOCKED", cl.Action)
	}
}

func TestClassifyDesignNeedsApproval_Rejected(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseSpecified)
	f.GetArtifact(ArtifactDesign).MarkDraft()
	f.GetArtifact(ArtifactDesign).Reject("user") // Rejected artifact triggers same rule

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionAwaitApproval {
		t.Errorf("Action = %q, want AWAIT_APPROVAL", cl.Action)
	}
	if cl.RuleMatched != "design-needs-approval" {
		t.Errorf("RuleMatched = %q, want design-needs-approval", cl.RuleMatched)
	}
}

func TestClassifyAuditHasIssues(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseAudit)
	f.GetArtifact(ArtifactAudit).MarkNeedsFix()

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionRemediateAudit {
		t.Errorf("Action = %q, want REMEDIATE_AUDIT", cl.Action)
	}
	if cl.RuleMatched != "audit-has-issues" {
		t.Errorf("RuleMatched = %q, want audit-has-issues", cl.RuleMatched)
	}
}

func TestClassifyQAHasFailures(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseQA)
	f.GetArtifact(ArtifactQAResults).MarkFailed()

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionFixQAFailures {
		t.Errorf("Action = %q, want FIX_QA_FAILURES", cl.Action)
	}
	if cl.RuleMatched != "qa-has-failures" {
		t.Errorf("RuleMatched = %q, want qa-has-failures", cl.RuleMatched)
	}
}

func TestClassifyNextCommandAndOutputPath(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseDraft)

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.NextCommand != "/spec-feature auth" {
		t.Errorf("NextCommand = %q, want /spec-feature auth", cl.NextCommand)
	}
	if cl.OutputPath != ".sdlc/features/auth/spec.md" {
		t.Errorf("OutputPath = %q, want .sdlc/features/auth/spec.md", cl.OutputPath)
	}
}

func TestClassifyTaskIDForImplementTask(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseImplementation)
	f.Tasks = AddTask(nil, "First Task")

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.TaskID != "task-001" {
		t.Errorf("TaskID = %q, want task-001", cl.TaskID)
	}
	if cl.NextCommand == "" {
		t.Error("NextCommand should be set for implement-next-task")
	}
}

func TestClassifyNoMatchReturnsIdle(t *testing.T) {
	// Create a classifier with no rules
	c := NewClassifierWithRules([]Rule{})
	f := makeTestFeature(PhaseDraft)

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionIdle {
		t.Errorf("Action = %q, want IDLE", cl.Action)
	}
	if cl.RuleMatched != "nothing-to-do" {
		t.Errorf("RuleMatched = %q, want nothing-to-do", cl.RuleMatched)
	}
}

func TestClassifyTasksNeedApproval_Rejected(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseSpecified)
	f.GetArtifact(ArtifactDesign).Approve("user")
	f.GetArtifact(ArtifactTasks).MarkDraft()
	f.GetArtifact(ArtifactTasks).Reject("user")

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionAwaitApproval {
		t.Errorf("Action = %q, want AWAIT_APPROVAL", cl.Action)
	}
	if cl.RuleMatched != "tasks-need-approval" {
		t.Errorf("RuleMatched = %q, want tasks-need-approval", cl.RuleMatched)
	}
}

func TestClassifyQAPlanNeedsApproval_Rejected(t *testing.T) {
	c := NewClassifier()
	f := makeTestFeature(PhaseSpecified)
	f.GetArtifact(ArtifactDesign).Approve("user")
	f.GetArtifact(ArtifactTasks).Approve("user")
	f.GetArtifact(ArtifactQAPlan).MarkDraft()
	f.GetArtifact(ArtifactQAPlan).Reject("user")

	cl := c.Classify(&EvalContext{
		State:   DefaultState("test"),
		Feature: f,
		Config:  DefaultConfig("test"),
	})

	if cl.Action != ActionAwaitApproval {
		t.Errorf("Action = %q, want AWAIT_APPROVAL", cl.Action)
	}
	if cl.RuleMatched != "qa-plan-needs-approval" {
		t.Errorf("RuleMatched = %q, want qa-plan-needs-approval", cl.RuleMatched)
	}
}
