package core

import (
	"embed"
	"log"

	_ "github.com/mattn/go-sqlite3"
)

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

// Idempotently initialize the database. Safe to call unconditionally.
func InitDatabase(db *DB) error {
	rows, err := db.Query(`
		select exists (
			select 1
			from sqlite_master
			where type = 'table'
			and name = 'migrations'
		)
	`)
	if err != nil {
		return err
	}

	var exists bool
	for rows.Next() {
		err = rows.Scan(&exists)
		if err != nil {
			return err
		}
	}

	if exists {
		return nil
	}

	err = ApplyMigration(db, "0000_baseline.sql")
	return err
}

// Get a map of migration names to whether the migration has been applied.
func GetPendingMigrations(db *DB) (map[string]bool, error) {
	allMigrations, err := migrations.ReadDir("sql")
	if err != nil {
		return nil, err
	}

	complete := map[string]bool{}
	for _, mig := range allMigrations {
		complete[mig.Name()] = false
	}

	rows, err := db.Query("select name from migrations")
	if err != nil {
		return nil, err
	}
	for rows.Next() {
		var name string
		err = rows.Scan(&name)
		if err != nil {
			return nil, err
		}
		complete[name] = true
	}

	return complete, nil
}

// Apply a migration by name.
func ApplyMigration(db *DB, name string) error {
	data, err := migrations.ReadFile("sql/" + name)
	if err != nil {
		log.Fatalf("Missing migration %s", name)
	}
	log.Printf("Applying migration %s", name)
	_, err = db.Exec(string(data))
	if err != nil {
		return err
	}
	_, err = db.Exec("insert into migrations (name) values (?)", name)
	return err
}

// Apply all pending migrations.
func MigrateDatabase(db *DB) error {
	pending, err := GetPendingMigrations(db)
	if err != nil {
		return err
	}
	for name, complete := range pending {
		if !complete {
			err = ApplyMigration(db, name)
			if err != nil {
				return err
			}
		}
	}
	return nil
}