/**
 * Per-kind metadata, kept deliberately small and generic. The provider defers
 * all real validation to mcpd's own Zod schemas — this table only covers the
 * few things a client must know to round-trip correctly:
 *
 *   - singular kind -> plural REST resource (mirrors apply.ts KIND_TO_RESOURCE)
 *   - which fields mcpd cannot change in place (stripped from PUT bodies)
 *   - which server-managed/runtime fields to drop on read so state does not
 *     churn (mirrors get.ts toApplyDocs)
 *   - the Zod defaults mcpd applies, so `check` can normalize inputs and avoid
 *     spurious diffs
 *
 * Unknown kinds still work (resource falls back to the kind string) — new mcpd
 * resource kinds are usable the day they ship, without a provider release.
 */

/** singular kind -> plural resource key (mirror of apply.ts KIND_TO_RESOURCE). */
export const KIND_TO_RESOURCE: Record<string, string> = {
  server: 'servers',
  project: 'projects',
  secret: 'secrets',
  template: 'templates',
  user: 'users',
  group: 'groups',
  rbac: 'rbac',
  prompt: 'prompts',
  promptrequest: 'promptrequests',
  serverattachment: 'serverattachments',
  mcptoken: 'mcptokens',
  secretbackend: 'secretbackends',
  llm: 'llms',
  agent: 'agents',
};

/**
 * Fields mcpd treats as immutable on update (beyond `name`, which is always
 * immutable). Changing one forces a replacement. Mirrors the update bodies in
 * apply.ts (e.g. llms strip `type`).
 */
export const KIND_IMMUTABLE_FIELDS: Record<string, string[]> = {
  llm: ['type'],
};

/**
 * Server-managed/runtime fields dropped on read so desired-vs-live comparison
 * stays clean. Mirrors get.ts `toApplyDocs`. `ALWAYS_STRIPPED` applies to every
 * kind.
 */
export const ALWAYS_STRIPPED: readonly string[] = ['id', 'createdAt', 'updatedAt', 'version'];

export const KIND_STRIPPED_ON_READ: Record<string, string[]> = {
  llm: ['kind', 'status', 'lastHeartbeatAt', 'inactiveSince', 'providerSessionId'],
  agent: ['kind', 'status', 'lastHeartbeatAt', 'inactiveSince', 'providerSessionId'],
};

/** Zod defaults mcpd applies; mirrored so `check` normalization avoids diffs. */
export const KIND_SPEC_DEFAULTS: Record<string, Record<string, unknown>> = {
  llm: { tier: 'fast', description: '', extraConfig: {}, poolName: null },
};

export function resourceForKind(kind: string): string {
  return KIND_TO_RESOURCE[kind] ?? kind;
}

/** Immutable fields for a kind, including the always-immutable `name`. */
export function immutableFieldsForKind(kind: string): string[] {
  return ['name', ...(KIND_IMMUTABLE_FIELDS[kind] ?? [])];
}

/** Drop undefined-valued keys and apply the kind's defaults (spec wins). */
export function normalizeSpec(kind: string, spec: Record<string, unknown>): Record<string, unknown> {
  const defaults = KIND_SPEC_DEFAULTS[kind] ?? {};
  const out: Record<string, unknown> = { ...defaults };
  for (const [key, value] of Object.entries(spec)) {
    if (value !== undefined) out[key] = value;
  }
  return out;
}

/** Strip server-managed + kind-specific runtime fields from a live row. */
export function stripForRead(kind: string, entity: Record<string, unknown>): Record<string, unknown> {
  const drop = new Set<string>([...ALWAYS_STRIPPED, ...(KIND_STRIPPED_ON_READ[kind] ?? [])]);
  const out: Record<string, unknown> = {};
  for (const [key, value] of Object.entries(entity)) {
    if (!drop.has(key)) out[key] = value;
  }
  return out;
}
