import type { DynamicTextFn, EventDefinition, EventPlayerContext } from '../types.js';
import { playerHasFlag } from '../engine/selector.js';
import { pickVariant } from '../../../utils/textVariations.js';

/**
 * Health / medical arc catalog (v2 / live runtime).
 *
 * Ported from the legacy class-based arcs in
 * `server/src/events/health/medicalArcs.ts` (UNREACHABLE in the v2 runtime)
 * into declarative `EventDefinition`s, following the T006b recipe:
 *
 *  - Each independent legacy event is one INTERACTIVE stage-1 `EventDefinition`.
 *  - Legacy `scheduleFollowUp(...)` multi-stage arcs become SEPARATE follow-up
 *    `EventDefinition`s (mostly `kind: 'passive'`, since the legacy follow-ups
 *    were informational message events). Stage N is gated on `wasAsked` of the
 *    prior stage plus, where the follow-up should only fire for SOME stage-1
 *    choices, a persistent flag set by those choices via `setFlags`.
 *  - Legacy COMPUTED/RANDOM outcomes (random condition pick, random recovery,
 *    cost-by-option) are FLATTENED to fixed `effects.stats` /
 *    `effects.resources.money` using a representative branch (documented per
 *    event). Valid stat keys: happiness, social, stress, health, energy.
 *  - Costs that the legacy code charged via `moneyCost` are modeled as the
 *    choice's `moneyCost` (folded into resources.money by the engine).
 *
 * Ids are namespaced under `health.` to stay globally unique in the registry.
 */

function wasAsked(player: EventPlayerContext, eventId: string): boolean {
  const asked = (player as { askedQuestions?: unknown }).askedQuestions;
  if (asked instanceof Set) return asked.has(eventId);
  if (Array.isArray(asked)) return asked.includes(eventId);
  return false;
}

/**
 * Affinity threshold at/above which a relation rallies during a hardship.
 * Mirrors CRISIS_SUPPORT_THRESHOLD in services/relationships/favors.ts: only a
 * relation who genuinely cares (>= 60) steps in. A neglected, low-affinity
 * family is mechanically absent when crisis strikes — the T008 payoff.
 */
const CRISIS_SUPPORT_AFFINITY = 60;

/** True if any LIVING relation likes the player at/above the crisis threshold. */
function hasHighAffinitySupporter(player: EventPlayerContext): boolean {
  const roster = (player as { r?: unknown }).r;
  if (!Array.isArray(roster)) return false;
  return roster.some((p) => {
    const person = p as { status?: unknown; affinity?: unknown };
    if (typeof person.status === 'string' && person.status !== 'alive') return false;
    return typeof person.affinity === 'number' && person.affinity >= CRISIS_SUPPORT_AFFINITY;
  });
}

function choice(
  choiceId: string,
  text: string,
  resolutionText: string,
  overrides?: Partial<EventDefinition['choices'][number]>
): EventDefinition['choices'][number] {
  return {
    choiceId,
    text,
    resolutionText,
    ...overrides,
  };
}

/**
 * Date-seeded variant seed. Stable within a single firing (same in-game day +
 * age) but distinct across firings/years, so repeats read differently. Falls
 * back to a constant when no day signal is present (then text is deterministic).
 */
function variantSeed(player: EventPlayerContext, salt: string): string {
  const day =
    (player as { dayOfYear?: unknown }).dayOfYear ??
    (player.c as { date?: unknown }).date ??
    (player as { date?: unknown }).date ??
    0;
  return `${salt}:${player.c.ageYears ?? 0}:${String(day)}`;
}

/** Build a `promptFn`/`resolutionTextFn` that picks a date-seeded variant. */
function seeded(salt: string, variants: string[]): DynamicTextFn {
  return (player) => pickVariant(variants, variantSeed(player, salt));
}

// Flags set by stage-1 choices to drive choice-branching follow-ups.
export const HEALTH_FLAGS = {
  diagnosed: 'health.diagnosed',
  inTherapy: 'health.inTherapy',
  pregnant: 'health.pregnant',
} as const;

export const healthCatalog: EventDefinition[] = [
  // ===========================================================================
  // 1. DiagnosisReveal (MULTI-STAGE, stage 1 INTERACTIVE)
  // Legacy: symptoms -> tests; a random health condition is applied and a
  // chronic/acute follow-up is scheduled. The "skip" branch instead schedules a
  // "symptoms worsened" follow-up. Flatten: each testing branch applies a fixed
  // health/happiness/stress hit + its money cost and sets `diagnosed` (unlocking
  // the treatment follow-up). The skip branch sets NO flag (its own worsening
  // follow-up is the separate `diagnosisSkippedWorsens` event).
  // ===========================================================================
  {
    id: 'health.diagnosisReveal',
    category: 'health',
    prompt:
      "Something hasn't felt right lately. Unusual symptoms won't go away, and your doctor looks concerned and recommends running some tests. What do you do?",
    minAge: 20,
    maxAge: 100,
    weight: 1,
    choices: [
      choice(
        'tests',
        'Get all the tests done ($500)',
        'The full panel came back with a clear diagnosis. The bills stung, but knowing what you are dealing with is a relief. Your doctor outlined a treatment plan.',
        {
          energyCost: 10,
          moneyCost: 500,
          effects: { stats: { happiness: -10, stress: 15, health: -5 } },
          setFlags: [HEALTH_FLAGS.diagnosed],
        }
      ),
      choice(
        'second',
        'Get a second opinion first ($300)',
        'A second opinion confirmed the diagnosis. You feel better having been thorough about it. Now you can focus on treatment.',
        {
          energyCost: 5,
          moneyCost: 300,
          effects: { stats: { happiness: -10, stress: 15, health: -5 } },
          setFlags: [HEALTH_FLAGS.diagnosed],
        }
      ),
      choice(
        'basic',
        'Do basic tests only ($200)',
        'The basic tests were enough to identify the problem. The diagnosis is real, but you caught it early. Time to start treatment.',
        {
          energyCost: 5,
          moneyCost: 200,
          effects: { stats: { happiness: -10, stress: 15, health: -5 } },
          setFlags: [HEALTH_FLAGS.diagnosed],
        }
      ),
      choice(
        'skip',
        "I'm sure it's nothing, skip tests",
        "You decided to skip the tests. Maybe it'll go away on its own. But the worry lingers in the back of your mind.",
        { effects: { stats: { stress: 10 } } }
      ),
    ],
  },

  // 1b. Diagnosis treatment progress (stage 2; flag-gated PASSIVE).
  // Fires only for players who got tested (set `diagnosed`). Legacy chronic vs
  // acute outcome is flattened to the representative "managing it" message.
  {
    id: 'health.diagnosisTreatment',
    category: 'health',
    kind: 'passive',
    prompt: 'Living With the Diagnosis',
    minAge: 20,
    maxAge: 100,
    weight: 1,
    isEligible: (player) =>
      wasAsked(player, 'health.diagnosisReveal') && playerHasFlag(player, HEALTH_FLAGS.diagnosed),
    choices: [
      choice(
        'manage',
        'Continue',
        'A couple of weeks into managing your condition. The treatment is helping but it is an adjustment. Your doctor says consistency is key, and you are learning to live with this new reality.',
        { effects: { stats: { health: 5, stress: -5 } } }
      ),
    ],
  },

  // 1c. Skipped symptoms worsen (stage 2; PASSIVE, gated on the skip branch).
  // The legacy skip path scheduled a "symptoms worsened" follow-up. We gate it
  // on stage 1 having fired but the player NOT being diagnosed (i.e. they skipped).
  {
    id: 'health.diagnosisSkippedWorsens',
    category: 'health',
    kind: 'passive',
    prompt: 'The Symptoms Worsen',
    minAge: 20,
    maxAge: 100,
    weight: 1,
    isEligible: (player) =>
      wasAsked(player, 'health.diagnosisReveal') && !playerHasFlag(player, HEALTH_FLAGS.diagnosed),
    choices: [
      choice(
        'acknowledge',
        'Continue',
        "The symptoms you ignored have gotten worse. You can't put off seeing a doctor any longer. The uncertainty is affecting your daily life.",
        { effects: { stats: { health: -10, stress: 15 } } }
      ),
    ],
  },

  // ===========================================================================
  // 2. MedicalEmergency (MULTI-STAGE, stage 1 INTERACTIVE)
  // Legacy: 911/drive/family all incur a (different) cost + health/energy/stress
  // hit and schedule a recovery follow-up; "wait" worsens things and schedules
  // a "ended up in hospital anyway" follow-up. Flatten: each acting branch keeps
  // its representative cost; "wait" applies a bigger health hit. All branches
  // lead to the (single) recovery follow-up below (gated on wasAsked).
  // ===========================================================================
  {
    id: 'health.medicalEmergency',
    category: 'health',
    prompt:
      'You wake up in the middle of the night with severe chest pains and difficulty breathing. This feels serious. What do you do?',
    minAge: 5,
    maxAge: 100,
    weight: 1,
    choices: [
      choice(
        'emergency',
        'Call 911 immediately',
        'The ambulance arrived quickly. The paramedics stabilized you and rushed you to the hospital. After hours of tests they found the problem and started treatment. The bill was steep, but you are alive.',
        { moneyCost: 5000, effects: { stats: { health: -10, energy: -25, stress: 20 } } }
      ),
      choice(
        'drive',
        'Drive yourself to the ER',
        'You drove yourself to the ER, white-knuckling the wheel. Triage got you in quickly, treated you, and let you go home. Risky, but it worked out.',
        { energyCost: 20, moneyCost: 3000, effects: { stats: { health: -10, energy: -25, stress: 20 } } }
      ),
      choice(
        'family',
        'Call a family member for help',
        'Your family member rushed over and drove you to the hospital. Having them there made it less terrifying. The doctors treated you and you are stable now.',
        { energyCost: 10, moneyCost: 3000, effects: { stats: { health: -10, energy: -25, stress: 20, social: 3 } } }
      ),
      choice(
        'wait',
        'Wait and see if it passes',
        "You tried to wait it out, but the symptoms got worse through the night. By morning you couldn't ignore it anymore.",
        { effects: { stats: { health: -20, happiness: -15, stress: 15 } } }
      ),
    ],
  },

  // 2b. Emergency recovery (stage 2; PASSIVE, gated on wasAsked stage 1).
  {
    id: 'health.emergencyRecovery',
    category: 'health',
    kind: 'passive',
    prompt: 'Recovering From the Emergency',
    minAge: 5,
    maxAge: 100,
    weight: 1,
    isEligible: (player) => wasAsked(player, 'health.medicalEmergency'),
    choices: [
      choice(
        'recover',
        'Continue',
        "A week after your medical emergency, you're recovering well. The experience was scary but the doctors caught it in time. You have a new appreciation for your health.",
        { effects: { stats: { health: 10, stress: -5 } } }
      ),
    ],
  },

  // 2c. CRISIS SUPPORT (PASSIVE; AFFINITY-GATED). After a medical emergency, a
  // high-affinity (>= CRISIS_SUPPORT_AFFINITY) living relation steps up with
  // money and emotional support. Gated on BOTH wasAsked(medicalEmergency) AND
  // having such a supporter — so a player with a neglected social circle never
  // gets this relief, while one who invested in relationships does. This is the
  // crisis-support half of the T008 "affinity is a real gate" payoff.
  {
    id: 'health.crisisSupportFromLovedOne',
    category: 'health',
    kind: 'passive',
    prompt: 'Someone Has Your Back',
    minAge: 5,
    maxAge: 100,
    weight: 2,
    isEligible: (player) =>
      wasAsked(player, 'health.medicalEmergency') && hasHighAffinitySupporter(player),
    choices: [
      choice(
        'accept_support',
        'Continue',
        'As you recover, someone who loves you quietly steps in: covering some of the bills, sitting with you, making sure you eat. Being cared for when you are down reminds you that the relationships you nurtured are worth everything.',
        { effects: { stats: { happiness: 12, stress: -10 }, resources: { money: 500 } } }
      ),
    ],
  },

  // ===========================================================================
  // 3. TherapyArc (MULTI-STAGE: start INTERACTIVE -> progress -> completion)
  // Legacy: start/online begin therapy (cost + small stress relief) and schedule
  // a 2-week progress follow-up and an 8-week completion follow-up; think/no do
  // nothing. Flatten: start/online set `inTherapy`; progress + completion are
  // flag-gated passives. Higher trigger likelihood (legacy: stress>50 OR
  // happiness<35) is approximated by an isEligible stress/happiness gate.
  // ===========================================================================
  {
    id: 'health.therapyStart',
    category: 'health',
    prompt:
      'A friend mentioned their therapist really helped them, and you have been thinking about it for a while. Therapy costs about $150 per session, usually weekly. Do you want to give it a try?',
    minAge: 16,
    maxAge: 100,
    weight: 1,
    isEligible: (player) => {
      const stress = (player.c.stress as number | undefined) ?? 0;
      const happiness = (player.c.happiness as number | undefined) ?? 50;
      return stress > 50 || happiness < 35;
    },
    choices: [
      choice(
        'start',
        'Yes, book a session ($150)',
        'You booked your first therapy appointment. Walking into that office took courage, but the therapist was warm and understanding. It felt like a weight lifted just by talking.',
        {
          energyCost: 10,
          moneyCost: 150,
          effects: { stats: { stress: -5 } },
          setFlags: [HEALTH_FLAGS.inTherapy],
        }
      ),
      choice(
        'online',
        'Look into online therapy ($80)',
        'You signed up for online therapy. Your first session is tonight from the comfort of your couch. It feels like a big step.',
        {
          energyCost: 5,
          moneyCost: 80,
          effects: { stats: { stress: -5 } },
          setFlags: [HEALTH_FLAGS.inTherapy],
        }
      ),
      choice(
        'think',
        "I'll think about it",
        "You told yourself you'd think about therapy. Maybe someday. For now, you're getting by.",
        {}
      ),
      choice(
        'no',
        "Therapy isn't for me",
        "You decided therapy isn't your thing. You'll find your own way to deal with things.",
        {}
      ),
    ],
  },

  // 3b. Therapy progress (stage 2; flag-gated PASSIVE).
  {
    id: 'health.therapyProgress',
    category: 'health',
    kind: 'passive',
    prompt: 'A Few Sessions In',
    minAge: 16,
    maxAge: 100,
    weight: 1,
    isEligible: (player) =>
      wasAsked(player, 'health.therapyStart') && playerHasFlag(player, HEALTH_FLAGS.inTherapy),
    choices: [
      choice(
        'progress',
        'Continue',
        "You've had a few therapy sessions now. It felt awkward at first, but your therapist is helping you see patterns you couldn't see before. It feels worth it.",
        { effects: { stats: { stress: -5, happiness: 5 } } }
      ),
    ],
  },

  // 3c. Therapy completion (stage 3; gated on progress having fired + flag).
  {
    id: 'health.therapyCompletion',
    category: 'health',
    kind: 'passive',
    prompt: 'Two Months of Therapy',
    minAge: 16,
    maxAge: 100,
    weight: 1,
    isEligible: (player) =>
      wasAsked(player, 'health.therapyProgress') && playerHasFlag(player, HEALTH_FLAGS.inTherapy),
    choices: [
      choice(
        'complete',
        'Continue',
        "After two months of therapy you feel like a different person. You've learned coping strategies, processed old wounds, and your relationships have improved. The investment in yourself was worth it.",
        { effects: { stats: { happiness: 12, stress: -10 } } }
      ),
    ],
  },

  // ===========================================================================
  // 4. PregnancyArc (MULTI-STAGE: discovery INTERACTIVE -> 4 passive stages)
  // Legacy: requires a living spouse/partner; discovery reaction sets pregnant +
  // happiness/stress and schedules 4 follow-ups (first trimester, midpoint,
  // third trimester, birth). The "discuss options" branch does NOT proceed.
  // Flatten: joy/nervous/shocked set `pregnant` (unlocking the chain); discuss
  // sets no flag. The 4 stages are flag-gated passives chained via wasAsked.
  // The partner-affinity bump and lifecycle child_born are NOT modeled (no NPC/
  // lifecycle plumbing in v2 effects) - documented approximation.
  // ===========================================================================
  {
    id: 'health.pregnancyDiscovery',
    category: 'health',
    prompt:
      "The test is positive. You're pregnant (or your partner is). A child is on the way. How do you react?",
    minAge: 18,
    maxAge: 45,
    weight: 1,
    isEligible: (player) => hasLivingPartner(player),
    choices: [
      choice(
        'joy',
        'Overjoyed! Start planning immediately',
        "You and your partner are over the moon! You immediately started looking at baby names and nursery ideas. This is the beginning of an incredible journey.",
        { effects: { stats: { happiness: 15, stress: -5 } }, setFlags: [HEALTH_FLAGS.pregnant] }
      ),
      choice(
        'nervous',
        'Happy but nervous about the responsibility',
        "You're happy but the reality is hitting you. A baby changes everything. Your partner squeezed your hand and said \"We'll figure it out together.\"",
        { effects: { stats: { happiness: 8, stress: 10 } }, setFlags: [HEALTH_FLAGS.pregnant] }
      ),
      choice(
        'shocked',
        'Shocked, need time to process',
        "The shock hasn't worn off yet. A baby. You sat in stunned silence for a while before a smile slowly crept across your face.",
        { effects: { stats: { happiness: 3, stress: 10 } }, setFlags: [HEALTH_FLAGS.pregnant] }
      ),
      choice(
        'discuss',
        "This wasn't planned, discuss options",
        'You and your partner had a long, honest conversation about the pregnancy. It was not easy, but you are working through it together.',
        { effects: { stats: { stress: 15 } } }
      ),
    ],
  },

  // 4b. First trimester (stage 2; flag-gated PASSIVE).
  {
    id: 'health.pregnancyTrimester1',
    category: 'health',
    kind: 'passive',
    prompt: 'The First Trimester',
    minAge: 18,
    maxAge: 46,
    weight: 1,
    isEligible: (player) =>
      wasAsked(player, 'health.pregnancyDiscovery') && playerHasFlag(player, HEALTH_FLAGS.pregnant),
    choices: [
      choice(
        'cope',
        'Continue',
        'First trimester is rough. Morning sickness, fatigue, and mood swings. Your partner is being supportive but you can tell they are nervous too. The first ultrasound is coming up.',
        { effects: { stats: { energy: -10, stress: 8 } } }
      ),
    ],
  },

  // 4c. Midpoint (stage 3; gated on trimester1 having fired + flag).
  {
    id: 'health.pregnancyMidpoint',
    category: 'health',
    kind: 'passive',
    prompt: 'Past the Halfway Point',
    minAge: 18,
    maxAge: 46,
    weight: 1,
    isEligible: (player) =>
      wasAsked(player, 'health.pregnancyTrimester1') && playerHasFlag(player, HEALTH_FLAGS.pregnant),
    choices: [
      choice(
        'glow',
        'Continue',
        'You are past the first trimester and feeling better. The baby is growing and you can feel movement now. Your partner teared up at the last ultrasound. You have started setting up the nursery.',
        { effects: { stats: { happiness: 8, stress: -3 } } }
      ),
    ],
  },

  // 4d. Third trimester (stage 4; gated on midpoint having fired + flag).
  {
    id: 'health.pregnancyTrimester3',
    category: 'health',
    kind: 'passive',
    prompt: 'The Third Trimester',
    minAge: 18,
    maxAge: 46,
    weight: 1,
    isEligible: (player) =>
      wasAsked(player, 'health.pregnancyMidpoint') && playerHasFlag(player, HEALTH_FLAGS.pregnant),
    choices: [
      choice(
        'prep',
        'Continue',
        'The third trimester is here. You are huge, uncomfortable, and excited. Your partner assembled the crib (it only took three tries). The baby shower was wonderful. Almost there.',
        { effects: { stats: { happiness: 5, energy: -10, stress: 5 } } }
      ),
    ],
  },

  // 4e. Birth (stage 5; gated on third trimester having fired + flag).
  {
    id: 'health.pregnancyBirth',
    category: 'health',
    kind: 'passive',
    prompt: 'The Big Day',
    minAge: 18,
    maxAge: 46,
    weight: 1,
    isEligible: (player) =>
      wasAsked(player, 'health.pregnancyTrimester3') && playerHasFlag(player, HEALTH_FLAGS.pregnant),
    choices: [
      choice(
        'birth',
        'Continue',
        "It's time! After hours of labor, your baby is born. The tiny cry fills the room and everything changes. You are crying. Your partner is crying. The nurses are smiling. Welcome to parenthood.",
        { effects: { stats: { happiness: 20, energy: -20, stress: -5 } } }
      ),
    ],
  },
];

/** Living spouse/partner check, mirroring the legacy PregnancyArc gate. */
function hasLivingPartner(player: EventPlayerContext): boolean {
  const roster = (player as { r?: unknown }).r;
  if (!Array.isArray(roster)) return false;
  return roster.some((p) => {
    const person = p as { status?: unknown; relationships?: unknown };
    if (person.status !== 'alive') return false;
    const rels = person.relationships;
    if (!Array.isArray(rels)) return false;
    return rels.includes('spouse') || rels.includes('partner');
  });
}

// Date-seeded variation for the REPEATABLE / recurring-feel medical follow-ups.
// (None of the health stage-1 events are repeatable - they are once-ever arcs -
// but the recovery/treatment passives benefit from variety if a player goes
// through multiple arcs over a lifetime.) Applied via the optional `*Fn`
// override so the static `resolutionText` remains the fallback.
const diagnosisTreatmentVariants = [
  'A couple of weeks into managing your condition. The treatment is helping but it is an adjustment. Your doctor says consistency is key.',
  'Two weeks in, and the new routine is becoming second nature. The medication helps, and you are learning to live with this new reality.',
  'The treatment plan is working. Some days are harder than others, but you feel more in control of your health than you have in a while.',
];

const emergencyRecoveryVariants = [
  "A week after your medical emergency, you're recovering well. The doctors caught it in time, and you have a new appreciation for your health.",
  'Recovery is going smoothly. The scare was a wake-up call, but you are back on your feet and grateful for the care you got.',
  "It's been a week, and you feel stronger every day. That night was terrifying, but you made it through and learned to listen to your body.",
];

for (const def of healthCatalog) {
  if (def.id === 'health.diagnosisTreatment') {
    def.choices[0].resolutionTextFn = seeded('diagnosisTreatment', diagnosisTreatmentVariants);
  }
  if (def.id === 'health.emergencyRecovery') {
    def.choices[0].resolutionTextFn = seeded('emergencyRecovery', emergencyRecoveryVariants);
  }
}

/**
 * Deterministic resolution-text lookup for a health choice (static fallback
 * text). Used by tests and any caller needing the resolved copy without firing
 * the engine. Returns the static `resolutionText`; per-firing variants are
 * produced by the engine via `resolutionTextFn`.
 */
export function resolveHealthChoice(eventId: string, choiceId: string): string | null {
  const definition = healthCatalog.find((event) => event.id === eventId);
  if (!definition) {
    return null;
  }
  const selectedChoice = definition.choices.find((item) => item.choiceId === choiceId);
  return selectedChoice?.resolutionText ?? null;
}
