// Command seed applies migrations and the seed file to a fresh
// Postgres. Idempotent — safe to re-run.
//
// Usage:
//
//	BA_POSTGRES_DSN=postgres://... BA_MIGRATIONS_DIR=./migrations \
//	  go run ./cmd/seed
//
// In docker-compose this is a one-shot sidecar that runs before
// the app services start.
package main

import (
	"context"
	"fmt"
	"os"
	"path/filepath"
	"sort"
	"strings"
	"time"

	"git3.techno-world.net/lrosales/broad-announce/internal/postgres"
)

func main() {
	dsn := os.Getenv("BA_POSTGRES_DSN")
	if dsn == "" {
		die("BA_POSTGRES_DSN is required")
	}
	dir := os.Getenv("BA_MIGRATIONS_DIR")
	if dir == "" {
		die("BA_MIGRATIONS_DIR is required")
	}

	ctx, cancel := context.WithTimeout(context.Background(), 60*time.Second)
	defer cancel()

	pool, err := postgres.Connect(ctx, dsn)
	if err != nil {
		die("connect: " + err.Error())
	}
	defer pool.Close()

	if err := applyDir(ctx, pool, dir, "*.up.sql"); err != nil {
		die("apply migrations: " + err.Error())
	}
	// Seed files run in lexical order, so seed.sql → seed_m2.sql
	// → seed_m3.sql → … Each is idempotent (ON CONFLICT DO NOTHING
	// or ON CONFLICT DO UPDATE) and safe to re-run.
	for _, name := range []string{"seed.sql", "seed_m2.sql", "seed_m3.sql"} {
		p := filepath.Join(dir, name)
		if _, err := os.Stat(p); err != nil {
			fmt.Fprintln(os.Stderr, "no", name, "in", dir, "(skipped)")
			continue
		}
		if err := execFile(ctx, pool, p); err != nil {
			die(name+": " + err.Error())
		}
		fmt.Fprintln(os.Stderr, "applied:", name)
	}
	fmt.Fprintln(os.Stderr, "ok")
}

func applyDir(ctx context.Context, pool *postgres.Pool, dir, pattern string) error {
	ups, err := filepath.Glob(filepath.Join(dir, pattern))
	if err != nil {
		return err
	}
	sort.Strings(ups)
	for _, p := range ups {
		if err := execFile(ctx, pool, p); err != nil {
			return fmt.Errorf("%s: %w", filepath.Base(p), err)
		}
		fmt.Fprintln(os.Stderr, "applied:", filepath.Base(p))
	}
	return nil
}

func execFile(ctx context.Context, pool *postgres.Pool, path string) error {
	body, err := os.ReadFile(path)
	if err != nil {
		return err
	}
	// pgx's Pool.Exec supports multi-statement SQL when the
	// underlying connection's protocol-mode supports it. To be
	// safe we acquire a single connection and exec the whole file
	// as one batch. If the file contains a CREATE EXTENSION that
	// requires superuser, the migration container runs as superuser
	// so this is fine.
	conn, err := pool.Acquire(ctx)
	if err != nil {
		return fmt.Errorf("acquire conn: %w", err)
	}
	defer conn.Release()
	_, err = conn.Exec(ctx, string(body))
	return err
}

func die(msg string) {
	fmt.Fprintln(os.Stderr, "seed:", msg)
	os.Exit(1)
}

// _ silences unused import for build tags.
var _ = strings.TrimSpace
