package main

import (
	"context"
	"os"
	"path/filepath"
	"testing"

	"github.com/steveyegge/beads/internal/types"
)

func TestParseChecks(t *testing.T) {
	tests := []struct {
		name      string
		input     string
		want      []string
		wantError bool
	}{
		{
			name:  "empty returns all defaults",
			input: "",
			want:  []string{"orphans", "duplicates", "pollution", "conflicts"},
		},
		{
			name:  "single check",
			input: "orphans",
			want:  []string{"orphans"},
		},
		{
			name:  "multiple checks",
			input: "orphans,duplicates",
			want:  []string{"orphans", "duplicates"},
		},
		{
			name:  "synonym dupes->duplicates",
			input: "dupes",
			want:  []string{"duplicates"},
		},
		{
			name:  "synonym git-conflicts->conflicts",
			input: "git-conflicts",
			want:  []string{"conflicts"},
		},
		{
			name:  "mixed with whitespace",
			input: " orphans , duplicates , pollution ",
			want:  []string{"orphans", "duplicates", "pollution"},
		},
		{
			name:  "deduplication",
			input: "orphans,orphans,duplicates",
			want:  []string{"orphans", "duplicates"},
		},
		{
			name:      "invalid check",
			input:     "orphans,invalid,duplicates",
			wantError: true,
		},
		{
			name:  "empty parts ignored",
			input: "orphans,,duplicates",
			want:  []string{"orphans", "duplicates"},
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			got, err := parseChecks(tt.input)
			if tt.wantError {
				if err == nil {
					t.Errorf("parseChecks(%q) expected error, got nil", tt.input)
				}
				return
			}
			if err != nil {
				t.Fatalf("parseChecks(%q) unexpected error: %v", tt.input, err)
			}
			if len(got) != len(tt.want) {
				t.Errorf("parseChecks(%q) length = %d, want %d", tt.input, len(got), len(tt.want))
				return
			}
			for i := range got {
				if got[i] != tt.want[i] {
					t.Errorf("parseChecks(%q)[%d] = %q, want %q", tt.input, i, got[i], tt.want[i])
				}
			}
		})
	}
}

func TestValidationResultsHasFailures(t *testing.T) {
	tests := []struct {
		name   string
		checks map[string]checkResult
		want   bool
	}{
		{
			name: "no failures - all clean",
			checks: map[string]checkResult{
				"orphans": {issueCount: 0, fixedCount: 0},
				"dupes":   {issueCount: 0, fixedCount: 0},
			},
			want: false,
		},
		{
			name: "has error",
			checks: map[string]checkResult{
				"orphans": {err: os.ErrNotExist},
			},
			want: true,
		},
		{
			name: "issues found but not all fixed",
			checks: map[string]checkResult{
				"orphans": {issueCount: 5, fixedCount: 3},
			},
			want: true,
		},
		{
			name: "issues found and all fixed",
			checks: map[string]checkResult{
				"orphans": {issueCount: 5, fixedCount: 5},
			},
			want: false,
		},
	}

	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			r := &validationResults{checks: tt.checks}
			got := r.hasFailures()
			if got != tt.want {
				t.Errorf("hasFailures() = %v, want %v", got, tt.want)
			}
		})
	}
}

func TestValidationResultsToJSON(t *testing.T) {
	r := &validationResults{
		checks: map[string]checkResult{
			"orphans": {
				issueCount: 3,
				fixedCount: 2,
				suggestions: []string{"Run bd repair"},
			},
			"dupes": {
				issueCount: 0,
				fixedCount: 0,
			},
		},
	}

	output := r.toJSON()

	if output["total_issues"] != 3 {
		t.Errorf("total_issues = %v, want 3", output["total_issues"])
	}
	if output["total_fixed"] != 2 {
		t.Errorf("total_fixed = %v, want 2", output["total_fixed"])
	}
	if output["healthy"] != false {
		t.Errorf("healthy = %v, want false", output["healthy"])
	}

	checks := output["checks"].(map[string]interface{})
	orphans := checks["orphans"].(map[string]interface{})
	if orphans["issue_count"] != 3 {
		t.Errorf("orphans issue_count = %v, want 3", orphans["issue_count"])
	}
	if orphans["fixed_count"] != 2 {
		t.Errorf("orphans fixed_count = %v, want 2", orphans["fixed_count"])
	}
}

func TestValidateOrphanedDeps(t *testing.T) {
	ctx := context.Background()

	allIssues := []*types.Issue{
		{
			ID: "bd-1",
			Dependencies: []*types.Dependency{
				{DependsOnID: "bd-2", Type: types.DepBlocks},
				{DependsOnID: "bd-999", Type: types.DepBlocks}, // orphaned
			},
		},
		{
			ID: "bd-2",
		},
	}

	result := validateOrphanedDeps(ctx, allIssues, false)

	if result.issueCount != 1 {
		t.Errorf("issueCount = %d, want 1", result.issueCount)
	}
	if result.fixedCount != 0 {
		t.Errorf("fixedCount = %d, want 0 (fix=false)", result.fixedCount)
	}
	if len(result.suggestions) == 0 {
		t.Error("expected suggestions")
	}
}

func TestValidateDuplicates(t *testing.T) {
	ctx := context.Background()

	allIssues := []*types.Issue{
		{
			ID:    "bd-1",
			Title: "Same title",
		},
		{
			ID:    "bd-2",
			Title: "Same title",
		},
		{
			ID:    "bd-3",
			Title: "Different",
		},
	}

	result := validateDuplicates(ctx, allIssues, false)

	// Should find 1 duplicate (bd-2 is duplicate of bd-1)
	if result.issueCount != 1 {
		t.Errorf("issueCount = %d, want 1", result.issueCount)
	}
	if len(result.suggestions) == 0 {
		t.Error("expected suggestions")
	}
}

func TestValidatePollution(t *testing.T) {
	ctx := context.Background()

	allIssues := []*types.Issue{
		{
			ID:    "test-1",
			Title: "Test issue",
		},
		{
			ID:    "bd-1",
			Title: "Normal issue",
		},
	}

	result := validatePollution(ctx, allIssues, false)

	// Should detect test-1 as pollution
	if result.issueCount != 1 {
		t.Errorf("issueCount = %d, want 1", result.issueCount)
	}
}

func TestValidateGitConflicts_NoFile(t *testing.T) {
	ctx := context.Background()

	// Create temp dir without JSONL
	tmpDir := t.TempDir()
	beadsDir := filepath.Join(tmpDir, ".beads")
	if err := os.MkdirAll(beadsDir, 0755); err != nil {
		t.Fatal(err)
	}

	// Override dbPath to point to temp dir
	originalDBPath := dbPath
	dbPath = filepath.Join(beadsDir, "beads.db")
	defer func() { dbPath = originalDBPath }()

	result := validateGitConflicts(ctx, false)

	if result.issueCount != 0 {
		t.Errorf("issueCount = %d, want 0 (no file)", result.issueCount)
	}
	if result.err != nil {
		t.Errorf("unexpected error: %v", result.err)
	}
}

func TestValidateGitConflicts_WithMarkers(t *testing.T) {
	ctx := context.Background()

	// Create temp JSONL with conflict markers
	tmpDir := t.TempDir()
	beadsDir := filepath.Join(tmpDir, ".beads")
	if err := os.MkdirAll(beadsDir, 0755); err != nil {
		t.Fatal(err)
	}

	jsonlPath := filepath.Join(beadsDir, "beads.jsonl")
	content := `{"id":"bd-1"}
<<<<<<< HEAD
{"id":"bd-2","title":"Version A"}
=======
{"id":"bd-2","title":"Version B"}
>>>>>>> main
{"id":"bd-3"}`

	if err := os.WriteFile(jsonlPath, []byte(content), 0644); err != nil {
		t.Fatal(err)
	}

	// Override dbPath to point to temp dir
	originalDBPath := dbPath
	dbPath = filepath.Join(beadsDir, "beads.db")
	defer func() { dbPath = originalDBPath }()

	result := validateGitConflicts(ctx, false)

	if result.issueCount != 1 {
		t.Errorf("issueCount = %d, want 1 (conflict found)", result.issueCount)
	}
	if len(result.suggestions) == 0 {
		t.Error("expected suggestions for conflict resolution")
	}
}

func TestValidateGitConflicts_Clean(t *testing.T) {
	ctx := context.Background()

	// Create temp JSONL without conflicts
	tmpDir := t.TempDir()
	beadsDir := filepath.Join(tmpDir, ".beads")
	if err := os.MkdirAll(beadsDir, 0755); err != nil {
		t.Fatal(err)
	}

	jsonlPath := filepath.Join(beadsDir, "beads.jsonl")
	content := `{"id":"bd-1","title":"Normal"}
{"id":"bd-2","title":"Also normal"}`

	if err := os.WriteFile(jsonlPath, []byte(content), 0644); err != nil {
		t.Fatal(err)
	}

	// Override dbPath to point to temp dir
	originalDBPath := dbPath
	dbPath = filepath.Join(beadsDir, "beads.db")
	defer func() { dbPath = originalDBPath }()

	result := validateGitConflicts(ctx, false)

	if result.issueCount != 0 {
		t.Errorf("issueCount = %d, want 0 (clean file)", result.issueCount)
	}
	if result.err != nil {
		t.Errorf("unexpected error: %v", result.err)
	}
}
