package sqlite

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

	"github.com/steveyegge/beads/internal/types"
	_ "modernc.org/sqlite"
)

// TestMigrateIssueCountersTable tests that the migration properly creates
// the issue_counters table and syncs it from existing issues
func TestMigrateIssueCountersTable(t *testing.T) {
	// Create temporary directory
	tmpDir, err := os.MkdirTemp("", "beads-migration-test-*")
	if err != nil {
		t.Fatalf("failed to create temp dir: %v", err)
	}
	defer os.RemoveAll(tmpDir)

	dbPath := filepath.Join(tmpDir, "test.db")

	// Step 1: Create database with old schema (no issue_counters table)
	db, err := sql.Open("sqlite", dbPath+"?_journal_mode=WAL&_foreign_keys=ON")
	if err != nil {
		t.Fatalf("failed to open database: %v", err)
	}

	// Create minimal schema (issues table only, no issue_counters)
	_, err = db.Exec(`
		CREATE TABLE IF NOT EXISTS issues (
			id TEXT PRIMARY KEY,
			title TEXT NOT NULL,
			description TEXT NOT NULL DEFAULT '',
			design TEXT NOT NULL DEFAULT '',
			acceptance_criteria TEXT NOT NULL DEFAULT '',
			notes TEXT NOT NULL DEFAULT '',
			status TEXT NOT NULL DEFAULT 'open',
			priority INTEGER NOT NULL DEFAULT 2,
			issue_type TEXT NOT NULL DEFAULT 'task',
			assignee TEXT,
			estimated_minutes INTEGER,
			created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
			closed_at DATETIME
		);

		CREATE TABLE IF NOT EXISTS config (
			key TEXT PRIMARY KEY,
			value TEXT NOT NULL
		);
	`)
	if err != nil {
		t.Fatalf("failed to create old schema: %v", err)
	}

	// Insert some existing issues with IDs
	_, err = db.Exec(`
		INSERT INTO issues (id, title, status, priority, issue_type)
		VALUES
			('bd-5', 'Issue 5', 'open', 2, 'task'),
			('bd-10', 'Issue 10', 'open', 2, 'task'),
			('bd-15', 'Issue 15', 'open', 2, 'task'),
			('custom-3', 'Custom 3', 'open', 2, 'task'),
			('custom-7', 'Custom 7', 'open', 2, 'task')
	`)
	if err != nil {
		t.Fatalf("failed to insert test issues: %v", err)
	}

	// Verify issue_counters table doesn't exist yet
	var tableName string
	err = db.QueryRow(`
		SELECT name FROM sqlite_master
		WHERE type='table' AND name='issue_counters'
	`).Scan(&tableName)
	if err != sql.ErrNoRows {
		t.Fatalf("Expected issue_counters table to not exist, but it does")
	}

	db.Close()

	// Step 2: Open database with New() which should trigger migration
	store, err := New(dbPath)
	if err != nil {
		t.Fatalf("failed to create storage (migration failed): %v", err)
	}
	defer store.Close()

	// Step 3: Verify issue_counters table now exists
	err = store.db.QueryRow(`
		SELECT name FROM sqlite_master
		WHERE type='table' AND name='issue_counters'
	`).Scan(&tableName)
	if err != nil {
		t.Fatalf("Expected issue_counters table to exist after migration: %v", err)
	}

	// Step 4: Verify counters were synced correctly
	ctx := context.Background()

	// Check bd prefix counter (max is bd-15)
	var bdCounter int
	err = store.db.QueryRowContext(ctx,
		`SELECT last_id FROM issue_counters WHERE prefix = 'bd'`).Scan(&bdCounter)
	if err != nil {
		t.Fatalf("Failed to query bd counter: %v", err)
	}
	if bdCounter != 15 {
		t.Errorf("Expected bd counter to be 15, got %d", bdCounter)
	}

	// Check custom prefix counter (max is custom-7)
	var customCounter int
	err = store.db.QueryRowContext(ctx,
		`SELECT last_id FROM issue_counters WHERE prefix = 'custom'`).Scan(&customCounter)
	if err != nil {
		t.Fatalf("Failed to query custom counter: %v", err)
	}
	if customCounter != 7 {
		t.Errorf("Expected custom counter to be 7, got %d", customCounter)
	}

	// Step 5: Verify next auto-generated IDs are correct
	// Set prefix to bd
	err = store.SetConfig(ctx, "issue_prefix", "bd")
	if err != nil {
		t.Fatalf("Failed to set config: %v", err)
	}

	issue := &types.Issue{
		Title:     "New issue",
		Status:    types.StatusOpen,
		Priority:  2,
		IssueType: types.TypeTask,
	}

	err = store.CreateIssue(ctx, issue, "test-user")
	if err != nil {
		t.Fatalf("CreateIssue failed: %v", err)
	}

	// Should be bd-16 (after bd-15)
	if issue.ID != "bd-16" {
		t.Errorf("Expected bd-16, got %s", issue.ID)
	}
}

// TestMigrateIssueCountersTableEmptyDB tests migration on a fresh database
func TestMigrateIssueCountersTableEmptyDB(t *testing.T) {
	tmpDir, err := os.MkdirTemp("", "beads-migration-empty-*")
	if err != nil {
		t.Fatalf("failed to create temp dir: %v", err)
	}
	defer os.RemoveAll(tmpDir)

	dbPath := filepath.Join(tmpDir, "test.db")

	// Create a fresh database with New() - should create table with no issues
	store, err := New(dbPath)
	if err != nil {
		t.Fatalf("failed to create storage: %v", err)
	}
	defer store.Close()

	// Verify table exists
	var tableName string
	err = store.db.QueryRow(`
		SELECT name FROM sqlite_master
		WHERE type='table' AND name='issue_counters'
	`).Scan(&tableName)
	if err != nil {
		t.Fatalf("Expected issue_counters table to exist: %v", err)
	}

	// Verify no counters exist (since no issues)
	var count int
	err = store.db.QueryRow(`SELECT COUNT(*) FROM issue_counters`).Scan(&count)
	if err != nil {
		t.Fatalf("Failed to query counters: %v", err)
	}
	if count != 0 {
		t.Errorf("Expected 0 counters in empty DB, got %d", count)
	}

	// Create first issue - should work fine
	ctx := context.Background()
	
	// Set the issue prefix to "bd" for this test
	err = store.SetConfig(ctx, "issue_prefix", "bd")
	if err != nil {
		t.Fatalf("Failed to set issue_prefix: %v", err)
	}
	
	issue := &types.Issue{
		Title:     "First issue",
		Status:    types.StatusOpen,
		Priority:  2,
		IssueType: types.TypeTask,
	}

	err = store.CreateIssue(ctx, issue, "test-user")
	if err != nil {
		t.Fatalf("CreateIssue failed: %v", err)
	}

	// Should be bd-1
	if issue.ID != "bd-1" {
		t.Errorf("Expected bd-1, got %s", issue.ID)
	}
}

// TestMigrateIssueCountersTableIdempotent verifies that running migration
// multiple times is safe and doesn't corrupt data
func TestMigrateIssueCountersTableIdempotent(t *testing.T) {
	tmpDir, err := os.MkdirTemp("", "beads-migration-idempotent-*")
	if err != nil {
		t.Fatalf("failed to create temp dir: %v", err)
	}
	defer os.RemoveAll(tmpDir)

	dbPath := filepath.Join(tmpDir, "test.db")

	// Create database and migrate
	store1, err := New(dbPath)
	if err != nil {
		t.Fatalf("failed to create storage: %v", err)
	}

	// Create some issues
	ctx := context.Background()
	
	// Set the issue prefix to "bd" for this test
	err = store1.SetConfig(ctx, "issue_prefix", "bd")
	if err != nil {
		t.Fatalf("Failed to set issue_prefix: %v", err)
	}
	
	issue := &types.Issue{
		Title:     "Test issue",
		Status:    types.StatusOpen,
		Priority:  2,
		IssueType: types.TypeTask,
	}
	err = store1.CreateIssue(ctx, issue, "test-user")
	if err != nil {
		t.Fatalf("CreateIssue failed: %v", err)
	}

	firstID := issue.ID // Should be bd-1
	store1.Close()

	// Re-open database (triggers migration again)
	store2, err := New(dbPath)
	if err != nil {
		t.Fatalf("failed to re-open storage: %v", err)
	}
	defer store2.Close()

	// Verify counter is still correct
	var bdCounter int
	err = store2.db.QueryRowContext(ctx,
		`SELECT last_id FROM issue_counters WHERE prefix = 'bd'`).Scan(&bdCounter)
	if err != nil {
		t.Fatalf("Failed to query bd counter: %v", err)
	}
	if bdCounter != 1 {
		t.Errorf("Expected bd counter to be 1 after idempotent migration, got %d", bdCounter)
	}

	// Create another issue
	issue2 := &types.Issue{
		Title:     "Second issue",
		Status:    types.StatusOpen,
		Priority:  2,
		IssueType: types.TypeTask,
	}
	err = store2.CreateIssue(ctx, issue2, "test-user")
	if err != nil {
		t.Fatalf("CreateIssue failed: %v", err)
	}

	// Should be bd-2 (not bd-1 again)
	if issue2.ID != "bd-2" {
		t.Errorf("Expected bd-2, got %s", issue2.ID)
	}

	// Verify first issue still exists
	firstIssue, err := store2.GetIssue(ctx, firstID)
	if err != nil {
		t.Fatalf("Failed to get first issue: %v", err)
	}
	if firstIssue == nil {
		t.Errorf("First issue was lost after re-opening database")
	}
}
