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

/**
 * Late-life ("third act") catalog (v2 / live runtime).
 *
 * Fills the old-age content desert: the 55+ span previously had almost no
 * bespoke events and was dominated by ageless/working-age content. These are
 * declarative `EventDefinition`s authored per the T006b recipe:
 *
 *  - INDEPENDENT one-shots are a single def, gated by minAge (+ optional
 *    occupation/condition checks via `isEligible`).
 *  - MULTI-STAGE arcs (will/estate, end-of-life reflection) are SEPARATE defs.
 *    Stage 2 is gated on the predecessor having fired (`wasAsked`) plus, where
 *    it must branch on WHICH choice the player made, a persistent flag set by
 *    the stage-1 choice's `setFlags` and read via `playerHasFlag`.
 *  - RECURRING beats (a peer's funeral, reflective evenings) are `repeatable`
 *    with a cooldown so they re-fire across the later years, with date-seeded
 *    `promptFn`/`resolutionTextFn` text variation so they do not read
 *    identically every time.
 *
 * Category: 'lateLife' (a new bucket so analytics/pacing can reason about the
 * third act as a distinct life stage).
 */

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;
}

function hasOccupation(player: EventPlayerContext, occupation: string): boolean {
  return (player.c.occupation as string | undefined) === occupation;
}

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

// Flags set by stage-1 choices to drive choice-branching follow-ups.
export const LATE_LIFE_FLAGS = {
  retired: 'lateLife.retired',
  estatePlanned: 'lateLife.estatePlanned',
  reconciledChild: 'lateLife.reconciledChild',
} as const;

function lateSeed(player: EventPlayerContext, salt: string): string {
  const day =
    (player as { dayOfYear?: unknown }).dayOfYear ??
    (player as { date?: unknown }).date ??
    0;
  return `lateLife:${salt}:${player.c.ageYears ?? 0}:${String(day)}`;
}

function lateSeeded(salt: string, variants: string[]): DynamicTextFn {
  return (player) => pickVariant(variants, lateSeed(player, salt));
}

export const lateLifeCatalog: EventDefinition[] = [
  // ===========================================================================
  // 1. Retirement Decision (MULTI-STAGE, stage 1; INDEPENDENT one-shot)
  // Sets `retired` on the choice that actually leaves work, so a follow-up
  // reflection can branch on whether the player walked away or kept going.
  // ===========================================================================
  {
    id: 'lateLife.retirementDecision',
    category: 'lateLife',
    prompt:
      'You hit the age where retirement is suddenly a real, near option. The pension math checks out, but so does the quiet dread of empty days. What do you do?',
    minAge: 60,
    maxAge: 75,
    weight: 2,
    isEligible: (player) => hasOccupation(player, 'work'),
    choices: [
      choice(
        'retire_now',
        'Retire and start the next chapter',
        'You handed in your notice. The last day was bittersweet, but waking up free felt earned. Time, finally, is yours.',
        { effects: { stats: { happiness: 10, stress: -15 } }, setFlags: [LATE_LIFE_FLAGS.retired] }
      ),
      choice(
        'phase_out',
        'Negotiate a phased, part-time exit',
        'You arranged to step down gradually - fewer hours, a softer landing. The structure keeps you steady while you let go slowly.',
        { energyCost: 5, effects: { stats: { happiness: 6, stress: -6 }, resources: { money: 200 } } }
      ),
      choice(
        'keep_working',
        'Keep working - you are not done yet',
        'You decided the work still means something to you. Retirement can wait; purpose does not.',
        { effects: { stats: { happiness: 4, stress: 4 }, resources: { money: 400 } } }
      ),
    ],
  },

  // 1b. Retirement adjustment (MULTI-STAGE, stage 2; choice-gated PASSIVE).
  // Fires only for those who actually retired (set `retired`).
  {
    id: 'lateLife.retirementAdjustment',
    category: 'lateLife',
    kind: 'passive',
    prompt: 'Settling Into Retirement',
    minAge: 60,
    maxAge: 90,
    weight: 1,
    isEligible: (player) =>
      wasAsked(player, 'lateLife.retirementDecision') &&
      playerHasFlag(player, LATE_LIFE_FLAGS.retired),
    choices: [
      choice(
        'continue',
        'Continue',
        'A few months on, retirement has stopped feeling like a vacation and started feeling like a life. You volunteer, you garden, you breathe. It fits.',
        { effects: { stats: { happiness: 8, stress: -5 } } }
      ),
    ],
  },

  // ===========================================================================
  // 2. Downsizing the Home (INDEPENDENT, once)
  // ===========================================================================
  {
    id: 'lateLife.downsizeHome',
    category: 'lateLife',
    prompt:
      'The house has grown too big and too quiet. The stairs are harder, the empty rooms echo. Friends keep mentioning smaller places. Is it time to downsize?',
    minAge: 62,
    maxAge: 90,
    weight: 1,
    choices: [
      choice(
        'downsize',
        'Sell and move somewhere smaller',
        'You let the old house go. Strange, at first - then freeing. Less to clean, less to worry about, and a tidy sum freed up for the years ahead.',
        { effects: { stats: { happiness: 5, stress: -5 }, resources: { money: 3000 } } }
      ),
      choice(
        'renovate',
        'Stay, but make it age-friendly',
        'You added grab bars, a ground-floor bedroom, better lighting. The house that holds your whole life now holds you a little safer too.',
        { moneyCost: 1500, effects: { stats: { happiness: 6, stress: -4 } } }
      ),
      choice(
        'stay_put',
        'Stay exactly as you are',
        'You are not ready to leave the place where everything happened. The memories are worth the upkeep.',
        { effects: { stats: { happiness: 4, stress: 3 } } }
      ),
    ],
  },

  // ===========================================================================
  // 3. First Grandchild (INDEPENDENT, once)
  // ===========================================================================
  {
    id: 'lateLife.firstGrandchild',
    category: 'lateLife',
    prompt:
      'The phone rings with the news you half-dreamed of: you are going to be a grandparent. A whole new generation, and a whole new role for you.',
    minAge: 55,
    maxAge: 90,
    weight: 1,
    choices: [
      choice(
        'all_in',
        'Throw yourself into being there',
        'You became the grandparent who shows up - school pickups, scraped knees, bedtime stories. Loving without the exhaustion of being the parent. Pure joy.',
        { energyCost: 5, effects: { stats: { happiness: 18, social: 8, stress: -5 } } }
      ),
      choice(
        'gentle_presence',
        'Be a warm, occasional presence',
        'You let the parents lead and slid into the easy role of the grandparent who delights without crowding. Visits became something everyone looks forward to.',
        { effects: { stats: { happiness: 12, social: 5 } } }
      ),
    ],
  },

  // ===========================================================================
  // 4. A Peer's Funeral (RECURRING, repeatable + cooldown, date-seeded text)
  // The losses come more often now. A bounded recurring beat.
  // ===========================================================================
  {
    id: 'lateLife.peersFuneral',
    category: 'lateLife',
    prompt: 'You learn that an old friend has passed. Another chair empties at the table of your generation.',
    minAge: 60,
    maxAge: 100,
    repeatable: true,
    cooldownDays: 540,
    weight: 1,
    choices: [
      choice(
        'eulogy',
        'Speak at the service',
        'You stood and told the truth about your friend - the funny parts and the tender parts. People laughed and wept. It was the right goodbye.',
        { energyCost: 5, effects: { stats: { happiness: -6, stress: 5, social: 6 } } }
      ),
      choice(
        'quiet_grief',
        'Grieve quietly and reach out to the family',
        'You sat with the loss and made sure the family knew they were not alone. Grief shared is grief carried.',
        { effects: { stats: { happiness: -8, stress: 4 } } }
      ),
      choice(
        'call_old_friends',
        'Call the friends who are still here',
        'The loss pushed you to pick up the phone. You and the survivors stayed on the line for hours, alive and grateful.',
        { effects: { stats: { happiness: -3, social: 8 } } }
      ),
    ],
  },

  // ===========================================================================
  // 5. Writing a Will / Estate (MULTI-STAGE, stage 1)
  // The plan-everything choice sets `estatePlanned` so the follow-up reflection
  // can acknowledge a settled affairs vs an avoided one.
  // ===========================================================================
  {
    id: 'lateLife.writeWill',
    category: 'lateLife',
    prompt:
      'A lawyer friend gently suggests it is time to put your affairs in order - a will, an estate plan, the decisions you have been avoiding. Where do you start?',
    minAge: 58,
    maxAge: 95,
    weight: 1,
    choices: [
      choice(
        'thorough_plan',
        'Sit down and plan it all thoroughly',
        'You spent the afternoon making every wish explicit - who gets what, who decides, what happens. It was heavy, then unexpectedly light. Nothing left to surprise anyone.',
        { energyCost: 5, moneyCost: 500, effects: { stats: { stress: -8, happiness: 4 } }, setFlags: [LATE_LIFE_FLAGS.estatePlanned] }
      ),
      choice(
        'simple_will',
        'Write a simple will and leave it at that',
        'You did the essentials and stopped there. Not everything is decided, but the most important things are. Good enough for now.',
        { effects: { stats: { stress: -3 } } }
      ),
      choice(
        'put_off',
        'Put it off a little longer',
        'You told yourself there was time. Maybe there is. But the unfinished business sits with you a little heavier than before.',
        { effects: { stats: { stress: 6 } } }
      ),
    ],
  },

  // 5b. Estate settled reflection (MULTI-STAGE, stage 2; choice-gated PASSIVE).
  {
    id: 'lateLife.estatePeace',
    category: 'lateLife',
    kind: 'passive',
    prompt: 'Affairs In Order',
    minAge: 58,
    maxAge: 100,
    weight: 1,
    isEligible: (player) =>
      wasAsked(player, 'lateLife.writeWill') &&
      playerHasFlag(player, LATE_LIFE_FLAGS.estatePlanned),
    choices: [
      choice(
        'continue',
        'Continue',
        'With everything settled, a strange calm has set in. You stopped bracing and started simply living the days you have.',
        { effects: { stats: { happiness: 6, stress: -6 } } }
      ),
    ],
  },

  // ===========================================================================
  // 6. Reconnect With an Estranged Child (MULTI-STAGE, stage 1)
  // The reach-out choice sets `reconciledChild`; the follow-up pays it off.
  // ===========================================================================
  {
    id: 'lateLife.estrangedChild',
    category: 'lateLife',
    prompt:
      'There is a child you have not spoken to in years. The silence, once a wound, has hardened into a habit. Lately you find yourself wondering how much time is left to fix it. What do you do?',
    minAge: 55,
    maxAge: 95,
    weight: 1,
    choices: [
      choice(
        'reach_out',
        'Reach out first, swallow your pride',
        'You wrote the letter you should have written years ago. No defenses, just truth. It was sent into silence - but for once, you had done your part.',
        { energyCost: 8, effects: { stats: { happiness: 3, stress: 8 } }, setFlags: [LATE_LIFE_FLAGS.reconciledChild] }
      ),
      choice(
        'through_family',
        'Ask another relative to bridge it',
        'You asked a relative to carry the message. Indirect, but the door is at least propped open now.',
        { effects: { stats: { stress: 4 } } }
      ),
      choice(
        'let_be',
        'Let it stay as it is',
        'You decided some breaks do not mend. It aches, but you made peace with the ache. Or told yourself you did.',
        { effects: { stats: { happiness: -6, stress: 3 } } }
      ),
    ],
  },

  // 6b. Reconciliation pays off (MULTI-STAGE, stage 2; choice-gated PASSIVE).
  {
    id: 'lateLife.estrangedChildReconciled',
    category: 'lateLife',
    kind: 'passive',
    prompt: 'A Door Reopens',
    minAge: 55,
    maxAge: 100,
    weight: 1,
    isEligible: (player) =>
      wasAsked(player, 'lateLife.estrangedChild') &&
      playerHasFlag(player, LATE_LIFE_FLAGS.reconciledChild),
    choices: [
      choice(
        'continue',
        'Continue',
        'Weeks later, a reply came. Tentative, careful - but real. The first phone call was awkward and then, suddenly, not. You have your child back. It was worth every hard word.',
        { effects: { stats: { happiness: 16, social: 6, stress: -8 } } }
      ),
    ],
  },

  // ===========================================================================
  // 7. A Health Scare That Lands Differently at 70 (INDEPENDENT, once)
  // The same diagnosis hits differently when the runway is short.
  // ===========================================================================
  {
    id: 'lateLife.healthScareLate',
    category: 'lateLife',
    prompt:
      'The test results need a follow-up, the doctor says, in that careful tone. At seventy, a health scare does not just frighten - it clarifies. How do you meet it?',
    minAge: 68,
    maxAge: 100,
    weight: 1,
    choices: [
      choice(
        'fight',
        'Throw everything at it',
        'You did the appointments, the regimen, the discipline. Whatever comes, you will not have gone quietly. The fight itself gave you something to hold.',
        { energyCost: 10, moneyCost: 800, effects: { stats: { stress: 8, happiness: 2 } } }
      ),
      choice(
        'acceptance',
        'Focus on living well, whatever comes',
        'You chose to spend your energy on the days rather than the dread. You called the people you love and watched the sun set, on purpose.',
        { effects: { stats: { happiness: 8, stress: -6, social: 6 } } }
      ),
      choice(
        'lean_on_family',
        'Let your family carry some of it',
        'You let them in instead of shielding them. They showed up, and the fear got smaller in a room full of people who love you.',
        { effects: { stats: { happiness: 5, social: 8, stress: -4 } } }
      ),
    ],
  },

  // ===========================================================================
  // 8. End-of-Life Reflection Arc (RECURRING, repeatable + cooldown, seeded)
  // A short, gentle recurring beat in the very late years - sitting with a life
  // lived. Bounded by a long cooldown; date-seeded so the copy varies.
  // ===========================================================================
  {
    id: 'lateLife.reflection',
    category: 'lateLife',
    prompt: 'A quiet evening turns reflective. You find yourself looking back over the whole long arc of your life.',
    minAge: 72,
    maxAge: 120,
    repeatable: true,
    cooldownDays: 400,
    weight: 1,
    choices: [
      choice(
        'gratitude',
        'Sit in gratitude for what was',
        'You counted the good - the people, the small mornings, the love that stayed. It was, you realize, enough. More than enough.',
        { effects: { stats: { happiness: 10, stress: -8 } } }
      ),
      choice(
        'reach_out',
        'Tell someone they mattered',
        'You picked up the phone and said the thing you always meant to say. Their voice cracked. So did yours. Worth it.',
        { effects: { stats: { happiness: 8, social: 8, stress: -5 } } }
      ),
      choice(
        'make_peace',
        'Make peace with a regret',
        'You stopped relitigating an old mistake and finally let it rest. The weight you set down had been yours a very long time.',
        { effects: { stats: { happiness: 6, stress: -10 } } }
      ),
    ],
  },
];

// Date-seeded variation for the two REPEATABLE beats so the late years do not
// read identically each cycle. Seeded on age/day: stable within a firing,
// varied across firings. Static strings remain the fallback.
const funeralPrompts = [
  'You learn that an old friend has passed. Another chair empties at the table of your generation.',
  'Word reaches you: someone you came up with is gone. The list of people who remember the old days grows shorter.',
  'A familiar name in the obituaries stops you cold. You knew them when you were both young and certain of everything.',
];

const reflectionPrompts = [
  'A quiet evening turns reflective. You find yourself looking back over the whole long arc of your life.',
  'The house is still, the tea is warm, and the years rise up unbidden. You let yourself remember.',
  'Late light through the window, and a lifetime to consider. You sit with all of it for a while.',
];

{
  const funeral = lateLifeCatalog.find((e) => e.id === 'lateLife.peersFuneral');
  if (funeral) {
    funeral.promptFn = lateSeeded('funeral:prompt', funeralPrompts);
  }
  const reflection = lateLifeCatalog.find((e) => e.id === 'lateLife.reflection');
  if (reflection) {
    reflection.promptFn = lateSeeded('reflection:prompt', reflectionPrompts);
  }
}

/**
 * Deterministic resolution-text lookup for a late-life choice (mirrors the
 * career/dilemma catalog helpers; used by tests and any caller needing the
 * resolved copy without firing the engine).
 */
export function resolveLateLifeChoice(eventId: string, choiceId: string): string | null {
  const definition = lateLifeCatalog.find((event) => event.id === eventId);
  if (!definition) {
    return null;
  }
  const selectedChoice = definition.choices.find((item) => item.choiceId === choiceId);
  return selectedChoice?.resolutionText ?? null;
}
