package infra

import (
	"database/sql"
	"testing"
	"testing/fstest"

	_ "github.com/mattn/go-sqlite3"
)

func newTestDB(t *testing.T) *sql.DB {
	t.Helper()
	db, err := sql.Open("sqlite3", ":memory:")
	if err != nil {
		t.Fatalf("open db: %v", err)
	}
	t.Cleanup(func() { db.Close() })
	return db
}

func TestApplyVersionedMigrations(t *testing.T) {
	t.Run("aplica todas desde cero y registra schema_migrations", func(t *testing.T) {
		db := newTestDB(t)
		fsys := fstest.MapFS{
			"migrations/001_init.sql":      {Data: []byte("CREATE TABLE users (id INTEGER PRIMARY KEY);")},
			"migrations/002_add_email.sql": {Data: []byte("ALTER TABLE users ADD COLUMN email TEXT;")},
		}

		if err := ApplyVersionedMigrations(db, fsys, "migrations"); err != nil {
			t.Fatalf("unexpected error: %v", err)
		}

		// Verify schema_migrations has 2 rows
		var count int
		if err := db.QueryRow("SELECT COUNT(*) FROM schema_migrations").Scan(&count); err != nil {
			t.Fatalf("count schema_migrations: %v", err)
		}
		if count != 2 {
			t.Errorf("schema_migrations rows: got %d, want 2", count)
		}

		// Verify MAX(version) == 2
		var maxV int
		if err := db.QueryRow("SELECT MAX(version) FROM schema_migrations").Scan(&maxV); err != nil {
			t.Fatalf("max version: %v", err)
		}
		if maxV != 2 {
			t.Errorf("max version: got %d, want 2", maxV)
		}

		// Verify the table from migration 001 exists
		if _, err := db.Exec("INSERT INTO users (id) VALUES (1)"); err != nil {
			t.Errorf("users table not created: %v", err)
		}
	})

	t.Run("idempotente por version, no vuelve a aplicar", func(t *testing.T) {
		db := newTestDB(t)
		fsys := fstest.MapFS{
			"migrations/001_init.sql": {Data: []byte("CREATE TABLE things (id INTEGER PRIMARY KEY);")},
		}

		// First run
		if err := ApplyVersionedMigrations(db, fsys, "migrations"); err != nil {
			t.Fatalf("first run: %v", err)
		}
		// Second run — must not error even though CREATE TABLE would fail normally
		if err := ApplyVersionedMigrations(db, fsys, "migrations"); err != nil {
			t.Fatalf("second run: %v", err)
		}

		var count int
		if err := db.QueryRow("SELECT COUNT(*) FROM schema_migrations").Scan(&count); err != nil {
			t.Fatalf("count: %v", err)
		}
		if count != 1 {
			t.Errorf("expected 1 row in schema_migrations, got %d", count)
		}
	})

	t.Run("migracion intermedia falla, version anterior no avanza", func(t *testing.T) {
		db := newTestDB(t)
		fsys := fstest.MapFS{
			"migrations/001_init.sql": {Data: []byte("CREATE TABLE ok (id INTEGER PRIMARY KEY);")},
			"migrations/002_bad.sql":  {Data: []byte("THIS IS NOT VALID SQL !!!;")},
			"migrations/003_more.sql": {Data: []byte("CREATE TABLE more (id INTEGER PRIMARY KEY);")},
		}

		err := ApplyVersionedMigrations(db, fsys, "migrations")
		if err == nil {
			t.Fatal("expected error from bad migration, got nil")
		}

		// Only version 1 should be recorded
		var maxV int
		if err2 := db.QueryRow("SELECT COALESCE(MAX(version), 0) FROM schema_migrations").Scan(&maxV); err2 != nil {
			t.Fatalf("read version: %v", err2)
		}
		if maxV != 1 {
			t.Errorf("max version after failure: got %d, want 1", maxV)
		}

		// Migration 003 table must NOT exist
		if _, err2 := db.Exec("INSERT INTO more (id) VALUES (1)"); err2 == nil {
			t.Error("table 'more' should not exist after failed migration 002")
		}
	})

	t.Run("archivos sin prefijo numerico se ignoran", func(t *testing.T) {
		db := newTestDB(t)
		fsys := fstest.MapFS{
			"migrations/README.sql":   {Data: []byte("-- this should be ignored")},
			"migrations/init.sql":     {Data: []byte("CREATE TABLE bad (id INTEGER PRIMARY KEY);")},
			"migrations/001_good.sql": {Data: []byte("CREATE TABLE good (id INTEGER PRIMARY KEY);")},
		}

		if err := ApplyVersionedMigrations(db, fsys, "migrations"); err != nil {
			t.Fatalf("unexpected error: %v", err)
		}

		var count int
		if err := db.QueryRow("SELECT COUNT(*) FROM schema_migrations").Scan(&count); err != nil {
			t.Fatalf("count: %v", err)
		}
		if count != 1 {
			t.Errorf("expected 1 migration applied, got %d", count)
		}

		// Only 'good' table should exist
		if _, err := db.Exec("INSERT INTO good (id) VALUES (1)"); err != nil {
			t.Errorf("table 'good' should exist: %v", err)
		}
		if _, err := db.Exec("INSERT INTO bad (id) VALUES (1)"); err == nil {
			t.Error("table 'bad' should NOT exist")
		}
	})

	t.Run("dir vacio no error y no crea schema_migrations", func(t *testing.T) {
		db := newTestDB(t)
		fsys := fstest.MapFS{
			"migrations/.keep": {Data: []byte("")},
		}

		if err := ApplyVersionedMigrations(db, fsys, "migrations"); err != nil {
			t.Fatalf("unexpected error on empty dir: %v", err)
		}

		// schema_migrations IS created (the CREATE TABLE IF NOT EXISTS runs regardless)
		// but it must be empty
		var count int
		if err := db.QueryRow("SELECT COUNT(*) FROM schema_migrations").Scan(&count); err != nil {
			t.Fatalf("count schema_migrations: %v", err)
		}
		if count != 0 {
			t.Errorf("expected 0 rows in schema_migrations, got %d", count)
		}
	})
}
