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

export const combatantKindSchema = z.enum(['pc', 'npc', 'monster']);

/** Damage/condition defenses snapshotted from the bestiary at add time, so the
 *  combat engine can apply resistances without an async compendium load. */
export const damageDefensesSchema = z.object({
  resist: z.array(z.string()).default([]),
  immune: z.array(z.string()).default([]),
  vulnerable: z.array(z.string()).default([]),
  conditionImmune: z.array(z.string()).default([]),
  notes: z.array(z.string()).default([]),
});
export type CombatantDamageDefenses = z.infer<typeof damageDefensesSchema>;

export const combatantSchema = z.object({
  id: z.string(),
  name: z.string().min(1).max(120),
  kind: combatantKindSchema,
  /** link to a Character, if this combatant is a tracked PC/NPC */
  characterId: z.string().optional(),
  /** ref into the bestiary, if spawned from a monster */
  monsterRef: z.string().optional(),

  initiative: int,
  /** modifier added when (re)rolling initiative */
  initBonus: int.default(0),
  ac: int,
  hp: hpSchema,
  conditions: z.array(conditionSchema).default([]),
  notes: z.string().max(2000).default(''),
  /** damage resistances/immunities snapshotted from the bestiary, if known */
  damageDefenses: damageDefensesSchema.optional(),
  /** 5e challenge rating (for difficulty budget), if known */
  cr: z.number().finite().nonnegative().optional(),
  /** PF2e creature level (for difficulty budget), if known */
  level: int.optional(),
});

export type Combatant = z.infer<typeof combatantSchema>;

export const logEntrySchema = z.object({
  round: int.nonnegative(),
  text: z.string(),
});
export type LogEntry = z.infer<typeof logEntrySchema>;

export const encounterStatusSchema = z.enum(['planning', 'active', 'ended']);

export const encounterSchema = z.object({
  id: z.string(),
  campaignId: z.string(),
  name: z.string().min(1).max(120),
  status: encounterStatusSchema.default('planning'),
  round: int.nonnegative().default(0),
  /** index into combatants of whose turn it is */
  turnIndex: int.nonnegative().default(0),
  combatants: z.array(combatantSchema).default([]),
  /** newest-last event feed for the fight */
  log: z.array(logEntrySchema).default([]),
  /** PC levels captured when combat started — lets the assistant rate this
   *  encounter's difficulty against the party as it was, not as it is now. */
  partyLevelsSnapshot: z.array(int.positive()).optional(),
  /** how the fight resolved, if recorded */
  outcome: z.enum(['tpk', 'party-victory', 'fled', 'unknown']).optional(),
  createdAt: z.string(),
  updatedAt: z.string(),
});

export type Encounter = z.infer<typeof encounterSchema>;
