import { z } from 'zod';
import { int, num, systemIdSchema, hpSchema, conditionSchema } from './common';

// ---------------- Notes / Wiki ----------------
export const noteSchema = z.object({
  id: z.string(),
  campaignId: z.string(),
  title: z.string().min(1).max(200),
  body: z.string().max(100000).default(''),
  tags: z.array(z.string().min(1)).default([]),
  createdAt: z.string(),
  updatedAt: z.string(),
});
export type Note = z.infer<typeof noteSchema>;

// ---------------- NPCs ----------------
export const npcSchema = z.object({
  id: z.string(),
  campaignId: z.string(),
  name: z.string().min(1).max(120),
  role: z.string().max(120).default(''),
  location: z.string().max(120).default(''),
  faction: z.string().max(120).default(''),
  status: z.enum(['alive', 'dead', 'unknown']).default('alive'),
  description: z.string().max(20000).default(''),
  createdAt: z.string(),
  updatedAt: z.string(),
});
export type Npc = z.infer<typeof npcSchema>;

// ---------------- Quests ----------------
export const objectiveSchema = z.object({
  id: z.string(),
  text: z.string().min(1).max(500),
  done: z.boolean().default(false),
});
export type Objective = z.infer<typeof objectiveSchema>;

export const questSchema = z.object({
  id: z.string(),
  campaignId: z.string(),
  title: z.string().min(1).max(200),
  status: z.enum(['active', 'completed', 'failed', 'on-hold']).default('active'),
  description: z.string().max(20000).default(''),
  reward: z.string().max(500).default(''),
  objectives: z.array(objectiveSchema).default([]),
  createdAt: z.string(),
  updatedAt: z.string(),
});
export type Quest = z.infer<typeof questSchema>;

// ---------------- Calendar (in-world) ----------------
// Stored as a plain integer "day number" — no real Date, so no timezone/DST bugs.
export const calendarEventSchema = z.object({
  id: z.string(),
  day: int,
  title: z.string().min(1).max(200),
});
export type CalendarEvent = z.infer<typeof calendarEventSchema>;

export const calendarSchema = z.object({
  /** one calendar per campaign — campaignId is the primary key */
  campaignId: z.string(),
  /** current in-world day number (arbitrary epoch) */
  currentDay: int.default(0),
  events: z.array(calendarEventSchema).default([]),
});
export type Calendar = z.infer<typeof calendarSchema>;

// ---------------- Homebrew content ----------------
export const homebrewKindSchema = z.enum(['monster', 'spell', 'item', 'feat', 'condition']);
export type HomebrewKind = z.infer<typeof homebrewKindSchema>;

export const homebrewSchema = z.object({
  id: z.string(),
  campaignId: z.string(),
  system: systemIdSchema,
  kind: homebrewKindSchema,
  name: z.string().min(1).max(160),
  /** category-specific scalar fields (AC, HP, CR, Level, School, Rarity, …) */
  fields: z.record(z.string(), z.union([z.string(), z.number()])).default({}),
  description: z.string().max(20000).default(''),
  createdAt: z.string(),
  updatedAt: z.string(),
});
export type Homebrew = z.infer<typeof homebrewSchema>;

// ---------------- Battle maps (VTT) ----------------
export const mapTokenSchema = z.object({
  id: z.string(),
  label: z.string().max(40).default(''),
  color: z.string().max(20).default('#d4af37'),
  /** grid cell coordinates (top-left cell of the footprint) */
  col: int,
  row: int,
  /** NxN footprint (1 = one cell) */
  size: int.min(1).max(4).default(1),
  kind: z.enum(['pc', 'npc', 'monster', 'object']).default('npc'),
  /** square icon as a data URL; if absent, a linked character's portrait is used */
  image: z.string().optional(),
  /** optional link to a Character for a live HP ring / conditions */
  characterId: z.string().optional(),
  /** optional link to an active-encounter Combatant for a live HP ring */
  combatantId: z.string().optional(),
  /** denormalized HP snapshot (used when no live combatant drives the ring) */
  hp: hpSchema.optional(),
  conditions: z.array(conditionSchema).default([]),
  /** hidden from the player-facing projection */
  gmOnly: z.boolean().default(false),
});
export type MapToken = z.infer<typeof mapTokenSchema>;

/** A point in image-space pixels (scale-independent, network-portable). */
export const pointSchema = z.object({ x: num, y: num });
export type MapPoint = z.infer<typeof pointSchema>;

export const drawingSchema = z.object({
  id: z.string(),
  kind: z.enum(['freehand', 'line', 'rect', 'circle', 'arrow', 'text']),
  points: z.array(pointSchema).default([]),
  color: z.string().max(20).default('#d4af37'),
  width: num.min(1).max(40).default(3),
  text: z.string().max(200).optional(),
  gmOnly: z.boolean().default(false),
});
export type MapDrawing = z.infer<typeof drawingSchema>;

/** Sight-blocking wall as a polyline (image-space px). From UVTT import or hand-drawn. */
export const mapWallSchema = z.object({
  id: z.string().optional(),
  points: z.array(pointSchema).min(2),
});
export type MapWall = z.infer<typeof mapWallSchema>;

/** A door/portal segment that can be opened (lets sight through). */
export const mapDoorSchema = z.object({
  id: z.string().optional(),
  a: pointSchema,
  b: pointSchema,
  open: z.boolean().default(false),
});
export type MapDoor = z.infer<typeof mapDoorSchema>;

/** A light source (image-space px position, px range). */
export const mapLightSchema = z.object({
  id: z.string().optional(),
  x: num,
  y: num,
  range: num.default(0),
  color: z.string().max(20).default('#ffd9a0'),
  intensity: num.default(0.5),
});
export type MapLight = z.infer<typeof mapLightSchema>;

export const battleMapSchema = z.object({
  id: z.string(),
  campaignId: z.string(),
  name: z.string().min(1).max(120),
  /** image as a data URL (stored locally) */
  image: z.string().default(''),
  gridSize: int.min(10).max(400).default(50),
  showGrid: z.boolean().default(true),
  fogEnabled: z.boolean().default(false),
  /** revealed cell keys "col,row"; everything else is fogged when enabled */
  revealed: z.array(z.string()).default([]),
  tokens: z.array(mapTokenSchema).default([]),
  /** GM annotation layer (some entries may be gmOnly) */
  drawings: z.array(drawingSchema).default([]),
  /** sight-blocking walls (UVTT import or hand-drawn) — drive dynamic vision */
  walls: z.array(mapWallSchema).default([]),
  /** doors that can open to let sight through */
  doors: z.array(mapDoorSchema).default([]),
  /** light sources */
  lights: z.array(mapLightSchema).default([]),
  /** auto-reveal fog from party tokens' line of sight (walls block) */
  dynamicVision: z.boolean().default(false),
  /** party sight radius in feet (0 = unlimited within the map) */
  sightRadiusFeet: int.min(0).max(500).default(60),
  /** real-world feet per grid cell, for measurement + AoE labels */
  gridUnit: z.object({ feet: int.min(1).max(100).default(5) }).default({ feet: 5 }),
  gridType: z.enum(['square', 'hex']).default('square'),
  /** fog model version, for future migrations */
  fogVersion: int.default(1),
  createdAt: z.string(),
  updatedAt: z.string(),
});
export type BattleMap = z.infer<typeof battleMapSchema>;
