package testenv

import (
	"context"
	"fmt"
	"os/exec"
	"path/filepath"
	"runtime"
	"sync"
	"testing"
	"time"
)

const (
	BackendImage = "galaxy/backend:integration"
	GatewayImage = "galaxy/gateway:integration"
	GameImage    = "galaxy/game:integration"
)

var (
	backendOnce sync.Once
	backendErr  error
	gatewayOnce sync.Once
	gatewayErr  error
	gameOnce    sync.Once
	gameErr     error
)

// EnsureBackendImage builds galaxy/backend:integration once per
// process. Subsequent calls reuse the result.
func EnsureBackendImage(t *testing.T) {
	t.Helper()
	backendOnce.Do(func() {
		backendErr = buildImage(BackendImage, "backend/Dockerfile")
	})
	if backendErr != nil {
		t.Skipf("build %s: %v", BackendImage, backendErr)
	}
}

// EnsureGatewayImage builds galaxy/gateway:integration once per
// process.
func EnsureGatewayImage(t *testing.T) {
	t.Helper()
	gatewayOnce.Do(func() {
		gatewayErr = buildImage(GatewayImage, "gateway/Dockerfile")
	})
	if gatewayErr != nil {
		t.Skipf("build %s: %v", GatewayImage, gatewayErr)
	}
}

// EnsureGameImage builds galaxy/game:integration once per process.
func EnsureGameImage(t *testing.T) {
	t.Helper()
	gameOnce.Do(func() {
		gameErr = buildImage(GameImage, "game/Dockerfile")
	})
	if gameErr != nil {
		t.Skipf("build %s: %v", GameImage, gameErr)
	}
}

// integrationImageLabel is the docker label stamped onto every image
// built from `integration/testenv/images.go`. The pre-clean script
// (`integration/scripts/preclean.sh`) keys off this label to wipe
// stale builds without touching testcontainers-pulled service images
// (postgres, redis, ryuk, mailpit) which we want to keep cached.
const integrationImageLabel = "galaxy.test.kind=integration-image"

func buildImage(tag, dockerfile string) error {
	root, err := workspaceRoot()
	if err != nil {
		return err
	}
	ctx, cancel := context.WithTimeout(context.Background(), 10*time.Minute)
	defer cancel()

	cmd := exec.CommandContext(ctx, "docker", "build",
		"-t", tag,
		"-f", filepath.Join(root, dockerfile),
		"--label", integrationImageLabel,
		root,
	)
	out, err := cmd.CombinedOutput()
	if err != nil {
		return fmt.Errorf("docker build %s: %v\n%s", tag, err, string(out))
	}
	return nil
}

func workspaceRoot() (string, error) {
	_, file, _, ok := runtime.Caller(0)
	if !ok {
		return "", fmt.Errorf("runtime.Caller failed")
	}
	// integration/testenv/images.go → workspace root
	return filepath.Dir(filepath.Dir(filepath.Dir(file))), nil
}
