package infra

import (
	"testing"
)

func makeMig(version int, name, upSQL string) Migration {
	return Migration{Version: version, Name: name, UpSQL: upSQL, DownSQL: "DROP TABLE IF EXISTS t;"}
}

func TestMigrationValidate(t *testing.T) {
	t.Run("secuencia valida retorna sin errores", func(t *testing.T) {
		migrations := []Migration{
			makeMig(1, "create_users", "CREATE TABLE users (id TEXT PRIMARY KEY);"),
			makeMig(2, "add_email", "ALTER TABLE users ADD COLUMN email TEXT;"),
			makeMig(3, "create_roles", "CREATE TABLE roles (id TEXT PRIMARY KEY);"),
		}
		errs := MigrationValidate(migrations)
		if len(errs) != 0 {
			t.Errorf("expected no errors, got: %v", errs)
		}
	})

	t.Run("secuencia vacia retorna sin errores", func(t *testing.T) {
		errs := MigrationValidate([]Migration{})
		if len(errs) != 0 {
			t.Errorf("expected no errors for empty slice, got: %v", errs)
		}
	})

	t.Run("version duplicada reporta error", func(t *testing.T) {
		migrations := []Migration{
			makeMig(1, "create_users", "CREATE TABLE users (id TEXT PRIMARY KEY);"),
			makeMig(1, "create_users_dup", "CREATE TABLE users2 (id TEXT PRIMARY KEY);"),
			makeMig(2, "add_email", "ALTER TABLE users ADD COLUMN email TEXT;"),
		}
		errs := MigrationValidate(migrations)
		if len(errs) == 0 {
			t.Fatal("expected error for duplicate version, got none")
		}
		found := false
		for _, e := range errs {
			if containsStr(e, "duplicate") {
				found = true
			}
		}
		if !found {
			t.Errorf("expected 'duplicate' in errors, got: %v", errs)
		}
	})

	t.Run("hueco en versiones reporta version faltante", func(t *testing.T) {
		migrations := []Migration{
			makeMig(1, "create_users", "CREATE TABLE users (id TEXT PRIMARY KEY);"),
			makeMig(3, "create_roles", "CREATE TABLE roles (id TEXT PRIMARY KEY);"),
		}
		errs := MigrationValidate(migrations)
		if len(errs) == 0 {
			t.Fatal("expected error for gap in versions, got none")
		}
		found := false
		for _, e := range errs {
			if containsStr(e, "gap") || containsStr(e, "missing 2") {
				found = true
			}
		}
		if !found {
			t.Errorf("expected gap error in errors, got: %v", errs)
		}
	})

	t.Run("up_sql vacio reporta error", func(t *testing.T) {
		migrations := []Migration{
			{Version: 1, Name: "create_users", UpSQL: "", DownSQL: "DROP TABLE users;"},
		}
		errs := MigrationValidate(migrations)
		if len(errs) == 0 {
			t.Fatal("expected error for empty up_sql, got none")
		}
		found := false
		for _, e := range errs {
			if containsStr(e, "empty up_sql") {
				found = true
			}
		}
		if !found {
			t.Errorf("expected 'empty up_sql' in errors, got: %v", errs)
		}
	})

	t.Run("nombre vacio reporta error", func(t *testing.T) {
		migrations := []Migration{
			{Version: 1, Name: "", UpSQL: "CREATE TABLE users (id TEXT PRIMARY KEY);"},
		}
		errs := MigrationValidate(migrations)
		if len(errs) == 0 {
			t.Fatal("expected error for empty name, got none")
		}
		found := false
		for _, e := range errs {
			if containsStr(e, "empty name") {
				found = true
			}
		}
		if !found {
			t.Errorf("expected 'empty name' in errors, got: %v", errs)
		}
	})

	t.Run("versiones que no empiezan en 1 reportan error", func(t *testing.T) {
		migrations := []Migration{
			makeMig(2, "create_users", "CREATE TABLE users (id TEXT PRIMARY KEY);"),
			makeMig(3, "add_email", "ALTER TABLE users ADD COLUMN email TEXT;"),
		}
		errs := MigrationValidate(migrations)
		if len(errs) == 0 {
			t.Fatal("expected error for versions not starting at 1, got none")
		}
		found := false
		for _, e := range errs {
			if containsStr(e, "start at 1") {
				found = true
			}
		}
		if !found {
			t.Errorf("expected 'start at 1' error, got: %v", errs)
		}
	})

	t.Run("multiple errores se reportan todos", func(t *testing.T) {
		migrations := []Migration{
			{Version: 2, Name: "", UpSQL: ""},
			{Version: 4, Name: "something", UpSQL: "CREATE TABLE x (id TEXT);"},
		}
		errs := MigrationValidate(migrations)
		// Expect: not starting at 1, gap between 2 and 4, empty name for v2, empty up_sql for v2
		if len(errs) < 3 {
			t.Errorf("expected at least 3 errors, got %d: %v", len(errs), errs)
		}
	})
}

func containsStr(s, sub string) bool {
	return len(s) >= len(sub) && (s == sub || len(sub) == 0 ||
		func() bool {
			for i := 0; i <= len(s)-len(sub); i++ {
				if s[i:i+len(sub)] == sub {
					return true
				}
			}
			return false
		}())
}
