import { z } from "zod";

// Shared between apps/api (server-side validation, source of truth) and
// apps/admin-web (client-side validation for instant feedback). Same
// rationale as schemas/auth.ts — one set of rules, French messages surfaced
// as-is in the admin login form.

/**
 * Payload accepted by `POST /admin/auth/login`. Deliberately its own schema
 * (not a re-export of `loginSchema`): the admin surface is a separate
 * contract from the end-user one even though the shape currently matches.
 */
export const adminLoginSchema = z.object({
  email: z.string().trim().toLowerCase().email("Email invalide"),
  password: z.string().min(1, "Le mot de passe est requis"),
});
/** Inferred TS type for {@link adminLoginSchema}'s validated output. */
export type AdminLoginInput = z.infer<typeof adminLoginSchema>;

/**
 * Query params for `GET /admin/metrics` — `?days=` bounds how far back the
 * time-series go (and how many daily buckets they carry). Coerced from the
 * query string; clamped to a sane window so a huge value can't make the
 * dashboard scan the whole history.
 */
export const getMetricsSchema = z.object({
  days: z.coerce.number().int().min(7).max(365).default(30),
});
/** Inferred TS type for {@link getMetricsSchema}'s validated output. */
export type GetMetricsInput = z.infer<typeof getMetricsSchema>;

/**
 * Payload of `POST /internal/tech-steps/heartbeat` — `services/tech-step-llm-worker`
 * (which has no inbound HTTP of its own) reporting that it's alive. Sent on
 * `boot`, on every scheduler `tick`, and after each `job` (with that job's
 * name, outcome and counts). The worker key is fixed server-side (only one
 * worker exists), so it isn't in the payload.
 */
export const workerHeartbeatSchema = z.object({
  event: z.enum(["boot", "tick", "job"]),
  /** The job that just ran — present only when `event === "job"`. */
  job: z.string().max(100).optional(),
  /** Whether that job succeeded — present only when `event === "job"`. */
  ok: z.boolean().optional(),
  /** Small `{ label: number }` summary of that job (e.g. `{ suggestions: 3 }`). */
  counts: z.record(z.string(), z.number()).optional(),
});
/** Inferred TS type for {@link workerHeartbeatSchema}'s validated output. */
export type WorkerHeartbeatInput = z.infer<typeof workerHeartbeatSchema>;

/** Statuses a `TechStepTrainingSuggestion` can be filtered by / set to. */
export const TRAINING_SUGGESTION_STATUSES = ["pending", "applied", "rejected"] as const;
/** One of {@link TRAINING_SUGGESTION_STATUSES}. */
export type TrainingSuggestionStatus = (typeof TRAINING_SUGGESTION_STATUSES)[number];

/** Query params for `GET /admin/tech-steps/suggestions` — every filter optional. */
export const listSuggestionsQuerySchema = z.object({
  status: z.enum(TRAINING_SUGGESTION_STATUSES).optional(),
  sourceType: z.enum(["correction", "llm_audit"]).optional(),
  techStepKey: z.string().min(1).optional(),
  locale: z.string().min(1).optional(),
});
/** Inferred TS type for {@link listSuggestionsQuerySchema}. */
export type ListSuggestionsQuery = z.infer<typeof listSuggestionsQuerySchema>;

/** Query params for `GET /admin/tech-steps/corrections`. `consumed`/`hasCorrectedTechStep` are tri-state (omitted = no filter). */
export const listCorrectionsQuerySchema = z.object({
  consumed: z.enum(["true", "false"]).optional(),
  hasCorrectedTechStep: z.enum(["true", "false"]).optional(),
});
/** Inferred TS type for {@link listCorrectionsQuerySchema}. */
export type ListCorrectionsQuery = z.infer<typeof listCorrectionsQuerySchema>;

/**
 * Body of `PATCH /admin/tech-steps/suggestions/:id` — curate a suggestion
 * before it feeds a `training_data.py` edit. Every field optional; at least
 * one must be present (enforced service-side).
 */
export const updateTrainingSuggestionSchema = z.object({
  status: z.enum(TRAINING_SUGGESTION_STATUSES).optional(),
  suggestedSynonyms: z.array(z.string().min(1)).max(200).optional(),
  suggestedUtterances: z.array(z.string().min(1)).max(200).optional(),
});
/** Inferred TS type for {@link updateTrainingSuggestionSchema}. */
export type UpdateTrainingSuggestionInput = z.infer<typeof updateTrainingSuggestionSchema>;

/**
 * Body of `POST /admin/tech-steps/retrain` — runs the F1 regression gate
 * then, if it passes, backfills every step and marks the given suggestion
 * ids. Both id lists optional (an empty run just re-gates + backfills).
 */
export const retrainRequestSchema = z.object({
  appliedIds: z.array(z.number().int().positive()).max(500).optional(),
  rejectedIds: z.array(z.number().int().positive()).max(500).optional(),
});
/** Inferred TS type for {@link retrainRequestSchema}. */
export type RetrainRequestInput = z.infer<typeof retrainRequestSchema>;

/** Query params for `GET /admin/tech-steps/training-data-snippet`. */
export const trainingDataSnippetQuerySchema = z.object({
  techStepKey: z.string().min(1),
  locale: z.string().min(1).default("fr"),
  status: z.enum(TRAINING_SUGGESTION_STATUSES).default("applied"),
});
/** Inferred TS type for {@link trainingDataSnippetQuerySchema}. */
export type TrainingDataSnippetQuery = z.infer<typeof trainingDataSnippetQuerySchema>;

/**
 * Query params for `GET /admin/catalog/placeholders` — the off-catalog
 * ingredient review. `reviewed` is tri-state: omitted / `"false"` hides
 * groups every row of which has already been triaged (the default working
 * view), `"true"` shows only fully-triaged groups.
 */
export const listPlaceholdersQuerySchema = z.object({
  reviewed: z.enum(["true", "false"]).optional(),
});
/** Inferred TS type for {@link listPlaceholdersQuerySchema}. */
export type ListPlaceholdersQuery = z.infer<typeof listPlaceholdersQuerySchema>;

/**
 * Body of `PATCH /admin/catalog/placeholders/mark-reviewed` — stamps
 * `reviewedAt` on the given placeholder `Ingredient` ids (a maintainer has
 * seen this catalog gap and, if warranted, added the real entry by hand).
 * Bounded so one call can't sweep an unbounded set.
 */
export const markPlaceholdersReviewedSchema = z.object({
  ingredientIds: z.array(z.number().int().positive()).min(1).max(500),
});
/** Inferred TS type for {@link markPlaceholdersReviewedSchema}. */
export type MarkPlaceholdersReviewedInput = z.infer<typeof markPlaceholdersReviewedSchema>;
