/**
 * Zod-parse a request part (body / query / route params) and turn a failure
 * into an AppError(400) with a per-field message map, so errorHandler stays the
 * only place a response body is written (PLANNING.md §5).
 */
import type { ZodTypeAny, z } from 'zod';
import { AppError } from './AppError';

function parse<T extends ZodTypeAny>(schema: T, value: unknown, label: string): z.infer<T> {
  const result = schema.safeParse(value);
  if (result.success) return result.data;

  const fields: Record<string, string> = {};
  for (const issue of result.error.issues) {
    const key = typeof issue.path[0] === 'string' ? issue.path[0] : '_';
    if (!fields[key]) fields[key] = issue.message;
  }
  throw new AppError(400, `Invalid ${label}`, { fields });
}

export const parseBody = <T extends ZodTypeAny>(schema: T, body: unknown): z.infer<T> =>
  parse(schema, body, 'request');

export const parseQuery = <T extends ZodTypeAny>(schema: T, query: unknown): z.infer<T> =>
  parse(schema, query, 'query');
