// Package store отвечает за подключение к SQLite и миграции схемы.
package store

import (
	"embed"
	"fmt"
	"os"
	"path/filepath"

	"github.com/jmoiron/sqlx"
	"github.com/pressly/goose/v3"
	_ "modernc.org/sqlite" // драйвер database/sql, имя "sqlite"

	// Go-миграции goose (регистрируются в init(); SQL-миграции — embed ниже).
	_ "git.vakhrushev.me/av/jellybit/internal/store/migrations"
)

//go:embed migrations/*.sql
var migrationsFS embed.FS

// Store оборачивает подключение к базе.
type Store struct {
	DB *sqlx.DB
}

// Open открывает (создавая при необходимости) базу по пути dbPath,
// прогоняет миграции и возвращает готовый Store.
func Open(dbPath string) (*Store, error) {
	if dir := filepath.Dir(dbPath); dir != "" {
		if err := os.MkdirAll(dir, 0o755); err != nil {
			return nil, fmt.Errorf("create db dir %q: %w", dir, err)
		}
	}

	// _txlock=immediate: каждая явная транзакция открывается как BEGIN
	// IMMEDIATE — write-транзакция с самого начала. На этом держатся
	// guarded-методы инварианта «одна активная загрузка на infohash»
	// (check-then-write без гонок: SQLite сериализует писателей).
	dsn := fmt.Sprintf(
		"file:%s?_txlock=immediate&_pragma=busy_timeout(5000)&_pragma=journal_mode(WAL)&_pragma=foreign_keys(1)",
		dbPath,
	)
	db, err := sqlx.Connect("sqlite", dsn)
	if err != nil {
		return nil, fmt.Errorf("open sqlite %q: %w", dbPath, err)
	}

	if err := migrate(db); err != nil {
		_ = db.Close()
		return nil, err
	}

	return &Store{DB: db}, nil
}

func migrate(db *sqlx.DB) error {
	goose.SetBaseFS(migrationsFS)
	goose.SetLogger(goose.NopLogger()) // не ломать JSON-логи; ошибки идут через return
	if err := goose.SetDialect("sqlite3"); err != nil {
		return fmt.Errorf("set goose dialect: %w", err)
	}
	if err := goose.Up(db.DB, "migrations"); err != nil {
		return fmt.Errorf("run migrations: %w", err)
	}
	return nil
}

// Close закрывает подключение к базе.
func (s *Store) Close() error {
	return s.DB.Close()
}
