/**
 * Named constants for the visual-grounding subsystem.
 *
 * Single source of truth — every number, enum, default and timeout
 * the grounding layer cares about lives here. New magic numbers in
 * adapter / bench / DesktopTool code should add a constant here
 * instead of inlining the value.
 *
 * Grouped by concern so consumers can `import { ReasoningEffort }` etc.
 */

// ── Reasoning-effort levels supported by Kimi K2.6 on Azure AI Foundry ────────
// Empirically (see Phase-0 bench): `low` is the accuracy sweet spot
// (~3px error / ~80s) while `none` is the fastest (~2s) but noisy.
// `medium` and `high` cost more without measurable accuracy gain.
export const ReasoningEffort = Object.freeze({
  NONE:   'none',
  LOW:    'low',
  MEDIUM: 'medium',
  HIGH:   'high',
});

export const REASONING_EFFORT_VALUES = Object.freeze([
  ReasoningEffort.NONE,
  ReasoningEffort.LOW,
  ReasoningEffort.MEDIUM,
  ReasoningEffort.HIGH,
]);

/** Default for click-decision calls. See bench results: 3-7px mean error. */
export const DEFAULT_REASONING_EFFORT = ReasoningEffort.LOW;

// ── Sampling parameters (per Moonshot's published recommendations) ────────────
// https://huggingface.co/moonshotai/Kimi-K2.6 — model card "Recommended Parameters"
export const SamplingDefaults = Object.freeze({
  THINKING_ON:  Object.freeze({ temperature: 1.0,  top_p: 1.0  }),
  THINKING_OFF: Object.freeze({ temperature: 0.6,  top_p: 0.95 }),
});

// ── Token / latency budgets ───────────────────────────────────────────────────
// Kimi reasoning traces routinely consume 5-15k tokens before emitting an
// answer. Below 4k it frequently runs out mid-trace (see Phase-0 bench).
export const TokenBudget = Object.freeze({
  /** Min ceiling to avoid truncation when reasoning is on. */
  THINKING_MIN_MAX_TOKENS: 8_000,
  /** Safe default for grounding requests. */
  THINKING_DEFAULT_MAX_TOKENS: 16_000,
  /** Short responses fit comfortably below this when reasoning is off. */
  INSTANT_DEFAULT_MAX_TOKENS: 500,
});

export const Timeouts = Object.freeze({
  /** Full reasoning round-trip can take 80-120s; allow generous headroom. */
  GROUNDING_REQUEST_MS: 180_000,
  /** No-reasoning calls return fast; this is the impatient ceiling. */
  GROUNDING_REQUEST_FAST_MS: 30_000,
});

// ── Image-input parameters ────────────────────────────────────────────────────
export const ImageDetail = Object.freeze({
  LOW:  'low',
  HIGH: 'high',
  AUTO: 'auto',
});

export const DEFAULT_IMAGE_DETAIL = ImageDetail.HIGH;

// ── Azure AI Foundry endpoints ────────────────────────────────────────────────
export const Foundry = Object.freeze({
  /** API version that supports Kimi K2.6 chat + vision on Foundry. */
  API_VERSION: '2024-05-01-preview',
  /** Foundry's Azure AI inference endpoint path (NOT the OpenAI /chat path). */
  CHAT_COMPLETIONS_PATH: 'models/chat/completions',
});

// ── Model identifiers (registry keys) ────────────────────────────────────────
// CRITICAL: in proxy mode (the default in production), this string is
// sent to the backend's POST /llm/grounding which does a CASE-SENSITIVE
// lookup against the Loxia model catalog before forwarding to Foundry.
// The catalog is the source of truth; a mismatch returns
//   "Backend 404: API deployment for this resource does not exist"
// with the model name echoed back. The catalog currently registers the
// entry as "Kimi-K2.6" (Pascal case), so we match it verbatim.
// (Direct-mode bench runs accept the lowercase name because Foundry
//  itself has both casings as aliases — but we standardise on the
//  catalog spelling because proxy mode is the production path.)
export const ModelId = Object.freeze({
  KIMI_K2_6:           'Kimi-K2.6',
  CLAUDE_SONNET_4_6:   'claude-sonnet-4-6',
  CLAUDE_OPUS_4_6:     'claude-opus-4-6',
  OPENAI_COMPUTER_USE: 'computer-use-preview',
  MOCK:                'mock',
});

export const DEFAULT_GROUNDING_MODEL_ID = ModelId.KIMI_K2_6;

// ── Knob types for the admin-UI form generator ────────────────────────────────
export const KnobType = Object.freeze({
  ENUM:    'enum',
  NUMBER:  'number',
  BOOLEAN: 'boolean',
  STRING:  'string',
});

// ── Error codes (stable, machine-readable, used by callers + UI) ─────────────
export const ErrorCode = Object.freeze({
  /** Grounding model rejected the request shape. */
  INVALID_REQUEST:     'INVALID_REQUEST',
  /** Provider auth / API key missing or rejected. */
  AUTH_FAILED:         'AUTH_FAILED',
  /** Provider 5xx or network error after retries. */
  PROVIDER_ERROR:      'PROVIDER_ERROR',
  /** Model did not return a parseable coord or tool_call. */
  UNPARSEABLE_RESPONSE:'UNPARSEABLE_RESPONSE',
  /** Caller asked for a knob this adapter doesn't support. */
  UNSUPPORTED_KNOB:    'UNSUPPORTED_KNOB',
  /** Caller asked for a model not in the registry. */
  UNKNOWN_MODEL:       'UNKNOWN_MODEL',
  /** Adapter timed out before the provider responded. */
  TIMEOUT:             'TIMEOUT',
});
