import { NextResponse } from 'next/server';
import { ZodSchema } from 'zod';
import { ValidationError } from '@server/errors';
import { RouteContext, RouteHandler } from './withErrorHandler';
import { AuthedRequest } from './withAuth';
import { getSession } from '@server/auth';
import { AuthError } from '@server/errors';

export type AuthedParsedRequest<T> = AuthedRequest & { parsedBody: T };

type AuthedValidatedHandler<T> = (
  req: AuthedParsedRequest<T>,
  context: RouteContext
) => Promise<NextResponse | Response>;

export function withAuthValidation<T>(
  schema: ZodSchema<T>,
  handler: AuthedValidatedHandler<T>
): RouteHandler {
  return async (req: Request, context: RouteContext) => {
    const session = await getSession();
    if (!session) {
      const err = new AuthError();
      return NextResponse.json(err.toJSON(), { status: err.statusCode });
    }

    let rawBody: unknown;
    try {
      rawBody = await req.json();
    } catch {
      const err = new ValidationError('Request body must be valid JSON');
      return NextResponse.json(err.toJSON(), { 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);
      }
      const err = new ValidationError('Validation failed', fields);
      return NextResponse.json(err.toJSON(), { status: 400 });
    }

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

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

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