package runtime

import (
	"context"
	"fmt"
	"strings"

	"git.cloudinit.dev/coreci/orca/internal/sshpush"
)

// WasmRuntime implements Runtime for the "wasm" one_of. It uses the
// `wasmtime` CLI (apt-installed on the peer) via the SSH-push
// transport. It does NOT use the Go wasmtime binding
// (github.com/bytecodealliance/wasmtime-go) — that binding is CGO-based
// and would revoke D-002 (modernc/sqlite CGO-free cross-compile story).
// See C01_WASMTIME_CGO_EVAL.md for the C-01 grill gate evaluation and
// the auto-decision D-187.
//
// Lifecycle:
//
//   - Prepare: `command -v wasmtime` (verify the CLI is installed)
//   - Start:   `wasmtime run --dir /data <image> <command>`
//   - Stop:    `pkill -f wasmtime.*<alloc-id>`
//   - Status:  `pgrep -f wasmtime.*<alloc-id>`
//
// The image is a .wasm file path on the peer. For v0.9 it is
// pre-staged (downloaded out-of-band); the full OCI pull lands in v0.10.
type WasmRuntime struct {
	transport *sshpush.Transport
}

// NewWasmRuntime returns a WasmRuntime backed by the given transport.
func NewWasmRuntime(t *sshpush.Transport) *WasmRuntime {
	return &WasmRuntime{transport: t}
}

// Prepare verifies wasmtime is installed on the peer.
func (w *WasmRuntime) Prepare(ctx context.Context, alloc *Alloc) error {
	if _, err := w.transport.Exec(ctx, alloc.Node, "command -v wasmtime"); err != nil {
		return fmt.Errorf("wasm: wasmtime not installed on peer: %w", err)
	}
	return nil
}

// Start runs `wasmtime run --dir /data <image> <command>` on the peer.
// The PID returned is a synthetic derived from the alloc ID hash.
func (w *WasmRuntime) Start(ctx context.Context, alloc *Alloc) (int, error) {
	image, err := imageFor(alloc)
	if err != nil {
		return 0, err
	}
	cmdStr, _ := commandFor(alloc)
	// Tag the process so pkill/pgrep can find it by alloc ID. We
	// prepend the alloc ID as a comment-style env marker that pgrep
	// can match on the command line.
	cmd := fmt.Sprintf("env 'ORCA_ALLOC_ID=%s' wasmtime run --dir /data %q %s",
		alloc.ID, image, shellQuote(cmdStr))
	if _, err := w.transport.Exec(ctx, alloc.Node, cmd); err != nil {
		return 0, fmt.Errorf("wasm: start: %w", err)
	}
	return allocIDHash(alloc.ID), nil
}

// Stop kills the wasmtime process matching the alloc ID.
func (w *WasmRuntime) Stop(ctx context.Context, alloc *Alloc) error {
	cmd := fmt.Sprintf("pkill -f %q", "wasmtime.*"+alloc.ID)
	if _, err := w.transport.Exec(ctx, alloc.Node, cmd); err != nil {
		return fmt.Errorf("wasm: stop: %w", err)
	}
	return nil
}

// Status reports whether the wasmtime process for the alloc is running.
func (w *WasmRuntime) Status(ctx context.Context, alloc *Alloc) (State, error) {
	cmd := fmt.Sprintf("pgrep -f %q", "wasmtime.*"+alloc.ID)
	out, err := w.transport.Exec(ctx, alloc.Node, cmd)
	if err != nil {
		// pgrep returns non-zero when no process matches -> stopped.
		return StateStopped, nil
	}
	if strings.TrimSpace(string(out)) == "" {
		return StateStopped, nil
	}
	return StateRunning, nil
}

// allocIDHash returns a stable positive int derived from the alloc ID
// (used as a synthetic PID for the interface contract).
func allocIDHash(id string) int {
	var h uint32
	for _, c := range id {
		h = h*31 + uint32(c)
	}
	pid := int(h % 99999)
	if pid <= 0 {
		pid = 1
	}
	return pid
}
