import type { Campaign, Character, Encounter, Note, Quest } from '@/lib/schemas';
import { getSystem, type SystemId } from '@/lib/rules';
import { computeBudget } from '@/lib/combat/budget';

export interface PartyMember {
  name: string;
  className: string;
  level: number;
  abilities: Character['abilities'];
  keyResources: string[];
}

export interface RecentEncounter {
  name: string;
  difficulty: string;
  ratingXp: number;
  basedOnSnapshot: boolean;
  outcome?: string;
}

export interface CampaignContext {
  system: SystemId;
  systemLabel: string;
  /** Leads every prompt — the anti-cross-system-hallucination anchor. */
  systemConstraint: string;
  partyLevels: number[];
  party: PartyMember[];
  recentEncounters: RecentEncounter[];
  questsSummary: string;
  notesSummary: string;
}

export interface CreatureCandidate {
  name: string;
  rating: number;
  ac?: number;
  hp?: number;
  ref?: string;
}

function num(v: unknown): number | undefined {
  const n = Number(v);
  return Number.isFinite(n) ? n : undefined;
}

/** Assemble a grounded, system-aware snapshot of the campaign for LLM prompts. */
export function buildCampaignContext(input: {
  campaign: Campaign;
  characters: Character[];
  encounters: Encounter[];
  quests: Quest[];
  notes: Note[];
}): CampaignContext {
  const { campaign, characters, encounters, quests, notes } = input;
  const system = campaign.system;
  const systemLabel = getSystem(system).label;
  const systemConstraint =
    `This is a ${systemLabel} (system id: ${system}) campaign. ` +
    `Only recommend ${systemLabel} rules, creatures, classes, feats, and options. ` +
    `Never suggest content from any other game system (e.g. do not mention D&D 5e content in a Pathfinder 2e game, or vice versa).`;

  const pcs = characters.filter((c) => c.kind === 'pc');
  const partyLevels = pcs.map((c) => c.level);
  const party: PartyMember[] = pcs.map((c) => ({
    name: c.name,
    className: c.className || 'Adventurer',
    level: c.level,
    abilities: c.abilities,
    keyResources: c.resources.map((r) => r.name),
  }));

  const recentEncounters: RecentEncounter[] = [...encounters]
    .filter((e) => e.status !== 'planning')
    .sort((a, b) => b.createdAt.localeCompare(a.createdAt))
    .slice(0, 8)
    .map((e) => {
      const levels = e.partyLevelsSnapshot?.length ? e.partyLevelsSnapshot : partyLevels;
      const budget = computeBudget(system, levels, e.combatants);
      return {
        name: e.name,
        difficulty: budget.difficulty,
        ratingXp: budget.ratingXp,
        basedOnSnapshot: !!e.partyLevelsSnapshot?.length,
        ...(e.outcome ? { outcome: e.outcome } : {}),
      };
    });

  const activeQuests = quests.filter((q) => q.status === 'active');
  const questsSummary = activeQuests.length
    ? `${activeQuests.length} active: ${activeQuests.map((q) => q.title).slice(0, 8).join('; ')}`
    : 'No active quests.';
  const notesSummary = notes.length
    ? `${notes.length} notes: ${notes.map((n) => n.title).slice(0, 8).join('; ')}`
    : 'No notes yet.';

  return { system, systemLabel, systemConstraint, partyLevels, party, recentEncounters, questsSummary, notesSummary };
}

/**
 * Pull a shortlist of level/CR-appropriate creatures from the correct system's
 * bestiary, so LLM recommendations are grounded in real data (no hallucination).
 * Reuses the eligibility window from `buildSuggestedEncounter`.
 */
export function pickCreatureCandidates(
  system: SystemId,
  partyLevels: number[],
  pool: Record<string, unknown>[],
  targetDifficulty: string,
  limit = 12,
): CreatureCandidate[] {
  const avg = partyLevels.length ? Math.round(partyLevels.reduce((a, b) => a + b, 0) / partyLevels.length) : 1;
  const is5e = system === '5e';

  const normalized = pool
    .map((m): CreatureCandidate | null => {
      const rating = is5e ? num(m.cr) : num(m.level);
      const name = typeof m.name === 'string' ? m.name : undefined;
      if (rating === undefined || !name) return null;
      const c: CreatureCandidate = { name, rating };
      const ac = num(is5e ? m.armor_class : m.ac);
      const hp = num(is5e ? m.hit_points : m.hp);
      const ref = typeof m.slug === 'string' ? m.slug : name;
      if (ac !== undefined) c.ac = ac;
      if (hp !== undefined) c.hp = hp;
      c.ref = ref;
      return c;
    })
    .filter((c): c is CreatureCandidate => c !== null)
    .filter((c) =>
      is5e ? c.rating <= avg + 1 && c.rating >= Math.max(0, avg - 6) : c.rating <= avg + 2 && c.rating >= avg - 4,
    );

  // Harder targets favour the top of the window; easier ones the bottom.
  const harder = /hard|deadly|severe|extreme/i.test(targetDifficulty);
  normalized.sort((a, b) => (harder ? b.rating - a.rating : a.rating - b.rating));
  return normalized.slice(0, limit);
}
