package migrations_test

// idempotency_test.go — Pre-fix tests for migration idempotency (ETHOS #4).
//
// F-B1-15 LOW: 7+ migrations lack IF NOT EXISTS on CREATE/ALTER statements.
//   ETHOS #4 requires all schema changes to be idempotent.
//   Running migrations twice on an existing DB will fail.
//
// Run: cd aggregator-server && go test ./internal/database/migrations/... -v -run TestMigrations

import (
	"fmt"
	"os"
	"strings"
	"testing"
)

// checkIdempotency counts non-idempotent SQL statements in a migration file.
// Go's regexp doesn't support negative lookahead, so we use string matching.
func checkIdempotency(src string) (violations int, details []string) {
	lines := strings.Split(src, "\n")
	for i, line := range lines {
		lower := strings.ToLower(strings.TrimSpace(line))
		// Skip comments
		if strings.HasPrefix(lower, "--") {
			continue
		}

		// CREATE TABLE without IF NOT EXISTS
		if strings.Contains(lower, "create table") && !strings.Contains(lower, "if not exists") {
			violations++
			details = append(details, fmt.Sprintf("  line %d: CREATE TABLE without IF NOT EXISTS", i+1))
		}
		// CREATE INDEX without IF NOT EXISTS
		if (strings.Contains(lower, "create index") || strings.Contains(lower, "create unique index")) &&
			!strings.Contains(lower, "if not exists") {
			violations++
			details = append(details, fmt.Sprintf("  line %d: CREATE INDEX without IF NOT EXISTS", i+1))
		}
		// ADD COLUMN without IF NOT EXISTS
		if strings.Contains(lower, "add column") && !strings.Contains(lower, "if not exists") {
			violations++
			details = append(details, fmt.Sprintf("  line %d: ADD COLUMN without IF NOT EXISTS", i+1))
		}
	}
	return
}

// ---------------------------------------------------------------------------
// Test 4.1 — Documents that idempotency violations exist (F-B1-15)
//
// Category: PASS-NOW (documents violations)
// ---------------------------------------------------------------------------

func TestMigrationsHaveIdempotencyViolations(t *testing.T) {
	// POST-FIX (F-B1-15): All migrations should now be idempotent.
	// This test confirms no violations remain.
	files, err := os.ReadDir(".")
	if err != nil {
		t.Fatalf("failed to read migrations directory: %v", err)
	}

	totalViolations := 0

	for _, f := range files {
		if !strings.HasSuffix(f.Name(), ".up.sql") {
			continue
		}

		content, err := os.ReadFile(f.Name())
		if err != nil {
			continue
		}

		violations, details := checkIdempotency(string(content))
		if violations > 0 {
			totalViolations += violations
			t.Logf("[WARNING] [server] [database] %s: %d violations", f.Name(), violations)
			for _, d := range details {
				t.Logf("  %s", d)
			}
		}
	}

	if totalViolations > 0 {
		t.Errorf("[ERROR] [server] [database] %d idempotency violations remain", totalViolations)
	}
	t.Log("[INFO] [server] [database] F-B1-15 FIXED: all migrations are idempotent")
}

// ---------------------------------------------------------------------------
// Test 4.2 — All migrations MUST be idempotent (assert fix)
//
// Category: FAIL-NOW / PASS-AFTER-FIX
// ---------------------------------------------------------------------------

func TestAllMigrationsAreIdempotent(t *testing.T) {
	files, err := os.ReadDir(".")
	if err != nil {
		t.Fatalf("failed to read migrations directory: %v", err)
	}

	for _, f := range files {
		if !strings.HasSuffix(f.Name(), ".up.sql") {
			continue
		}
		// Skip A-series migrations (already idempotent)
		if strings.HasPrefix(f.Name(), "025_") || strings.HasPrefix(f.Name(), "026_") {
			continue
		}

		content, err := os.ReadFile(f.Name())
		if err != nil {
			continue
		}

		violations, details := checkIdempotency(string(content))
		if violations > 0 {
			t.Errorf("[ERROR] [server] [database] %s: %d idempotency violations\n"+
				"F-B1-15: all schema changes must use IF NOT EXISTS per ETHOS #4.\n%s",
				f.Name(), violations, strings.Join(details, "\n"))
		}
	}
}
