import { NextResponse } from 'next/server';
import { ZodSchema } from 'zod';
import { v1ErrorBody } from './v1Errors';
import type { RouteContext, RouteHandler } from './withErrorHandler';
import type { ParsedRequest } from './withValidation';

/**
 * v1-aware request validator. Behaves like `withValidation` but emits the
 * public REST API error envelope (`{ error: { code, message, details? } }`)
 * for malformed JSON / failed schema instead of the dashboard-internal
 * `{ error, code, fields }` shape that `ValidationError.toJSON()` produces.
 *
 * We can't just throw `ValidationError` and let `withV1ErrorHandler` map it,
 * because the request body is parsed inside this middleware before the route
 * handler — the throw would still be caught at the v1 boundary, but having a
 * dedicated wrapper keeps the parse-failure path local and avoids depending
 * on a particular middleware stacking order.
 */
export function withV1Validation<T>(
  schema: ZodSchema<T>,
  handler: (req: ParsedRequest<T>, context: RouteContext) => Promise<NextResponse | Response>
): RouteHandler {
  return async (req: Request, context: RouteContext): Promise<NextResponse | Response> => {
    let rawBody: unknown;
    try {
      rawBody = await req.json();
    } catch {
      return NextResponse.json(
        v1ErrorBody('Request body must be valid JSON', 'VALIDATION_ERROR'),
        { status: 400 }
      );
    }

    const parsed = schema.safeParse(rawBody);
    if (!parsed.success) {
      const fields: Record<string, string[]> = {};
      for (const issue of parsed.error.issues) {
        const key = issue.path.join('.') || 'root';
        if (!fields[key]) fields[key] = [];
        fields[key].push(issue.message);
      }
      return NextResponse.json(
        v1ErrorBody('Validation failed', 'VALIDATION_ERROR', { fields }),
        { status: 400 }
      );
    }

    Object.defineProperty(req, 'parsedBody', {
      value: parsed.data,
      writable: false,
      enumerable: true,
      configurable: true,
    });

    return handler(req as ParsedRequest<T>, context);
  };
}
