// Package gormtest provides reusable GORM database fixtures for tests.
package gormtest

import (
	"fmt"
	"path/filepath"
	"strings"
	"sync/atomic"
	"testing"

	"gorm.io/driver/sqlite"
	"gorm.io/gorm"
)

var sqliteDBSeq atomic.Uint64

func OpenSQLiteDB(t *testing.T, suffix ...string) (*gorm.DB, error) {
	t.Helper()
	return openSQLiteDB(t, SQLiteDSN(t, suffix...))
}

// OpenConcurrentSQLiteDB opens a file-backed WAL database for service tests
// that exercise reads and writes on separate connections.
func OpenConcurrentSQLiteDB(t *testing.T) (*gorm.DB, error) {
	t.Helper()
	return openSQLiteDB(t, "file:"+filepath.Join(t.TempDir(), "service.db")+"?_journal_mode=WAL&_busy_timeout=5000&_txlock=immediate")
}

func openSQLiteDB(t *testing.T, dsn string) (*gorm.DB, error) {
	t.Helper()
	db, err := gorm.Open(sqlite.Open(dsn), &gorm.Config{})
	if err != nil {
		return nil, err
	}
	t.Cleanup(func() {
		sqlDB, err := db.DB()
		if err != nil {
			t.Fatalf("get sqlite DB: %v", err)
		}
		if err := sqlDB.Close(); err != nil {
			t.Fatalf("close sqlite DB: %v", err)
		}
	})
	return db, nil
}

func SQLiteDSN(t *testing.T, suffix ...string) string {
	t.Helper()
	name := sanitizeSQLiteName(t.Name())
	if len(suffix) > 0 && strings.TrimSpace(suffix[0]) != "" {
		name += "_" + sanitizeSQLiteName(suffix[0])
	}
	name = fmt.Sprintf("%s_%d", name, sqliteDBSeq.Add(1))
	return "file:" + name + "?mode=memory&cache=shared"
}

func sanitizeSQLiteName(name string) string {
	replacer := strings.NewReplacer("/", "_", "\\", "_", " ", "_", ":", "_")
	name = replacer.Replace(strings.TrimSpace(name))
	if name == "" {
		return "service_test"
	}
	return name
}
