package web

// testdb_test.go exposes a setupTestDB helper for the web package integration
// tests. The implementation is a verbatim copy of the auth package's
// setupTestDB (~20 LOC kernel) so the web package does not import a test-only
// function from another package (Go does not allow importing _test.go files).
//
// Decision: duplication chosen over a shared non-test helper to keep the
// coupling surface minimal and avoid moving test infra into production code.
// Recorded in 02-04-SUMMARY.md.

import (
	"context"
	"database/sql"
	"fmt"
	"os"
	"path/filepath"
	"runtime"
	"sync"
	"testing"
	"time"

	"github.com/google/uuid"
	_ "github.com/jackc/pgx/v5/stdlib"
	"github.com/jackc/pgx/v5/pgxpool"
	"github.com/pressly/goose/v3"
)

func migrationsDir() string {
	_, filename, _, _ := runtime.Caller(0)
	// filename: .../backend/internal/web/testdb_test.go
	// migrations: .../backend/migrations
	return filepath.Join(filepath.Dir(filename), "..", "..", "migrations")
}

var webGooseMu sync.Mutex

func setupTestDB(t *testing.T) (*pgxpool.Pool, func()) {
	t.Helper()

	dsn := os.Getenv("TEST_DATABASE_URL")
	if dsn == "" {
		dsn = os.Getenv("DATABASE_URL")
	}
	if dsn == "" {
		t.Skip("TEST_DATABASE_URL (or DATABASE_URL) not set — integration test skipped")
		return nil, nil
	}

	rawID := uuid.New().String()[:12]
	cleanID := make([]byte, len(rawID))
	for i := 0; i < len(rawID); i++ {
		if rawID[i] == '-' {
			cleanID[i] = '_'
		} else {
			cleanID[i] = rawID[i]
		}
	}
	schemaName := "test_" + string(cleanID)

	bootstrapDB, err := sql.Open("pgx", dsn)
	if err != nil {
		t.Fatalf("setupTestDB: sql.Open bootstrap: %v", err)
	}
	if err := bootstrapDB.Ping(); err != nil {
		bootstrapDB.Close()
		t.Fatalf("setupTestDB: ping: %v", err)
	}
	if _, err := bootstrapDB.Exec(fmt.Sprintf("CREATE SCHEMA %q", schemaName)); err != nil {
		bootstrapDB.Close()
		t.Fatalf("setupTestDB: CREATE SCHEMA: %v", err)
	}
	bootstrapDB.Close()

	sep := "?"
	for i := 0; i < len(dsn); i++ {
		if dsn[i] == '?' {
			sep = "&"
			break
		}
	}
	schemaDSN := fmt.Sprintf("%s%ssearch_path=%s,public", dsn, sep, schemaName)

	versionTable := schemaName + "_goose_version"
	{
		webGooseMu.Lock()
		defer webGooseMu.Unlock()

		prevTable := goose.TableName()
		goose.SetTableName(versionTable)
		defer goose.SetTableName(prevTable)

		goose.SetBaseFS(nil)
		if err := goose.SetDialect("postgres"); err != nil {
			webDropSchema(dsn, schemaName)
			t.Fatalf("setupTestDB: goose.SetDialect: %v", err)
		}

		schemaDB, err := sql.Open("pgx", schemaDSN)
		if err != nil {
			webDropSchema(dsn, schemaName)
			t.Fatalf("setupTestDB: sql.Open schema-scoped: %v", err)
		}
		if err := goose.Up(schemaDB, migrationsDir()); err != nil {
			schemaDB.Close()
			webDropSchema(dsn, schemaName)
			t.Fatalf("setupTestDB: goose.Up: %v", err)
		}
		schemaDB.Close()
	}

	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
	defer cancel()

	cfg, err := pgxpool.ParseConfig(schemaDSN)
	if err != nil {
		webDropSchema(dsn, schemaName)
		t.Fatalf("setupTestDB: pgxpool.ParseConfig: %v", err)
	}
	cfg.MaxConns = 5

	pool, err := pgxpool.NewWithConfig(ctx, cfg)
	if err != nil {
		webDropSchema(dsn, schemaName)
		t.Fatalf("setupTestDB: pgxpool.NewWithConfig: %v", err)
	}

	cleanup := func() {
		pool.Close()
		webDropSchema(dsn, schemaName)
	}
	return pool, cleanup
}

func webDropSchema(dsn, schemaName string) {
	db, err := sql.Open("pgx", dsn)
	if err != nil {
		return
	}
	defer db.Close()
	db.Exec(fmt.Sprintf("DROP SCHEMA IF EXISTS %q CASCADE", schemaName)) //nolint:errcheck
}
