/**
 * VERBATIM COPY of the consumer's parser.
 *
 * These schemas are copied character-for-character from the frontend's
 * `genesiscoworkingspace-landing-page/src/api/schemas.ts`. The frontend parses
 * every response through them at its network boundary; a mismatch means the
 * landing page silently drops to its baked snapshot (docs/API-CONTRACT.md
 * "Failure behavior"). PLANNING.md §5 / §12: `gallery.service.ts` parses its own
 * output back through `spaceGallerySchema` here in dev/test so drift fails loudly
 * on our side first. A CI regression test that diffs this file against the
 * frontend's is step 8.
 *
 * Do not "improve" these. If the frontend's schema changes, change this to match.
 */
import { z } from 'zod';

// ---- CMS: space gallery ----

// A real CMS response always sends a fully-qualified URL, but the baked
// snapshot fallback also needs to hold locally-hosted photos served from
// public/ — a root-relative path (`/gallery/...`). Accept both; reject
// anything else.
const imageUrlSchema = z
  .string()
  .refine((value) => /^https?:\/\//.test(value) || value.startsWith('/'), {
    message: 'must be an absolute URL or a root-relative path',
  });

export const imageVariantSchema = z.object({
  url: imageUrlSchema,
  width: z.number().int().positive(),
});

export const galleryImageSchema = z.object({
  id: z.string(),
  alt: z.string().default(''),
  order: z.number().int(),
  width: z.number().int().positive(),
  height: z.number().int().positive(),
  src: imageUrlSchema,
  variants: z
    .object({
      avif: z.array(imageVariantSchema).default([]),
      webp: z.array(imageVariantSchema).default([]),
    })
    .default({ avif: [], webp: [] }),
  caption: z.string().optional(),
});

export const spaceGallerySchema = z.object({
  slug: z.enum(['genesis', 'hive']),
  updatedAt: z.string().datetime(),
  images: z.array(galleryImageSchema).min(1),
});

export type SpaceGallery = z.infer<typeof spaceGallerySchema>;
export type GalleryImage = z.infer<typeof galleryImageSchema>;

// ---- Enquiry submission ----

export const enquiryInputSchema = z.object({
  name: z.string().trim().min(2).max(100),
  phone: z
    .string()
    .trim()
    .min(7)
    .max(20)
    .regex(/^[+()\d\s-]+$/),
  email: z.string().trim().email().max(254),
  message: z.string().trim().min(10).max(2000),
  source: z.enum(['contact', 'genesis', 'hive', 'home']).default('contact'),
  // honeypot — intentionally unconstrained; a filled value is spam, handled in
  // application logic, not rejected at the schema boundary.
  website: z.string().optional(),
  token: z.string().optional(),
});

export type EnquiryInput = z.infer<typeof enquiryInputSchema>;

export const enquiryResponseSchema = z.discriminatedUnion('ok', [
  z.object({ ok: z.literal(true), id: z.string() }),
  z.object({ ok: z.literal(false), error: z.string(), fields: z.record(z.string()).optional() }),
]);

export type EnquiryResponse = z.infer<typeof enquiryResponseSchema>;
