package sqlite

import (
	"context"
	"testing"
	"time"

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

func TestCreateTombstone(t *testing.T) {
	store := newTestStore(t, "file::memory:?mode=memory&cache=private")
	ctx := context.Background()

	t.Run("create tombstone for existing issue", func(t *testing.T) {
		issue := &types.Issue{
			ID:        "bd-1",
			Title:     "Test Issue",
			Status:    types.StatusOpen,
			Priority:  1,
			IssueType: types.TypeTask,
		}

		if err := store.CreateIssue(ctx, issue, "test"); err != nil {
			t.Fatalf("Failed to create issue: %v", err)
		}

		// Create tombstone
		if err := store.CreateTombstone(ctx, "bd-1", "tester", "testing tombstone"); err != nil {
			t.Fatalf("CreateTombstone failed: %v", err)
		}

		// Verify issue still exists but is a tombstone
		tombstone, err := store.GetIssue(ctx, "bd-1")
		if err != nil {
			t.Fatalf("Failed to get tombstone: %v", err)
		}
		if tombstone == nil {
			t.Fatal("Tombstone should still exist in database")
		}
		if tombstone.Status != types.StatusTombstone {
			t.Errorf("Expected status=tombstone, got %s", tombstone.Status)
		}
		if tombstone.DeletedAt == nil {
			t.Error("DeletedAt should be set")
		}
		if tombstone.DeletedBy != "tester" {
			t.Errorf("Expected DeletedBy=tester, got %s", tombstone.DeletedBy)
		}
		if tombstone.DeleteReason != "testing tombstone" {
			t.Errorf("Expected DeleteReason='testing tombstone', got %s", tombstone.DeleteReason)
		}
		if tombstone.OriginalType != string(types.TypeTask) {
			t.Errorf("Expected OriginalType=task, got %s", tombstone.OriginalType)
		}
	})

	t.Run("create tombstone for closed issue", func(t *testing.T) {
		// Regression test: closed issues have closed_at set, which must be
		// cleared when creating tombstone due to CHECK constraint:
		// (status = 'closed') = (closed_at IS NOT NULL)
		issue := &types.Issue{
			ID:        "bd-closed-1",
			Title:     "Closed Issue",
			Status:    types.StatusOpen, // Create as open first
			Priority:  1,
			IssueType: types.TypeTask,
		}

		if err := store.CreateIssue(ctx, issue, "test"); err != nil {
			t.Fatalf("Failed to create issue: %v", err)
		}

		// Close the issue to set closed_at
		if err := store.CloseIssue(ctx, "bd-closed-1", "closing for test", "tester"); err != nil {
			t.Fatalf("Failed to close issue: %v", err)
		}

		// Verify closed_at is set
		closedIssue, err := store.GetIssue(ctx, "bd-closed-1")
		if err != nil {
			t.Fatalf("Failed to get closed issue: %v", err)
		}
		if closedIssue.ClosedAt == nil {
			t.Fatal("closed_at should be set for closed issue")
		}

		// Create tombstone - this should work without constraint violation
		if err := store.CreateTombstone(ctx, "bd-closed-1", "tester", "testing tombstone from closed"); err != nil {
			t.Fatalf("CreateTombstone from closed issue failed: %v", err)
		}

		// Verify tombstone was created correctly
		tombstone, err := store.GetIssue(ctx, "bd-closed-1")
		if err != nil {
			t.Fatalf("Failed to get tombstone: %v", err)
		}
		if tombstone.Status != types.StatusTombstone {
			t.Errorf("Expected status=tombstone, got %s", tombstone.Status)
		}
		// closed_at should be nil for tombstone
		if tombstone.ClosedAt != nil {
			t.Error("closed_at should be nil for tombstone")
		}
		if tombstone.DeletedAt == nil {
			t.Error("deleted_at should be set for tombstone")
		}
	})

	t.Run("create tombstone for non-existent issue", func(t *testing.T) {
		err := store.CreateTombstone(ctx, "bd-999", "tester", "testing")
		if err == nil {
			t.Error("CreateTombstone should fail for non-existent issue")
		}
	})

	t.Run("tombstone excluded from normal search", func(t *testing.T) {
		// Create two issues
		issue1 := &types.Issue{
			ID:        "bd-10",
			Title:     "Active Issue",
			Status:    types.StatusOpen,
			Priority:  1,
			IssueType: types.TypeTask,
		}
		issue2 := &types.Issue{
			ID:        "bd-11",
			Title:     "To Be Deleted",
			Status:    types.StatusOpen,
			Priority:  1,
			IssueType: types.TypeTask,
		}

		if err := store.CreateIssue(ctx, issue1, "test"); err != nil {
			t.Fatalf("Failed to create issue1: %v", err)
		}
		if err := store.CreateIssue(ctx, issue2, "test"); err != nil {
			t.Fatalf("Failed to create issue2: %v", err)
		}

		// Create tombstone for issue2
		if err := store.CreateTombstone(ctx, "bd-11", "tester", "test"); err != nil {
			t.Fatalf("CreateTombstone failed: %v", err)
		}

		// Search without tombstones
		results, err := store.SearchIssues(ctx, "", types.IssueFilter{})
		if err != nil {
			t.Fatalf("SearchIssues failed: %v", err)
		}

		// Should only return bd-10 (active issue)
		foundActive := false
		foundTombstone := false
		for _, issue := range results {
			if issue.ID == "bd-10" {
				foundActive = true
			}
			if issue.ID == "bd-11" {
				foundTombstone = true
			}
		}

		if !foundActive {
			t.Error("Active issue bd-10 should be in results")
		}
		if foundTombstone {
			t.Error("Tombstone bd-11 should not be in results")
		}
	})

	t.Run("tombstone included with IncludeTombstones flag", func(t *testing.T) {
		// Search with tombstones included
		filter := types.IssueFilter{IncludeTombstones: true}
		results, err := store.SearchIssues(ctx, "", filter)
		if err != nil {
			t.Fatalf("SearchIssues failed: %v", err)
		}

		// Should return both active and tombstone
		foundActive := false
		foundTombstone := false
		for _, issue := range results {
			if issue.ID == "bd-10" {
				foundActive = true
			}
			if issue.ID == "bd-11" {
				foundTombstone = true
			}
		}

		if !foundActive {
			t.Error("Active issue bd-10 should be in results")
		}
		if !foundTombstone {
			t.Error("Tombstone bd-11 should be in results when IncludeTombstones=true")
		}
	})

	t.Run("search for tombstones explicitly", func(t *testing.T) {
		// Search for tombstone status explicitly
		status := types.StatusTombstone
		filter := types.IssueFilter{Status: &status}
		results, err := store.SearchIssues(ctx, "", filter)
		if err != nil {
			t.Fatalf("SearchIssues failed: %v", err)
		}

		// Should only return tombstones
		for _, issue := range results {
			if issue.Status != types.StatusTombstone {
				t.Errorf("Expected only tombstones, found %s with status %s", issue.ID, issue.Status)
			}
		}

		// Should find at least bd-11
		foundTombstone := false
		for _, issue := range results {
			if issue.ID == "bd-11" {
				foundTombstone = true
			}
		}
		if !foundTombstone {
			t.Error("Should find tombstone bd-11")
		}
	})
}

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

	t.Run("single issue deletion creates tombstone", func(t *testing.T) {
		store := newTestStore(t, "file::memory:?mode=memory&cache=private")

		issue := &types.Issue{ID: "bd-1", Title: "Test", Status: types.StatusOpen, Priority: 1, IssueType: types.TypeFeature}
		if err := store.CreateIssue(ctx, issue, "test"); err != nil {
			t.Fatalf("Failed to create issue: %v", err)
		}

		result, err := store.DeleteIssues(ctx, []string{"bd-1"}, false, true, false)
		if err != nil {
			t.Fatalf("DeleteIssues failed: %v", err)
		}
		if result.DeletedCount != 1 {
			t.Errorf("Expected 1 deletion, got %d", result.DeletedCount)
		}

		// Issue should still exist as tombstone
		tombstone, err := store.GetIssue(ctx, "bd-1")
		if err != nil {
			t.Fatalf("Failed to get issue: %v", err)
		}
		if tombstone == nil {
			t.Fatal("Issue should exist as tombstone")
		}
		if tombstone.Status != types.StatusTombstone {
			t.Errorf("Expected tombstone status, got %s", tombstone.Status)
		}
		if tombstone.OriginalType != string(types.TypeFeature) {
			t.Errorf("Expected OriginalType=feature, got %s", tombstone.OriginalType)
		}
	})

	t.Run("batch deletion creates tombstones", func(t *testing.T) {
		store := newTestStore(t, "file::memory:?mode=memory&cache=private")

		issue1 := &types.Issue{ID: "bd-10", Title: "Test 1", Status: types.StatusOpen, Priority: 1, IssueType: types.TypeBug}
		issue2 := &types.Issue{ID: "bd-11", Title: "Test 2", Status: types.StatusOpen, Priority: 1, IssueType: types.TypeTask}

		if err := store.CreateIssue(ctx, issue1, "test"); err != nil {
			t.Fatalf("Failed to create issue1: %v", err)
		}
		if err := store.CreateIssue(ctx, issue2, "test"); err != nil {
			t.Fatalf("Failed to create issue2: %v", err)
		}

		result, err := store.DeleteIssues(ctx, []string{"bd-10", "bd-11"}, false, true, false)
		if err != nil {
			t.Fatalf("DeleteIssues failed: %v", err)
		}
		if result.DeletedCount != 2 {
			t.Errorf("Expected 2 deletions, got %d", result.DeletedCount)
		}

		// Both should exist as tombstones
		tombstone1, _ := store.GetIssue(ctx, "bd-10")
		if tombstone1 == nil || tombstone1.Status != types.StatusTombstone {
			t.Error("bd-10 should be tombstone")
		}
		if tombstone1.OriginalType != string(types.TypeBug) {
			t.Errorf("bd-10: Expected OriginalType=bug, got %s", tombstone1.OriginalType)
		}

		tombstone2, _ := store.GetIssue(ctx, "bd-11")
		if tombstone2 == nil || tombstone2.Status != types.StatusTombstone {
			t.Error("bd-11 should be tombstone")
		}
		if tombstone2.OriginalType != string(types.TypeTask) {
			t.Errorf("bd-11: Expected OriginalType=task, got %s", tombstone2.OriginalType)
		}
	})

	t.Run("batch deletion of closed issues creates tombstones (bd-tnsq)", func(t *testing.T) {
		// Regression test: batch deletion of closed issues was failing with
		// CHECK constraint: (status = 'closed') = (closed_at IS NOT NULL)
		// because closed_at wasn't being set to NULL when creating tombstones
		store := newTestStore(t, "file::memory:?mode=memory&cache=private")

		now := time.Now()
		closedAt := now.Add(-24 * time.Hour)

		// Create closed issues (with closed_at set)
		issue1 := &types.Issue{
			ID:        "bd-closed-10",
			Title:     "Closed Issue 1",
			Status:    types.StatusClosed,
			Priority:  1,
			IssueType: types.TypeBug,
			ClosedAt:  &closedAt,
		}
		issue2 := &types.Issue{
			ID:        "bd-closed-11",
			Title:     "Closed Issue 2",
			Status:    types.StatusClosed,
			Priority:  1,
			IssueType: types.TypeTask,
			ClosedAt:  &closedAt,
		}

		if err := store.CreateIssue(ctx, issue1, "test"); err != nil {
			t.Fatalf("Failed to create closed issue1: %v", err)
		}
		if err := store.CreateIssue(ctx, issue2, "test"); err != nil {
			t.Fatalf("Failed to create closed issue2: %v", err)
		}

		// Batch delete closed issues - this was failing before the fix
		result, err := store.DeleteIssues(ctx, []string{"bd-closed-10", "bd-closed-11"}, false, true, false)
		if err != nil {
			t.Fatalf("DeleteIssues on closed issues failed: %v", err)
		}
		if result.DeletedCount != 2 {
			t.Errorf("Expected 2 deletions, got %d", result.DeletedCount)
		}

		// Verify tombstones have closed_at = NULL (required by CHECK constraint)
		tombstone1, _ := store.GetIssue(ctx, "bd-closed-10")
		if tombstone1 == nil || tombstone1.Status != types.StatusTombstone {
			t.Error("bd-closed-10 should be tombstone")
		}
		if tombstone1.ClosedAt != nil {
			t.Error("bd-closed-10 tombstone should have closed_at = NULL")
		}

		tombstone2, _ := store.GetIssue(ctx, "bd-closed-11")
		if tombstone2 == nil || tombstone2.Status != types.StatusTombstone {
			t.Error("bd-closed-11 should be tombstone")
		}
		if tombstone2.ClosedAt != nil {
			t.Error("bd-closed-11 tombstone should have closed_at = NULL")
		}
	})

	t.Run("cascade deletion creates tombstones", func(t *testing.T) {
		store := newTestStore(t, "file::memory:?mode=memory&cache=private")

		// Create chain: bd-1 -> bd-2 -> bd-3
		issue1 := &types.Issue{ID: "bd-1", Title: "Parent", Status: types.StatusOpen, Priority: 1, IssueType: types.TypeEpic}
		issue2 := &types.Issue{ID: "bd-2", Title: "Child", Status: types.StatusOpen, Priority: 1, IssueType: types.TypeTask}
		issue3 := &types.Issue{ID: "bd-3", Title: "Grandchild", Status: types.StatusOpen, Priority: 1, IssueType: types.TypeTask}

		if err := store.CreateIssue(ctx, issue1, "test"); err != nil {
			t.Fatalf("Failed to create issue1: %v", err)
		}
		if err := store.CreateIssue(ctx, issue2, "test"); err != nil {
			t.Fatalf("Failed to create issue2: %v", err)
		}
		if err := store.CreateIssue(ctx, issue3, "test"); err != nil {
			t.Fatalf("Failed to create issue3: %v", err)
		}

		dep1 := &types.Dependency{IssueID: "bd-2", DependsOnID: "bd-1", Type: types.DepBlocks}
		if err := store.AddDependency(ctx, dep1, "test"); err != nil {
			t.Fatalf("Failed to add dependency: %v", err)
		}
		dep2 := &types.Dependency{IssueID: "bd-3", DependsOnID: "bd-2", Type: types.DepBlocks}
		if err := store.AddDependency(ctx, dep2, "test"); err != nil {
			t.Fatalf("Failed to add dependency: %v", err)
		}

		result, err := store.DeleteIssues(ctx, []string{"bd-1"}, true, false, false)
		if err != nil {
			t.Fatalf("DeleteIssues with cascade failed: %v", err)
		}
		if result.DeletedCount != 3 {
			t.Errorf("Expected 3 deletions (cascade), got %d", result.DeletedCount)
		}

		// All should exist as tombstones
		for _, id := range []string{"bd-1", "bd-2", "bd-3"} {
			tombstone, _ := store.GetIssue(ctx, id)
			if tombstone == nil {
				t.Errorf("%s should exist as tombstone", id)
				continue
			}
			if tombstone.Status != types.StatusTombstone {
				t.Errorf("%s should have tombstone status, got %s", id, tombstone.Status)
			}
		}
	})

	t.Run("dependencies removed from tombstones", func(t *testing.T) {
		store := newTestStore(t, "file::memory:?mode=memory&cache=private")

		// Create issues with dependency
		issue1 := &types.Issue{ID: "bd-100", Title: "Parent", Status: types.StatusOpen, Priority: 1, IssueType: types.TypeTask}
		issue2 := &types.Issue{ID: "bd-101", Title: "Child", Status: types.StatusOpen, Priority: 1, IssueType: types.TypeTask}

		if err := store.CreateIssue(ctx, issue1, "test"); err != nil {
			t.Fatalf("Failed to create issue1: %v", err)
		}
		if err := store.CreateIssue(ctx, issue2, "test"); err != nil {
			t.Fatalf("Failed to create issue2: %v", err)
		}

		dep := &types.Dependency{IssueID: "bd-101", DependsOnID: "bd-100", Type: types.DepBlocks}
		if err := store.AddDependency(ctx, dep, "test"); err != nil {
			t.Fatalf("Failed to add dependency: %v", err)
		}

		// Delete parent
		_, err := store.DeleteIssues(ctx, []string{"bd-100"}, false, true, false)
		if err != nil {
			t.Fatalf("DeleteIssues failed: %v", err)
		}

		// Dependency should be removed
		deps, err := store.GetDependencies(ctx, "bd-101")
		if err != nil {
			t.Fatalf("GetDependencies failed: %v", err)
		}
		if len(deps) != 0 {
			t.Errorf("Dependency should be removed, found %d dependencies", len(deps))
		}
	})
}
