// Package runtime owns the lifecycle of game-engine containers and the
// engine-version registry on the platform side. It is the single
// component permitted to talk to the Docker daemon
// (`internal/dockerclient`) and to running engine HTTP listeners
// (`internal/engineclient`); cross-cutting concerns such as the lobby
// state machine, notification fan-out, or player-mapping persistence
// live in their domain packages and reach into runtime through a
// narrow interface set documented in `deps.go`.
//
// The package introduces the package on top of the The implementation lobby. The
// lobby `RuntimeGateway` shifts from a logger-only no-op to a real
// adapter backed by `*runtime.Service`; runtime publishes snapshots
// back into lobby through `LobbyConsumer.OnRuntimeSnapshot`. The
// engine-version registry CRUD endpoints under
// `/api/v1/admin/engine-versions/*` and the runtime admin/user proxy
// endpoints flip from 501 placeholders to real responses.
package runtime

import (
	"errors"

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

// Runtime status vocabulary mirrors `runtime_records_status_chk` in
// `backend/internal/postgres/migrations/00001_init.sql`.
const (
	RuntimeStatusStarting = "starting"
	RuntimeStatusRunning = "running"
	RuntimeStatusGenerationInProgress = "generation_in_progress"
	RuntimeStatusGenerationFailed = "generation_failed"
	RuntimeStatusStopped = "stopped"
	RuntimeStatusEngineUnreachable = "engine_unreachable"
	RuntimeStatusFinished = "finished"
	RuntimeStatusRemoved = "removed"
)

// Operation log vocabulary recorded into `runtime_operation_log.op` and
// `runtime_operation_log.status`. Kept as exported constants so
// runtime, admin handlers, and tests share the same wire values.
const (
	OpStart = "start"
	OpStop = "stop"
	OpPause = "pause"
	OpResume = "resume"
	OpRestart = "restart"
	OpPatch = "patch"
	OpForceNextTurn = "force_next_turn"
	OpReconcile = "reconcile"
	OpTurn = "turn"

	OpSourceLobby = "lobby"
	OpSourceAdmin = "admin"
	OpSourceScheduler = "scheduler"
	OpSourceReconciler = "reconciler"

	OpStatusQueued = "queued"
	OpStatusRunning = "running"
	OpStatusSucceeded = "succeeded"
	OpStatusFailed = "failed"
)

// Container naming convention. The hostname is the primary alias on
// the user-defined Docker network; the engine endpoint URL is
// synthesised by `dockerclient.Adapter.Run` as `http://{hostname}:8080`.
const (
	containerNamePrefix = "galaxy-game-"
	containerHostPrefix = "galaxy-game-"
)

// pgErrCodeUniqueViolation is the SQLSTATE Postgres emits on a UNIQUE
// constraint violation. Kept locally so the runtime package does not
// import `internal/admin` or `internal/lobby` for the constant.
const pgErrCodeUniqueViolation = "23505"

// isUniqueViolation reports whether err is a Postgres UNIQUE
// constraint violation, optionally restricted to a specific constraint
// name. Empty constraintName matches any UNIQUE violation.
func isUniqueViolation(err error, constraintName string) bool {
	var pgErr *pgconn.PgError
	if !errors.As(err, &pgErr) {
		return false
	}
	if pgErr.Code != pgErrCodeUniqueViolation {
		return false
	}
	if constraintName == "" {
		return true
	}
	return pgErr.ConstraintName == constraintName
}

// ContainerName synthesises the Docker container / hostname for the
// supplied game id. Exported so tests and the reconciler can resolve
// the inverse mapping without duplicating the format string.
func ContainerName(gameID string) string { return containerNamePrefix + gameID }

// HostName synthesises the in-network hostname for the supplied game
// id. Mirrors ContainerName so the engine endpoint URL `http://{host}:8080`
// resolves through Docker DNS on the user-defined network.
func HostName(gameID string) string { return containerHostPrefix + gameID }
