package main

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

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

func TestRenumberWithGaps(t *testing.T) {
	// Create a temporary directory for the test database
	tmpDir := t.TempDir()
	dbPath := filepath.Join(tmpDir, "test.db")

	// Initialize store
	testStore, err := sqlite.New(dbPath)
	if err != nil {
		t.Fatalf("failed to create store: %v", err)
	}
	defer testStore.Close()

	ctx := context.Background()

	// Set up config
	if err := testStore.SetConfig(ctx, "issue_prefix", "bd"); err != nil {
		t.Fatalf("failed to set prefix: %v", err)
	}

	// Create issues with large gaps in numbering (simulating compacted database)
	// This reproduces the scenario from bd-345: 107 issues with IDs 1-344
	testIssues := []struct {
		id    string
		title string
	}{
		{"bd-1", "Issue 1"},
		{"bd-4", "Issue 4"},     // Gap here (2, 3 missing)
		{"bd-100", "Issue 100"}, // Large gap
		{"bd-200", "Issue 200"}, // Another large gap
		{"bd-344", "Issue 344"}, // Final issue
	}

	for _, tc := range testIssues {
		issue := &types.Issue{
			Title:       tc.title,
			Description: "Test issue for renumbering",
			Priority:    1,
			IssueType:   "task",
			Status:      "open",
		}
		if err := testStore.CreateIssue(ctx, issue, "test"); err != nil {
			t.Fatalf("failed to create issue: %v", err)
		}
		// Manually update ID to simulate gaps
		if err := testStore.UpdateIssueID(ctx, issue.ID, tc.id, issue, "test"); err != nil {
			t.Fatalf("failed to set issue ID to %s: %v", tc.id, err)
		}
	}

	// Add a dependency to test that it gets updated
	dep := &types.Dependency{
		IssueID:     "bd-4",
		DependsOnID: "bd-1",
		Type:        "blocks",
	}
	if err := testStore.AddDependency(ctx, dep, "test"); err != nil {
		t.Fatalf("failed to add dependency: %v", err)
	}

	// Get all issues before renumbering
	issuesBefore, err := testStore.SearchIssues(ctx, "", types.IssueFilter{})
	if err != nil {
		t.Fatalf("failed to list issues before: %v", err)
	}
	if len(issuesBefore) != 5 {
		t.Fatalf("expected 5 issues, got %d", len(issuesBefore))
	}

	// Build the ID mapping (what renumber would create)
	idMapping := map[string]string{
		"bd-1":   "bd-1",
		"bd-4":   "bd-2",
		"bd-100": "bd-3",
		"bd-200": "bd-4",
		"bd-344": "bd-5",
	}

	// Temporarily set the global store for renumberIssuesInDB
	oldStore := store
	store = testStore
	defer func() { store = oldStore }()

	// Run the renumbering
	if err := renumberIssuesInDB(ctx, "bd", idMapping, issuesBefore); err != nil {
		t.Fatalf("renumberIssuesInDB failed: %v", err)
	}

	// Verify all issues were renumbered correctly
	issuesAfter, err := testStore.SearchIssues(ctx, "", types.IssueFilter{})
	if err != nil {
		t.Fatalf("failed to list issues after: %v", err)
	}

	if len(issuesAfter) != 5 {
		t.Fatalf("expected 5 issues after renumbering, got %d", len(issuesAfter))
	}

	// Check that new IDs are sequential 1-5
	expectedIDs := map[string]bool{
		"bd-1": true,
		"bd-2": true,
		"bd-3": true,
		"bd-4": true,
		"bd-5": true,
	}

	for _, issue := range issuesAfter {
		if !expectedIDs[issue.ID] {
			t.Errorf("unexpected issue ID after renumbering: %s", issue.ID)
		}
		delete(expectedIDs, issue.ID)
	}

	if len(expectedIDs) > 0 {
		t.Errorf("missing expected IDs after renumbering: %v", expectedIDs)
	}

	// Verify dependency was updated using GetAllDependencyRecords
	finalDeps, err := testStore.GetAllDependencyRecords(ctx)
	if err != nil {
		t.Fatalf("failed to get dependencies: %v", err)
	}

	foundDep := false
	if deps, ok := finalDeps["bd-2"]; ok {
		for _, dep := range deps {
			if dep.DependsOnID == "bd-1" && dep.Type == "blocks" {
				foundDep = true
				break
			}
		}
	}

	if !foundDep {
		t.Errorf("dependency not updated correctly: expected bd-2 -> bd-1")
	}
}

func TestRenumberWithTextReferences(t *testing.T) {
	tmpDir := t.TempDir()
	dbPath := filepath.Join(tmpDir, "test.db")

	testStore, err := sqlite.New(dbPath)
	if err != nil {
		t.Fatalf("failed to create store: %v", err)
	}
	defer testStore.Close()

	ctx := context.Background()

	if err := testStore.SetConfig(ctx, "issue_prefix", "bd"); err != nil {
		t.Fatalf("failed to set prefix: %v", err)
	}

	// Create issues with text references to each other
	issue1 := &types.Issue{
		Title:       "First issue",
		Description: "See bd-2 for details",
		Priority:    1,
		IssueType:   "task",
		Status:      "open",
	}
	if err := testStore.CreateIssue(ctx, issue1, "test"); err != nil {
		t.Fatalf("failed to create issue: %v", err)
	}
	// Manually set ID to bd-1
	oldID1 := issue1.ID
	if err := testStore.UpdateIssueID(ctx, oldID1, "bd-1", issue1, "test"); err != nil {
		t.Fatalf("failed to set issue ID: %v", err)
	}

	issue2 := &types.Issue{
		Title:       "Second issue",
		Description: "Blocks bd-1",
		Notes:       "Also see bd-1 and bd-100",
		Priority:    1,
		IssueType:   "task",
		Status:      "open",
	}
	if err := testStore.CreateIssue(ctx, issue2, "test"); err != nil {
		t.Fatalf("failed to create issue: %v", err)
	}
	// Manually set ID to bd-100
	oldID2 := issue2.ID
	if err := testStore.UpdateIssueID(ctx, oldID2, "bd-100", issue2, "test"); err != nil {
		t.Fatalf("failed to set issue ID: %v", err)
	}

	// Renumber: bd-1 -> bd-1, bd-100 -> bd-2
	issues, _ := testStore.SearchIssues(ctx, "", types.IssueFilter{})
	idMapping := map[string]string{
		"bd-1":   "bd-1",
		"bd-100": "bd-2",
	}

	oldStore := store
	store = testStore
	defer func() { store = oldStore }()

	if err := renumberIssuesInDB(ctx, "bd", idMapping, issues); err != nil {
		t.Fatalf("renumberIssuesInDB failed: %v", err)
	}

	// Verify text references were updated
	updated1, err := testStore.GetIssue(ctx, "bd-1")
	if err != nil {
		t.Fatalf("failed to get bd-1: %v", err)
	}
	if updated1.Description != "See bd-2 for details" {
		t.Errorf("bd-1 description not updated: got %q, want %q",
			updated1.Description, "See bd-2 for details")
	}

	updated2, err := testStore.GetIssue(ctx, "bd-2")
	if err != nil {
		t.Fatalf("failed to get bd-2: %v", err)
	}
	if updated2.Description != "Blocks bd-1" {
		t.Errorf("bd-2 description not updated: got %q, want %q",
			updated2.Description, "Blocks bd-1")
	}
	if updated2.Notes != "Also see bd-1 and bd-2" {
		t.Errorf("bd-2 notes not updated: got %q, want %q",
			updated2.Notes, "Also see bd-1 and bd-2")
	}
}

func TestRenumberEmptyDatabase(t *testing.T) {
	tmpDir := t.TempDir()
	dbPath := filepath.Join(tmpDir, "test.db")

	testStore, err := sqlite.New(dbPath)
	if err != nil {
		t.Fatalf("failed to create store: %v", err)
	}
	defer testStore.Close()

	ctx := context.Background()

	if err := testStore.SetConfig(ctx, "issue_prefix", "bd"); err != nil {
		t.Fatalf("failed to set prefix: %v", err)
	}

	oldStore := store
	store = testStore
	defer func() { store = oldStore }()

	// Renumber should succeed with no issues
	issues, _ := testStore.SearchIssues(ctx, "", types.IssueFilter{})
	if err := renumberIssuesInDB(ctx, "bd", map[string]string{}, issues); err != nil {
		t.Fatalf("renumberIssuesInDB failed on empty database: %v", err)
	}
}

// Cleanup any test databases
func TestMain(m *testing.M) {
	code := m.Run()
	os.Exit(code)
}
