/**
 * Agent reflection (v1) — wire schema.
 *
 * Runtime source of truth for the `reflection.v1` sidecar emitted at end-of-run
 * by the Stop hook (design §10 step 1). The JSON Schema artifact at
 * `@mosaicstack/macp` `src/schemas/reflection.v1.schema.json` is the documented
 * contract; this zod schema is the executable one and MUST agree with it.
 *
 * Field provenance:
 *   - MECHANICAL  (risk, files_changed, ids, provenance): written by the hook.
 *   - SELF-REPORTED (confidence, most_likely_wrong, known_not_in_diff): merged
 *     from an optional agent-supplied input; null when absent.
 *
 * Pure — no NestJS, no DB, no Node-only APIs. Safe for browser/edge.
 */

import { z } from 'zod';

/** Review surfaces, ordered most- to least-sensitive. Mirrors macp risk-floor. */
export const REVIEW_SURFACES = [
  'auth',
  'data',
  'infra',
  'build',
  'ui',
  'test',
  'docs',
  'none',
] as const;

export const ReviewSurfaceSchema = z.enum(REVIEW_SURFACES);
export type ReviewSurface = z.infer<typeof ReviewSurfaceSchema>;

/** SELF-REPORTED: the single most-likely way the work is wrong. */
export const MostLikelyWrongSchema = z.object({
  surface: ReviewSurfaceSchema,
  description: z.string(),
});
export type MostLikelyWrong = z.infer<typeof MostLikelyWrongSchema>;

/** MECHANICAL: output of the diff risk-floor (see `@mosaicstack/macp`). */
export const ReflectionRiskSchema = z.object({
  needs_review: z.boolean(),
  score: z.number().min(0).max(1),
  surface: ReviewSurfaceSchema,
  reason: z.string(),
});
export type ReflectionRisk = z.infer<typeof ReflectionRiskSchema>;

export const ReflectionModeSchema = z.enum(['off', 'solo', 'orchestrated']);
export type ReflectionMode = z.infer<typeof ReflectionModeSchema>;

export const ReflectionProvenanceSchema = z.object({
  source: z.literal('stop-hook'),
  reflection_attempt: z.number().int().min(1),
  degraded: z.boolean(),
  reflection_mode: ReflectionModeSchema,
});
export type ReflectionProvenance = z.infer<typeof ReflectionProvenanceSchema>;

/**
 * The self-reported half of a reflection. Supplied by the agent out-of-band
 * (e.g. `<repo>/.mosaic/reflection-input.json`) and merged by the hook. All
 * fields optional; missing fields become `null` in the assembled record.
 */
export const ReflectionSelfReportSchema = z.object({
  confidence: z.number().min(0).max(1).nullable().optional(),
  most_likely_wrong: MostLikelyWrongSchema.nullable().optional(),
  known_not_in_diff: z.string().nullable().optional(),
});
export type ReflectionSelfReport = z.infer<typeof ReflectionSelfReportSchema>;

/** The full assembled `reflection.v1` sidecar. */
export const ReflectionV1Schema = z.object({
  schema: z.literal('reflection.v1'),
  task_ref: z.string(),
  agent: z.string(),
  session_id: z.string(),
  timestamp: z.string(),
  repo: z.string(),
  confidence: z.number().min(0).max(1).nullable(),
  most_likely_wrong: MostLikelyWrongSchema.nullable(),
  known_not_in_diff: z.string().nullable(),
  risk: ReflectionRiskSchema,
  files_changed: z.array(z.string()),
  provenance: ReflectionProvenanceSchema,
});
export type ReflectionV1 = z.infer<typeof ReflectionV1Schema>;

export const REFLECTION_SCHEMA_ID = 'reflection.v1' as const;
