/*
  Content schemas (DATA_MODEL.md), validated at build via zod (bundled with Astro).
  Model: invariant facts live once at the top level; locale-varying prose lives under
  `content.{en,no}` or as `{en,no}` pairs — so facts exist once, prose exists twice
  (DATA_MODEL, I18N_SPEC §3). Any violation fails the build.
*/

import { z } from 'astro/zod';

/** A localised string pair. */
export const loc = z.object({ en: z.string(), no: z.string() });
/** A localised string-array pair. */
export const locArr = z.object({ en: z.array(z.string()), no: z.array(z.string()) });

/** Prose block — the only content primitives (no markdown runtime needed). */
export const block = z.discriminatedUnion('type', [
  z.object({ type: z.literal('p'), text: z.string() }),
  z.object({ type: z.literal('ul'), items: z.array(z.string()) }),
]);
export type Block = z.infer<typeof block>;

export const link = z.object({
  type: z.enum(['repo', 'live', 'docs']),
  url: z.string().url(),
  public: z.boolean().default(true),
});

export const stackChip = z.object({
  name: z.string(),
  context: loc.optional(),
});

export const media = z.object({
  src: z.string(),
  width: z.number().int().positive(),
  height: z.number().int().positive(),
  alt: loc,
  caption: loc,
});

/* ---- Architecture diagram (data-driven; localised labels) ---- */
export const diagramNode = z.object({
  id: z.string(),
  x: z.number(),
  y: z.number(),
  w: z.number(),
  h: z.number(),
  kind: z.enum(['internal', 'primary', 'external']),
  label: z.string(),
  sub: loc.optional(),
});
export const diagramEdge = z.object({
  d: z.string(), // SVG path
  kind: z.enum(['flow', 'external']),
  label: z.string().optional(),
  labelX: z.number().optional(),
  labelY: z.number().optional(),
});
export const diagram = z.object({
  viewBox: z.string(),
  title: loc,
  desc: loc,
  nodes: z.array(diagramNode),
  edges: z.array(diagramEdge),
});

/* ---- Case-study sections ---- */
export const decision = z.object({
  n: z.number().int().positive(),
  choice: z.string(),
  alternative: z.string(),
  rationale: z.string(),
});

export const SECTION_KINDS = [
  'problem',
  'architecture',
  'decisions',
  'security',
  'screenshots',
  'next',
] as const;

export const section = z.object({
  kind: z.enum(SECTION_KINDS),
  heading: z.string(),
  anchorId: z.string(),
  blocks: z.array(block).optional(),
  decisions: z.array(decision).optional(),
});

export const projectLocaleContent = z.object({
  valueProp: z.string(),
  cardTeaser: z.string(),
  tldr: z.object({ what: z.string(), why: z.string(), stack: z.string(), role: z.string() }),
  sections: z.array(section),
});

export const project = z.object({
  id: z.enum(['jobtrack', 'inboxintel', 'homelab']),
  name: z.string(),
  order: z.number().int(),
  status: z.enum(['active', 'in-development', 'archived']),
  template: z.enum(['case-study', 'capability']),
  stack: z.array(stackChip),
  links: z.array(link).default([]),
  diagram: diagram.optional(),
  media: z.array(media).default([]),
  content: z.object({ en: projectLocaleContent, no: projectLocaleContent }),
});
export type Project = z.infer<typeof project>;

/* ---- Skills ---- */
export const skillGroup = z.object({
  id: z.enum(['development', 'devops-infrastructure', 'practices']),
  title: loc,
  context: loc,
  skills: z.array(z.object({ name: z.string(), tooltip: loc.optional() })),
});
export const skills = z.object({ groups: z.array(skillGroup) });
export type Skills = z.infer<typeof skills>;

/* ---- Experience ---- */
export const experienceItem = z.object({
  id: z.string(),
  employer: z.string(),
  location: z.string(),
  period: z.object({ from: z.number().int(), to: z.number().int().nullable() }),
  emphasis: z.enum(['featured', 'compact']),
  alongside: z.boolean().default(false),
  progression: z
    .array(z.object({ from: z.number().int(), to: z.number().int(), label: loc }))
    .optional(),
  role: loc,
  summary: loc.optional(),
  highlights: locArr.optional(),
});
export const experience = z.object({ items: z.array(experienceItem) });
export type Experience = z.infer<typeof experience>;

/* ---- Profile (singleton) ---- */
export const profile = z.object({
  name: z.string(),
  links: z.object({
    email: z.string().email(),
    phone: z.string(),
    phoneHref: z.string(),
    linkedin: z.string(),
    gitea: z.string(),
  }),
  cv: z.object({
    en: z.object({ path: z.string(), sizeKb: z.number(), updated: z.string() }),
    no: z.object({ path: z.string(), sizeKb: z.number(), updated: z.string() }),
  }),
  photo: z.object({ src: z.string(), alt: loc, caption: loc }),
  languages: z.array(z.object({ label: loc, level: loc })),
  chips: z.array(z.object({ id: z.string(), label: loc })),
  proof: z.array(z.object({ stat: loc, label: loc })),
  content: z.object({
    en: z.object({
      eyebrow: z.string(),
      roleLine: z.string(),
      heroTagline: z.string(),
      heroSummary: z.string(),
      statusLine: z.string(),
      aboutHeading: z.string(),
      aboutParagraphs: z.array(z.string()),
      interestsLine: z.string(),
      contactHeading: z.string(),
      contactBody: z.string(),
    }),
    no: z.object({
      eyebrow: z.string(),
      roleLine: z.string(),
      heroTagline: z.string(),
      heroSummary: z.string(),
      statusLine: z.string(),
      aboutHeading: z.string(),
      aboutParagraphs: z.array(z.string()),
      interestsLine: z.string(),
      contactHeading: z.string(),
      contactBody: z.string(),
    }),
  }),
});
export type Profile = z.infer<typeof profile>;

/* ---- Per-page SEO meta ---- */
export const pageMeta = z.object({
  en: z.object({ title: z.string().max(70), description: z.string().max(160) }),
  no: z.object({ title: z.string().max(70), description: z.string().max(160) }),
});
export const meta = z.record(z.string(), pageMeta);
export type Meta = z.infer<typeof meta>;
