package registry

import (
	"database/sql"
	"fmt"

	_ "modernc.org/sqlite"
)

// Open opens the registry database at the given path and runs migrations.
func Open(path string) (*sql.DB, error) {
	db, err := sql.Open("sqlite", path)
	if err != nil {
		return nil, fmt.Errorf("open database: %w", err)
	}

	for _, pragma := range []string{
		"PRAGMA journal_mode = WAL",
		"PRAGMA foreign_keys = ON",
		"PRAGMA busy_timeout = 5000",
	} {
		if _, err := db.Exec(pragma); err != nil {
			_ = db.Close()
			return nil, fmt.Errorf("exec %q: %w", pragma, err)
		}
	}

	if err := migrate(db); err != nil {
		_ = db.Close()
		return nil, fmt.Errorf("migrate: %w", err)
	}

	return db, nil
}

func migrate(db *sql.DB) error {
	_, err := db.Exec(`
		CREATE TABLE IF NOT EXISTS schema_migrations (
			version INTEGER PRIMARY KEY,
			applied_at TEXT NOT NULL DEFAULT (datetime('now'))
		);
	`)
	if err != nil {
		return fmt.Errorf("create migrations table: %w", err)
	}

	for i, m := range migrations {
		version := i + 1
		var count int
		if err := db.QueryRow("SELECT COUNT(*) FROM schema_migrations WHERE version = ?", version).Scan(&count); err != nil {
			return fmt.Errorf("check migration %d: %w", version, err)
		}
		if count > 0 {
			continue
		}

		if _, err := db.Exec(m); err != nil {
			return fmt.Errorf("run migration %d: %w", version, err)
		}
		if _, err := db.Exec("INSERT INTO schema_migrations (version) VALUES (?)", version); err != nil {
			return fmt.Errorf("record migration %d: %w", version, err)
		}
	}

	return nil
}

var migrations = []string{
	// Migration 1: initial schema
	`
	CREATE TABLE IF NOT EXISTS services (
		name       TEXT PRIMARY KEY,
		active     INTEGER NOT NULL DEFAULT 1,
		created_at TEXT NOT NULL DEFAULT (datetime('now')),
		updated_at TEXT NOT NULL DEFAULT (datetime('now'))
	);

	CREATE TABLE IF NOT EXISTS components (
		name           TEXT NOT NULL,
		service        TEXT NOT NULL REFERENCES services(name) ON DELETE CASCADE,
		image          TEXT NOT NULL,
		network        TEXT NOT NULL DEFAULT 'bridge',
		user_spec      TEXT NOT NULL DEFAULT '',
		restart        TEXT NOT NULL DEFAULT 'unless-stopped',
		desired_state  TEXT NOT NULL DEFAULT 'running',
		observed_state TEXT NOT NULL DEFAULT 'unknown',
		version        TEXT NOT NULL DEFAULT '',
		created_at     TEXT NOT NULL DEFAULT (datetime('now')),
		updated_at     TEXT NOT NULL DEFAULT (datetime('now')),
		PRIMARY KEY (service, name)
	);

	CREATE TABLE IF NOT EXISTS component_ports (
		service   TEXT NOT NULL,
		component TEXT NOT NULL,
		mapping   TEXT NOT NULL,
		PRIMARY KEY (service, component, mapping),
		FOREIGN KEY (service, component) REFERENCES components(service, name) ON DELETE CASCADE
	);

	CREATE TABLE IF NOT EXISTS component_volumes (
		service   TEXT NOT NULL,
		component TEXT NOT NULL,
		mapping   TEXT NOT NULL,
		PRIMARY KEY (service, component, mapping),
		FOREIGN KEY (service, component) REFERENCES components(service, name) ON DELETE CASCADE
	);

	CREATE TABLE IF NOT EXISTS component_cmd (
		service   TEXT NOT NULL,
		component TEXT NOT NULL,
		position  INTEGER NOT NULL,
		arg       TEXT NOT NULL,
		PRIMARY KEY (service, component, position),
		FOREIGN KEY (service, component) REFERENCES components(service, name) ON DELETE CASCADE
	);

	CREATE TABLE IF NOT EXISTS events (
		id         INTEGER PRIMARY KEY AUTOINCREMENT,
		service    TEXT NOT NULL,
		component  TEXT NOT NULL,
		prev_state TEXT NOT NULL,
		new_state  TEXT NOT NULL,
		timestamp  TEXT NOT NULL DEFAULT (datetime('now'))
	);

	CREATE INDEX IF NOT EXISTS idx_events_component_time
		ON events(service, component, timestamp);
	`,

	// Migration 2: component routes
	`
	CREATE TABLE IF NOT EXISTS component_routes (
		service   TEXT NOT NULL,
		component TEXT NOT NULL,
		name      TEXT NOT NULL,
		port      INTEGER NOT NULL,
		mode      TEXT NOT NULL DEFAULT 'l4',
		hostname  TEXT NOT NULL DEFAULT '',
		host_port INTEGER NOT NULL DEFAULT 0,
		PRIMARY KEY (service, component, name),
		FOREIGN KEY (service, component) REFERENCES components(service, name) ON DELETE CASCADE
	);
	`,
}
