import { z, type ZodType } from 'zod';
// Zod's optional JIT probes Function(), which strict CSP blocks. Its interpreted
// validator provides the same validation rules without an eval capability.
z.config({ jitless: true });
export { z };
export interface Issue { path: string; message: string }
export type ValidationResult<T> = { ok: true; value: T; warnings: Issue[] } | { ok: false; errors: Issue[]; warnings: Issue[] };
export class InputError extends Error {
  readonly issues: Issue[];
  constructor(message: string, issues: Issue[] = []) { super(message); this.name = 'InputError'; this.issues = issues; }
}
export function guardInput(input: unknown, maxBytes = 1_500_000): void {
  const seen = new Set<object>();
  function walk(value: unknown, depth: number) {
    if (depth > 12) throw new InputError('This file has too many nested fields.');
    if (typeof value === 'string' && value.length > 100_000) throw new InputError('A text field is too long.');
    if (typeof value === 'number' && !Number.isFinite(value)) throw new InputError('Numbers must be finite.');
    if (!value || typeof value !== 'object') return;
    if (seen.has(value)) throw new InputError('Circular data is not supported.');
    seen.add(value);
    if (Array.isArray(value) && value.length > 1000) throw new InputError('This file contains too many items.');
    if (!Array.isArray(value) && ![Object.prototype, null].includes(Object.getPrototypeOf(value))) throw new InputError('Only plain data objects are supported.');
    for (const [key, child] of Object.entries(value)) {
      if (['__proto__', 'prototype', 'constructor'].includes(key)) throw new InputError('This file contains an unsafe field.');
      walk(child, depth + 1);
    }
    seen.delete(value);
  }
  walk(input, 0);
  if (new TextEncoder().encode(JSON.stringify(input)).byteLength > maxBytes) throw new InputError('This file is too large.');
}
export function parseJson(text: string, maxBytes = 1_500_000): unknown {
  if (new TextEncoder().encode(text).byteLength > maxBytes) throw new InputError('This file is too large.');
  let value: unknown;
  try { value = JSON.parse(text); } catch { throw new InputError('This is not a valid JSON file.'); }
  guardInput(value, maxBytes); return value;
}
export function validateWith<T>(schema: ZodType<T>, input: unknown): ValidationResult<T> {
  try { guardInput(input); } catch (error) { return { ok: false, errors: [{ path: '', message: friendlyError(error) }], warnings: [] }; }
  const result = schema.safeParse(input);
  return result.success ? { ok: true, value: result.data, warnings: [] } : { ok: false, errors: result.error.issues.map(issue => ({ path: issue.path.map(String).join('.'), message: issue.message })), warnings: [] };
}
export function friendlyError(error: unknown): string {
  return error instanceof InputError ? error.message : 'That action could not be completed. Check your inputs and try again.';
}
