package store

import (
	"database/sql"
	"embed"
	"fmt"
	"log/slog"
	"strings"

	_ "modernc.org/sqlite" // درایور SQLite خالص Go (بدون cgo)
)

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

// Store دسترسی به دیتابیس را کپسوله می‌کند.
type Store struct {
	DB *sql.DB
}

// Open اتصال SQLite را باز کرده و migrations را اجرا می‌کند.
func Open(path string) (*Store, error) {
	// pragmaها برای کارایی و یکپارچگی روی سرور کوچک
	dsn := fmt.Sprintf("file:%s?_pragma=journal_mode(WAL)&_pragma=busy_timeout(5000)&_pragma=foreign_keys(ON)", path)
	db, err := sql.Open("sqlite", dsn)
	if err != nil {
		return nil, err
	}
	// SQLite تک‌نویسنده است؛ pool کوچک نگه می‌داریم.
	db.SetMaxOpenConns(1)
	if err := db.Ping(); err != nil {
		return nil, err
	}
	s := &Store{DB: db}
	if err := s.migrate(); err != nil {
		return nil, err
	}
	return s, nil
}

func (s *Store) migrate() error {
	// جدول ردیابی تا هر migration فقط یک‌بار اجرا شود.
	if _, err := s.DB.Exec(
		`CREATE TABLE IF NOT EXISTS schema_migrations (
			name       TEXT PRIMARY KEY,
			applied_at TEXT NOT NULL DEFAULT (datetime('now'))
		)`); err != nil {
		return fmt.Errorf("create schema_migrations: %w", err)
	}

	entries, err := migrationsFS.ReadDir("migrations")
	if err != nil {
		return fmt.Errorf("read migrations: %w", err)
	}
	for _, e := range entries {
		if e.IsDir() {
			continue
		}
		name := e.Name()

		var applied int
		if err := s.DB.QueryRow(
			`SELECT COUNT(*) FROM schema_migrations WHERE name = ?`, name).Scan(&applied); err != nil {
			return err
		}
		if applied > 0 {
			continue
		}

		b, err := migrationsFS.ReadFile("migrations/" + name)
		if err != nil {
			return err
		}
		if _, err := s.DB.Exec(string(b)); err != nil {
			// دیتابیس توسعه‌ای که قبل از افزودن ردیابی، این migration را اعمال کرده
			// (ستون/جدول از قبل هست) ⇒ به‌عنوان اعمال‌شده علامت می‌زنیم.
			if alreadyApplied(err) {
				slog.Warn("migration schema already present; marking applied", "name", name)
			} else {
				return fmt.Errorf("apply %s: %w", name, err)
			}
		}
		if _, err := s.DB.Exec(
			`INSERT INTO schema_migrations (name) VALUES (?)`, name); err != nil {
			return fmt.Errorf("record %s: %w", name, err)
		}
	}
	return nil
}

// alreadyApplied تشخیص می‌دهد خطا ناشی از وجود قبلی همان تغییر است.
func alreadyApplied(err error) bool {
	m := strings.ToLower(err.Error())
	return strings.Contains(m, "duplicate column") || strings.Contains(m, "already exists")
}

func (s *Store) Close() error { return s.DB.Close() }
