//go:build integration

// Package testutil contains opt-in helpers for isolated integration databases.
package testutil

import (
	"crypto/rand"
	"encoding/hex"
	"fmt"
	"net/url"
	"os"
	"strings"
	"testing"

	"github.com/stretchr/testify/require"
	"gorm.io/driver/postgres"
	"gorm.io/gorm"
)

// OpenIsolatedPostgres creates a random schema and binds every pooled test
// connection to it through search_path. It never reads the production DSN.
func OpenIsolatedPostgres(t testing.TB, config *gorm.Config) *gorm.DB {
	t.Helper()
	dsn := strings.TrimSpace(os.Getenv("TEST_DATABASE_URL"))
	require.NotEmpty(t, dsn, "TEST_DATABASE_URL is required for integration tests and must point to an isolated database")

	schema := randomSchemaName(t)
	admin, err := gorm.Open(postgres.Open(dsn), config)
	require.NoError(t, err)
	adminSQL, err := admin.DB()
	require.NoError(t, err)
	require.NoError(t, admin.Exec(fmt.Sprintf(`CREATE SCHEMA "%s"`, schema)).Error)

	schemaDSN, err := postgresSchemaDSN(dsn, schema)
	require.NoError(t, err)
	database, err := gorm.Open(postgres.Open(schemaDSN), config)
	require.NoError(t, err)
	isolationSQL, err := database.DB()
	require.NoError(t, err)
	t.Cleanup(func() {
		_ = isolationSQL.Close()
		_ = admin.Exec(fmt.Sprintf(`DROP SCHEMA IF EXISTS "%s" CASCADE`, schema)).Error
		_ = adminSQL.Close()
	})
	return database
}

func randomSchemaName(t testing.TB) string {
	t.Helper()
	value := make([]byte, 12)
	_, err := rand.Read(value)
	require.NoError(t, err)
	return "senlin_test_" + hex.EncodeToString(value)
}

func postgresSchemaDSN(dsn, schema string) (string, error) {
	if strings.Contains(dsn, "://") {
		parsed, err := url.Parse(dsn)
		if err != nil {
			return "", err
		}
		query := parsed.Query()
		query.Set("search_path", schema)
		parsed.RawQuery = query.Encode()
		return parsed.String(), nil
	}
	return strings.TrimSpace(dsn) + " search_path=" + schema, nil
}
