import type { Monster } from '@/lib/compendium/types';
import { asDamageType, type DamageDefenses, type DamageType } from '../types';

/**
 * Parse a free-text defense string like "cold, fire" or
 * "bludgeoning, piercing, and slashing from nonmagical attacks".
 *
 * We deliberately only enforce *clean* lists: a clause that carries a qualifier
 * ("from nonmagical attacks", "that is silvered", etc.) is dropped into `notes`
 * for the GM to read, never auto-applied — matching the "don't over-promise
 * automation" rule. Returns the clean damage types plus any leftover notes.
 */
function parseDamageList(raw: string | undefined): { types: DamageType[]; notes: string[] } {
  const types: DamageType[] = [];
  const notes: string[] = [];
  const text = (raw ?? '').trim();
  if (!text) return { types, notes };

  // Split on ';' first (distinct clauses), then commas/"and" within a clause.
  for (const clause of text.split(/;/)) {
    const c = clause.trim();
    if (!c) continue;
    // A qualifier ("from", "that aren't", "except") means it's conditional — note it.
    if (/\b(from|that|except|unless|nonmagical|magical|silvered|adamantine)\b/i.test(c)) {
      notes.push(c);
      continue;
    }
    for (const word of c.split(/,|\band\b/)) {
      const t = asDamageType(word);
      if (t && !types.includes(t)) types.push(t);
    }
  }
  return { types, notes };
}

function parseConditionList(raw: string | undefined): string[] {
  return (raw ?? '')
    .split(/,|\band\b|;/)
    .map((s) => s.trim().toLowerCase())
    .filter(Boolean);
}

/** Normalize a pf2e damage/condition word ("non-magical attacks", "cold_iron"). */
function pf2eType(s: string): DamageType | undefined {
  return asDamageType(s.replace(/_/g, ' ').trim());
}

/** pf2e creature flat-defense shape, ready to attach to a combatant. */
export interface Pf2eDefenses {
  immune: DamageType[];
  conditionImmune: string[];
  resistFlat: { type: string; amount: number }[];
  weakness: { type: string; amount: number }[];
  notes: string[];
}

/**
 * pf2e creature → flat resistances/weaknesses + immunities. pf2e uses flat
 * amounts (resistance 5, weakness 5) rather than 5e's halve/double, and 'all'
 * applies to every type. Immunities mix damage types and conditions.
 */
export function normalizeMonsterDefensesPf2e(raw: {
  immunity?: unknown; resistance?: unknown; weakness?: unknown;
}): Pf2eDefenses {
  const immune: DamageType[] = [];
  const conditionImmune: string[] = [];
  for (const i of Array.isArray(raw.immunity) ? (raw.immunity as string[]) : []) {
    const t = pf2eType(String(i));
    if (t) immune.push(t);
    else conditionImmune.push(String(i).trim().toLowerCase());
  }
  const flat = (obj: unknown): { type: string; amount: number }[] => {
    if (!obj || typeof obj !== 'object') return [];
    const out: { type: string; amount: number }[] = [];
    for (const [k, v] of Object.entries(obj as Record<string, unknown>)) {
      const amount = Number(v);
      if (!Number.isFinite(amount) || amount <= 0) continue;
      const key = k.toLowerCase() === 'all' ? 'all' : pf2eType(k);
      if (key) out.push({ type: key, amount });
    }
    return out;
  };
  return { immune, conditionImmune, resistFlat: flat(raw.resistance), weakness: flat(raw.weakness), notes: [] };
}

/** Open5e monster → structured damage/condition defenses. */
export function normalizeMonsterDefenses(raw: Monster): DamageDefenses {
  const resist = parseDamageList(raw.damage_resistances);
  const immune = parseDamageList(raw.damage_immunities);
  const vulnerable = parseDamageList(raw.damage_vulnerabilities);
  return {
    resist: resist.types,
    immune: immune.types,
    vulnerable: vulnerable.types,
    conditionImmune: parseConditionList(raw.condition_immunities),
    notes: [...resist.notes, ...immune.notes, ...vulnerable.notes],
  };
}
