// Package db owns the Controller's durable state in Postgres. It exposes
// a small typed surface (UpsertAgent, InsertInstance, ListInstances, …)
// built on pgx/v5 with a connection pool. Migrations are embedded so the
// binary is self-contained.
package db

import (
	"context"
	"fmt"
	"time"

	"github.com/jackc/pgx/v5/pgxpool"
)

// DB wraps a pgx pool with our typed query methods.
type DB struct {
	pool *pgxpool.Pool
}

// Open connects to Postgres using dsn, pings once to fail fast on bad
// credentials or unreachable server, and runs any pending migrations.
func Open(ctx context.Context, dsn string) (*DB, error) {
	cfg, err := pgxpool.ParseConfig(dsn)
	if err != nil {
		return nil, fmt.Errorf("parse db dsn: %w", err)
	}
	cfg.MaxConns = 10
	cfg.MaxConnLifetime = 30 * time.Minute

	pool, err := pgxpool.NewWithConfig(ctx, cfg)
	if err != nil {
		return nil, fmt.Errorf("new pool: %w", err)
	}

	pingCtx, cancel := context.WithTimeout(ctx, 5*time.Second)
	defer cancel()
	if err := pool.Ping(pingCtx); err != nil {
		pool.Close()
		return nil, fmt.Errorf("ping db: %w", err)
	}

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

	return &DB{pool: pool}, nil
}

// Close releases pool resources.
func (db *DB) Close() {
	if db != nil && db.pool != nil {
		db.pool.Close()
	}
}

// Pool exposes the underlying pool for tests / advanced callers.
func (db *DB) Pool() *pgxpool.Pool { return db.pool }
