import { NextResponse } from 'next/server';
import { z } from 'zod';
import { withErrorHandler, RouteContext } from '@server/middleware/withErrorHandler';
import { withRateLimit, getClientIp, checkRateLimit } from '@server/middleware/withRateLimit';
import { ValidationError } from '@server/errors';
import { createStorefrontOrder } from '@server/services/storefront.service';

const cartItemSchema = z.object({
  menu_item_id: z.string().uuid(),
  quantity: z.number().int().min(1).max(99),
  selected_modifier_option_ids: z.array(z.string().uuid()).optional(),
  note: z.string().max(140).optional(),
});

const addressSchema = z.object({
  line1: z.string().min(2).max(120),
  line2: z.string().max(120).optional(),
  city: z.string().min(1).max(60),
  postal_code: z.string().max(20).optional(),
  notes: z.string().max(160).optional(),
}).optional().nullable();

const E164_PHONE = /^\+[1-9]\d{6,14}$/;

const orderInputSchema = z.object({
  customer_name: z.string().min(1).max(80),
  customer_phone: z.string()
    .min(8).max(20)
    .transform((s) => s.replace(/[\s\-().]/g, ''))
    .refine((s) => E164_PHONE.test(s), {
      message: 'Phone must be in E.164 format (e.g. +14155551234)',
    }),
  customer_email: z.string().email().optional(),
  delivery_type: z.enum(['dine-in', 'takeaway', 'delivery']),
  table_id: z.string().uuid().optional().nullable(),
  table_number: z.string().max(32).optional().nullable(),
  delivery_address_json: addressSchema,
  special_instructions: z.string().max(280).optional().nullable(),
  points_to_redeem: z.number().int().min(0).optional(),
  items: z.array(cartItemSchema).min(1).max(50),
  gift_card_code: z.string().trim().min(4).max(40).optional().nullable(),
  coupon_code: z.string().trim().max(40).optional().nullable(),
});

function rateLimitResponse(scope: string, retryAfterSec: number | undefined) {
  return NextResponse.json(
    {
      error: 'Too many requests. Please slow down and try again later.',
      code: 'RATE_LIMITED',
      scope,
      retryAfter: retryAfterSec,
    },
    {
      status: 429,
      headers: { 'Retry-After': String(retryAfterSec ?? 60) },
    }
  );
}

export const POST = withRateLimit(
  [
    // IP bucket: defense-in-depth (user-injectable header, but raises the bar).
    {
      scope: 'storefront:order:ip',
      limit: 10,
      windowMs: 10 * 60 * 1000,
      keyOf: (req) => getClientIp(req),
    },
  ],
  withErrorHandler(async (req: Request, ctx: RouteContext) => {
    const { restaurantSlug, branchSlug } = await ctx.params;

    // Per-branch bucket: keyed from URL route params (server-controlled, not
    // attacker-controlled). An attacker targeting a specific restaurant cannot
    // rotate this key. Limits total order throughput per branch regardless of
    // how many IPs or phone numbers the attacker rotates.
    const branchKey = `${restaurantSlug}:${branchSlug}`;
    const branchLimit = checkRateLimit('storefront:order:branch', branchKey, 60, 10 * 60 * 1000);
    if (!branchLimit.allowed) return rateLimitResponse('storefront:order:branch', branchLimit.retryAfterSec);

    const body = await req.json().catch(() => null);
    if (!body) throw new ValidationError('Invalid request body');
    const parsed = orderInputSchema.safeParse(body);
    if (!parsed.success) {
      throw new ValidationError('Invalid order payload', parsed.error.flatten().fieldErrors as Record<string, string[]>);
    }

    // Per-phone bucket: limits how many orders a single phone number can place.
    const phoneLimit = checkRateLimit('storefront:order:phone', parsed.data.customer_phone, 5, 60 * 60 * 1000);
    if (!phoneLimit.allowed) return rateLimitResponse('storefront:order:phone', phoneLimit.retryAfterSec);

    const result = await createStorefrontOrder(restaurantSlug, branchSlug, parsed.data);
    return NextResponse.json({ order: result }, { status: 201 });
  })
);
