import { NextRequest, NextResponse } from 'next/server';
import { z, ZodSchema } from 'zod';

export async function parseBody<T>(req: NextRequest, schema: ZodSchema<T>): Promise<
  | { success: true; data: T; response?: undefined }
  | { success: false; data?: undefined; response: NextResponse }
> {
  let raw: unknown;
  try {
    raw = await req.json();
  } catch {
    return {
      success: false,
      response: NextResponse.json(
        { error: 'Ungueltiger JSON-Body' },
        { status: 400 }
      ),
    };
  }

  const parsed = schema.safeParse(raw);
  if (!parsed.success) {
    return {
      success: false,
      response: NextResponse.json(
        { error: 'Ungueltige Eingabe', issues: parsed.error.flatten() },
        { status: 400 }
      ),
    };
  }

  return { success: true, data: parsed.data };
}

const dietFilterEnum = z.enum(['vegan', 'vegetarian', 'halal', 'gluten_free']);

export const createSessionSchema = z.object({
  city: z.string().min(1).trim(),
  postalCode: z.string().trim().optional(),
  lat: z.number().optional(),
  lon: z.number().optional(),
  radiusKm: z.coerce.number().int().min(1).max(15).default(3),
  dietFilter: dietFilterEnum.optional().nullable(),
  cuisineTags: z.array(z.string()).default([]),
  priceMin: z.coerce.number().int().min(1).max(4).default(1),
  priceMax: z.coerce.number().int().min(1).max(4).default(4),
  excludeFastFood: z.boolean().default(false),
  excludeChains: z.boolean().default(false),
  openNowOnly: z.boolean().default(false),
  displayName: z.string().trim().optional(),
});

export const joinSessionSchema = z.object({
  displayName: z.string().trim().optional(),
  anonymousId: z.string().uuid().optional(),
});

export const swipeSchema = z.object({
  participantId: z.coerce.number().int().positive(),
  restaurantId: z.union([z.coerce.number().int().positive(), z.string().min(1)]),
  direction: z.enum(['yes', 'no', 'veto']),
});

export const participantActionSchema = z.object({
  participantId: z.coerce.number().int().positive(),
});

export const kickSchema = z.object({
  requesterId: z.coerce.number().int().positive(),
  targetParticipantId: z.coerce.number().int().positive(),
});

export const jokerSchema = z.object({
  participantId: z.coerce.number().int().positive(),
});

export const trackClickSchema = z.object({
  sessionCode: z.string().min(1),
  restaurantId: z.union([z.coerce.number().int().positive(), z.string().min(1)]).optional(),
  clickType: z.enum(['maps', 'call', 'reservation', 'order', 'website']),
});

export const loginSchema = z.object({
  username: z.string().min(1),
  password: z.string().min(1),
  totpCode: z.string().length(6).optional(),
});

const sponsorBaseSchema = z.object({
  name: z.string().min(1).trim(),
  // Uploads werden als relative Pfade (/api/uploads/sponsors/...) gespeichert,
  // daher muessen absolute URLs UND relative Upload-Pfade erlaubt sein.
  imageUrl: z.union([z.string().url(), z.string().regex(/^\/(api\/)?uploads\//)]).optional().nullable(),
  address: z.string().trim().optional().nullable(),
  city: z.string().min(1).trim(),
  postalCode: z.string().trim().optional().nullable(),
  lat: z.coerce.number().optional().nullable(),
  lon: z.coerce.number().optional().nullable(),
  filterCuisineTags: z.array(z.string()).optional().nullable(),
  discountText: z.string().trim().optional().nullable(),
  priority: z.coerce.number().int().optional().nullable(),
  active: z.boolean().optional().nullable(),
  activeFrom: z.string().optional().nullable(),
  activeUntil: z.string().optional().nullable(),
  disclaimerText: z.string().trim().optional().nullable(),
  openingHoursRaw: z.string().trim().optional().nullable(),
  reservationUrl: z.string().url().optional().nullable(),
  lieferandoUrl: z.string().url().optional().nullable(),
  restaurantId: z.coerce.number().int().positive().optional().nullable(),
});

export const sponsorCreateSchema = sponsorBaseSchema;
export const sponsorUpdateSchema = sponsorBaseSchema.partial();

export const clientErrorSchema = z.object({
  message: z.string().max(2000).optional(),
  context: z.unknown().optional(),
  url: z.string().max(500).optional(),
  sessionCode: z.string().optional(),
});

export const estimateSchema = z.object({
  city: z.string().min(1).trim(),
  postalCode: z.string().trim().optional(),
  radiusKm: z.coerce.number().int().min(1).max(15).default(3),
  cuisineTags: z.array(z.string()).default([]),
  excludeFastFood: z.boolean().default(false),
  openNowOnly: z.boolean().default(false),
});
