<?php

/**
 * Deterministic pre-generation scenario plan for PE Emergency Management (main SBA generator).
 */

$formSkillsPath = dirname(__DIR__) . '/app/ai/form_generator_default_skills.php';
if (is_readable($formSkillsPath)) {
    require_once $formSkillsPath;
}
require_once __DIR__ . '/sba_pe_emergency_batch_concepts.php';

/**
 * @return array<string, int>
 */
function sbaPePresentationAffinityScores(string $presentation, string $tutorComments = ''): array
{
    $p = mb_strtolower(trim($presentation . ' ' . $tutorComments));
    $scores = [];
    foreach (sbaPeEmergencyScenarioSlotKeys() as $slot) {
        $scores[$slot] = 10;
    }

    if (preg_match('/\b(?:massive|shock|unstable|high[\s-]?risk|hypotension|cardiac\s+arrest)\b/i', $p)) {
        $scores['initial_massive_pe_shock'] += 40;
        $scores['unstable_pe_reperfusion_required'] += 25;
    }
    if (preg_match('/\b(?:contraindication|recent\s+surgery|post[\s-]?operative|intracranial|bleed|haemorrhage|hemorrhage)\b/i', $p)) {
        $scores['thrombolysis_contraindicated'] += 50;
    }
    if (preg_match('/\b(?:arrest|peri[\s-]?arrest|collapsed|pulseless)\b/i', $p)) {
        $scores['peri_arrest_pe_resuscitation'] += 48;
    }
    if (preg_match('/\b(?:hypox|breathless|low\s+oxygen)\b/i', $p)
        || preg_match('/\b(?:spo2|oxygen\s+saturation).{0,30}(?:8[0-9]|9[01])\s*%/i', $p)) {
        $scores['hypoxic_pe_oxygen_escalation'] += 12;
    }
    if (preg_match('/\b(?:massive|shock|unstable)\b/i', $p)) {
        $scores['hypoxic_pe_oxygen_escalation'] -= 20;
    }

    return $scores;
}

/**
 * @return list<string>
 */
function sbaPeSlotsOrderedByPresentationAffinity(string $presentation, string $tutorComments = ''): array
{
    $affinity = sbaPePresentationAffinityScores($presentation, $tutorComments);
    $slots = sbaPeEmergencyScenarioSlotKeys();
    usort($slots, static function (string $a, string $b) use ($affinity): int {
        $sa = $affinity[$a] ?? 0;
        $sb = $affinity[$b] ?? 0;
        if ($sa !== $sb) {
            return $sb <=> $sa;
        }

        return strcmp($a, $b);
    });

    return $slots;
}

/**
 * @param list<array<string, mixed>> $priorRows
 * @return list<string>
 */
function sbaPeEmergencySlotsFromPriorRows(array $priorRows): array
{
    $slots = [];
    foreach ($priorRows as $p) {
        if (!is_array($p) || !preg_match('/^emergency\s+management$/i', trim((string) ($p['skill'] ?? '')))) {
            continue;
        }
        $tags = (string) ($p['tags'] ?? '');
        if (preg_match('/\bpe_emergency_slot=([a-z_]+)/i', $tags, $m)) {
            $slots[] = sbaPeNormalizeScenarioSlotKey($m[1]);
            continue;
        }
        $stem = trim((string) ($p['stem'] ?? ''));
        $lead = trim((string) ($p['lead_in'] ?? ''));
        $keyed = trim((string) ($p['correct_answer'] ?? ''));
        if ($keyed !== '') {
            $slots[] = sbaPeInferEmergencyScenarioCategory($stem, $lead, $keyed);
        }
    }

    return $slots;
}

/**
 * @param array<string, int> $slotUseCounts
 * @return array{slot: string, repeat_cycle: int}
 */
function sbaPeSelectNextUnusedEmergencySlot(string $presentation, array $slotUseCounts, string $tutorComments = ''): array
{
    $ordered = sbaPeSlotsOrderedByPresentationAffinity($presentation, $tutorComments);
    foreach ($ordered as $slot) {
        if (($slotUseCounts[$slot] ?? 0) === 0) {
            return ['slot' => $slot, 'repeat_cycle' => 0];
        }
    }
    $minUse = min(array_merge([PHP_INT_MAX], array_values($slotUseCounts)));
    if ($minUse === PHP_INT_MAX) {
        $minUse = 0;
    }
    foreach ($ordered as $slot) {
        if (($slotUseCounts[$slot] ?? 0) === $minUse) {
            return ['slot' => $slot, 'repeat_cycle' => $minUse];
        }
    }

    return ['slot' => 'initial_massive_pe_shock', 'repeat_cycle' => 0];
}

function sbaPeEmergencyScenarioPlanApplies(array $job): bool
{
    $skill = trim((string) ($job['skill'] ?? ''));
    if (!preg_match('/^emergency\s+management$/i', $skill)) {
        return false;
    }
    if (!function_exists('sbaGeneratorConditionLooksPulmonaryEmbolism')) {
        require_once __DIR__ . '/sba_pe_emergency_concepts.php';
    }

    return sbaGeneratorConditionLooksPulmonaryEmbolism((string) ($job['condition_name'] ?? ''));
}

/**
 * @return array{emergency_question_numbers: list<int>, emergency_total: int, emergency_ordinal: int, current_question_number: int}
 */
function sbaPeJobEmergencyQuestionContext(array $job): array
{
    $skillsCsv = (string) ($job['_job_skills'] ?? $job['skills'] ?? '');
    $skills = array_values(array_filter(array_map('trim', explode(',', $skillsCsv))));
    if ($skills === []) {
        $skills = ['Emergency Management'];
    }
    $numQuestions = max(1, (int) ($job['num_questions'] ?? 1));
    $currentQ = max(1, (int) ($job['questions_completed'] ?? 0) + 1);
    $emergencyQs = [];
    $ordinal = 0;
    for ($i = 1; $i <= $numQuestions; ++$i) {
        $sk = formGeneratorSkillForQuestionNumber($skills, $i);
        if (!preg_match('/^emergency\s+management$/i', $sk)) {
            continue;
        }
        $emergencyQs[] = $i;
        if ($i <= $currentQ) {
            ++$ordinal;
        }
    }

    return [
        'emergency_question_numbers' => $emergencyQs,
        'emergency_total' => count($emergencyQs),
        'emergency_ordinal' => $ordinal,
        'current_question_number' => $currentQ,
    ];
}

/**
 * @return array<string, mixed>
 */
function sbaPeEmergencyScenarioPlanSpec(string $slot, int $repeatCycle): array
{
    $slot = sbaPeNormalizeScenarioSlotKey($slot);
    $repeatNote = $repeatCycle > 0
        ? '**Repeat cycle:** new setting, stem cues, lead-in, key wording, and distractors — do not copy a prior PE emergency item.'
        : '';

    $specs = match ($slot) {
        'initial_massive_pe_shock' => [
            'stem_cues' => [
                'Suspected or likely **massive / high-risk PE** with **hypotension, shock, or systolic BP under about 90 mmHg** (document a numeric BP or clear shock wording).',
                'Usually include **hypoxia** (SpO₂ clearly low) when clinically relevant.',
                '**No definitive emergency PE treatment started yet** (no thrombolysis, no established reperfusion pathway).',
                'If **IV fluids** appear in the key, phrase as **cautious supportive resuscitation** alongside oxygen and reperfusion — **not** fluids as the main teaching differentiator.',
                'Observations in **MS AKT order** where used; use **observations** not “vital signs”.',
            ],
            'lead_in_focus' => '**Definitive emergency / reperfusion** lead-in (vary wording) — e.g. life-threatening cause, definitive emergency treatment, massive PE with shock — **not** vague “immediate management” alone when SpO₂ is low.',
            'required_key_concept' => '**Oxygen plus urgent thrombolysis or reperfusion escalation** (one combined keyed line preferred when hypoxic) — **not** stable PE anticoagulation alone, **not** IV fluids plus thrombolysis without oxygen when hypoxia is in the stem.',
            'prohibited_key_concepts' => [
                'Anticoagulation alone when shock is documented.',
                'CTPA / investigation-only keys.',
                'Broad-spectrum IV antibiotics or sepsis resuscitation bundle.',
                'Thrombolysis/reperfusion when the stem documents **hypoxia only** without hypotension/shock.',
            ],
            'forbidden_distractor_lines' => [
                'Oxygen therapy only; anticoagulation alone; oral anticoagulant or outpatient referral; CTPA/CT pulmonary angiogram before treatment; antibiotics; monitor observations only; “oxygen plus thrombolysis” as distractor when the key is fluids-plus-thrombolysis without oxygen under a generic lead-in.',
            ],
        ],
        'unstable_pe_reperfusion_required' => [
            'stem_cues' => [
                'PE **suspected or diagnosed** with **haemodynamic instability** (hypotension/shock).',
                'State that **anticoagulation has already been started** (or PE confirmed on treatment) but **hypotension/shock persists**.',
                '**Do not** write this as an untreated initial presentation — this tests why **anticoagulation alone is insufficient**.',
            ],
            'lead_in_focus' => '**Definitive treatment / next step** when **anticoagulation alone is insufficient** — e.g. most appropriate definitive treatment despite anticoagulation; next step for persistent shock — **must differ** from item-1 lead-ins.',
            'required_key_concept' => '**Urgent thrombolysis / reperfusion** or **critical care-led escalation** — **not** anticoagulation alone, **not** “oxygen plus thrombolysis” as a duplicate of initial massive PE unless lead-in explicitly tests oxygen fairness.',
            'prohibited_key_concepts' => [
                'LMWH/heparin alone as keyed answer when shock persists.',
                'Investigation-first keys.',
                'Repeating the **same** “start oxygen and give thrombolysis” wording used for **initial massive PE** in the same batch.',
            ],
            'forbidden_distractor_lines' => [
                'Anticoagulation alone; oxygen only; arrange CTPA/CT pulmonary angiogram before treatment despite shock; delay reperfusion while observing response; non-urgent specialty review.',
            ],
        ],
        'thrombolysis_contraindicated' => [
            'stem_cues' => [
                '**Massive PE with shock** (document hypotension/shock) plus a **clear contraindication to thrombolysis** (e.g. recent intracranial haemorrhage, recent major surgery) stated in the stem.',
            ],
            'lead_in_focus' => '**Alternative urgent reperfusion / escalation** when thrombolysis is contraindicated — vary wording from other batch items.',
            'required_key_concept' => '**Urgent critical care/specialist escalation for alternative reperfusion** — e.g. “Arrange urgent alternative reperfusion intervention” or “Urgent critical care escalation for alternative reperfusion”. **Do not key thrombolysis/alteplase.** Prefer **generic escalation** wording — **do not** force a narrow surgical-versus-catheter choice unless that distinction is explicitly the learning point.',
            'prohibited_key_concepts' => [
                'Thrombolysis / alteplase as keyed answer.',
                'Anticoagulation alone without escalation.',
                'Keyed answer that depends only on “surgical embolectomy” vs “catheter-directed therapy” without broader urgent escalation wording.',
            ],
            'forbidden_distractor_lines' => [
                'Give alteplase/thrombolysis despite contraindication; oxygen only; anticoagulation alone without escalation; CTPA before escalation when shocked.',
            ],
        ],
        'peri_arrest_pe_resuscitation' => [
            'stem_cues' => [
                'Suspected **massive PE** in **peri-arrest or cardiac arrest** context (collapsed, pulseless, CPR).',
            ],
            'lead_in_focus' => '**Immediate resuscitation and emergency PE management** — all options must be **emergency actions**.',
            'required_key_concept' => '**Immediate resuscitation (CPR/ALS) plus urgent reperfusion/thrombolysis consideration** appropriate to context.',
            'prohibited_key_concepts' => [
                'Investigation-only or ward-review keys.',
                'Anticoagulation alone without resuscitation.',
            ],
            'forbidden_distractor_lines' => [
                'CTPA first; ward review; oxygen only without resuscitation/reperfusion.',
            ],
        ],
        'hypoxic_pe_oxygen_escalation' => [
            'stem_cues' => [
                '**Prominent hypoxia** (SpO₂ clearly low) with suspected PE.',
                'If the keyed answer includes **thrombolysis/reperfusion**, the stem **must also document haemodynamic instability** (hypotension/shock/BP under 90 mmHg) — **hypoxia alone is not enough**.',
                'Treatment not yet established.',
            ],
            'lead_in_focus' => 'Immediate management addressing **hypoxia and escalation** — vary wording.',
            'required_key_concept' => '**Oxygen plus urgent escalation** — if shocked, **oxygen plus thrombolysis/reperfusion** in one keyed line; if stable, **oxygen plus anticoagulation/urgent review** (no thrombolysis key without instability).',
            'prohibited_key_concepts' => [
                'Thrombolysis/reperfusion key with **hypoxia only** and no documented hypotension/shock.',
                'Reperfusion/thrombolysis key **without oxygen** when SpO₂ is clearly low and an oxygen-plus-thrombolysis distractor exists.',
            ],
            'forbidden_distractor_lines' => [
                'Oxygen therapy only; monitor saturation only; anticoagulation alone without oxygen when hypoxic.',
            ],
        ],
        default => [
            'stem_cues' => ['Suspected PE emergency vignette.'],
            'lead_in_focus' => 'Emergency management lead-in.',
            'required_key_concept' => 'Category-appropriate PE emergency action.',
            'prohibited_key_concepts' => [],
            'forbidden_distractor_lines' => [],
        ],
    };

    $distractorGuidance = [
        '**All five options:** homogeneous **emergency management/escalation** actions only.',
        '**Suppress:** CTPA/V-Q/D-dimer (unless explicitly testing unsafe sequencing); **oral anticoagulants** and outpatient referral in massive/shocked PE; **antibiotics**; **monitor-only** lines; easy **oxygen-only** distractors when hypoxic and the key is reperfusion.',
        '**Prefer distractors:** anticoagulation alone despite shock; supportive oxygen/fluids without reperfusion escalation; vasopressor without treating PE; non-urgent specialty review; delay reperfusion while observing; thrombolysis despite clear contraindication; imaging before escalation when shock is established.',
        '**Similar option length**; plausible **sequencing/priority errors**.',
        '**Do not** include **thrombolysis in both key and distractor** unless the distinction is explicit and fair.',
        '**Do not** repeat the same oxygen-only or anticoagulation-alone distractor across a batch.',
    ];

    $styleNotes = [
        'Neutral title — e.g. “Massive pulmonary embolism with shock”, “Haemodynamically unstable pulmonary embolism”, “Pulmonary embolism with thrombolysis contraindication”, “Peri-arrest pulmonary embolism” — **not** “Management of …”.',
        'Avoid “history of”; avoid “vital signs”; avoid “tachycardic with a pulse” — use “pulse 120/min”.',
        'Vary lead-in from prior PE emergency items in the batch.',
    ];
    if ($repeatNote !== '') {
        $styleNotes[] = $repeatNote;
    }

    return [
        'slot' => $slot,
        'label' => sbaPeEmergencyScenarioDisplayLabel($slot),
        'repeat_cycle' => $repeatCycle,
        'stem_cues' => $specs['stem_cues'],
        'lead_in_focus' => $specs['lead_in_focus'],
        'required_key_concept' => $specs['required_key_concept'],
        'prohibited_key_concepts' => $specs['prohibited_key_concepts'],
        'forbidden_distractor_lines' => $specs['forbidden_distractor_lines'],
        'distractor_guidance' => $distractorGuidance,
        'style_notes' => $styleNotes,
    ];
}

/**
 * @return array<int, array<string, mixed>>
 */
function sbaPeBuildFullEmergencyJobScenarioPlan(array $job): array
{
    if (!sbaPeEmergencyScenarioPlanApplies($job)) {
        return [];
    }

    $ctx = sbaPeJobEmergencyQuestionContext($job);
    $presentation = trim((string) ($job['presentation'] ?? ''));
    $tutor = trim((string) ($job['tutor_comments'] ?? ''));
    $priors = $job['_batch_prior_questions'] ?? [];
    $priorRows = is_array($priors) ? $priors : [];

    $plannedSequence = sbaPePlannedBatchSlotSequence($ctx['emergency_total'], $presentation, $tutor);
    $slotUseCounts = [];
    foreach (sbaPeEmergencySlotsFromPriorRows($priorRows) as $s) {
        $slotUseCounts[$s] = ($slotUseCounts[$s] ?? 0) + 1;
    }

    $plan = [];
    $ordinal = 0;
    foreach ($ctx['emergency_question_numbers'] as $qNum) {
        ++$ordinal;
        $slot = null;
        foreach ($plannedSequence as $candidate) {
            if (($slotUseCounts[$candidate] ?? 0) === 0) {
                $slot = $candidate;
                break;
            }
        }
        if ($slot === null) {
            $pick = sbaPeSelectNextUnusedEmergencySlot($presentation, $slotUseCounts, $tutor);
            $slot = $pick['slot'];
            $repeatCycle = $pick['repeat_cycle'];
        } else {
            $repeatCycle = 0;
        }
        $slotUseCounts[$slot] = ($slotUseCounts[$slot] ?? 0) + 1;

        $item = sbaPeEmergencyScenarioPlanSpec($slot, $repeatCycle);
        $item['emergency_ordinal'] = $ordinal;
        $item['emergency_total'] = $ctx['emergency_total'];
        $item['question_number'] = $qNum;
        $plan[$qNum] = $item;
    }

    return $plan;
}

/**
 * @return ?array<string, mixed>
 */
function sbaPeResolveEmergencyScenarioPlanItem(array $job): ?array
{
    if (!sbaPeEmergencyScenarioPlanApplies($job)) {
        return null;
    }
    if (!empty($job['_pe_emergency_scenario_plan']) && is_array($job['_pe_emergency_scenario_plan'])) {
        return $job['_pe_emergency_scenario_plan'];
    }
    $full = sbaPeBuildFullEmergencyJobScenarioPlan($job);
    $qNum = max(1, (int) ($job['questions_completed'] ?? 0) + 1);

    return $full[$qNum] ?? null;
}

/**
 * @param array<string, mixed> $job
 */
function buildSbaGeneratorPeEmergencyScenarioPlanBlock(array $job): string
{
    $item = sbaPeResolveEmergencyScenarioPlanItem($job);
    if ($item === null) {
        return '';
    }

    $priors = $job['_batch_prior_questions'] ?? [];
    $priorRows = is_array($priors) ? $priors : [];
    $usage = sbaPeEmergencyBatchUsageSummary($priorRows);
    $usedLabels = [];
    foreach (array_unique($usage['used_slots']) as $us) {
        $usedLabels[] = sbaPeEmergencyScenarioDisplayLabel($us) . ' (`' . $us . '`)';
    }

    $qNum = (int) $item['question_number'];
    $lines = [];
    $lines[] = 'BINDING — ASSIGNED PE EMERGENCY SCENARIO (deterministic plan — mandatory)';
    $lines[] = 'Write **only** this PE emergency category. **Do not** reuse stems, lead-ins, keyed answers, or option sets from earlier batch items.';
    $lines[] = '**Do not change scenario category** on this item unless regeneration explicitly allows it.';
    $lines[] = '';
    $lines[] = '**This item:** PE Emergency #' . (int) $item['emergency_ordinal'] . ' of ' . (int) $item['emergency_total']
        . ' (job question #' . $qNum . ').';
    $lines[] = '**Assigned category (locked):** ' . (string) $item['label'] . ' — slot `' . (string) $item['slot'] . '`.';
    if ($usedLabels !== []) {
        $lines[] = '**Categories already used in this batch (forbidden for this item):** ' . implode('; ', $usedLabels) . '.';
    }
    if ($usage['used_concepts'] !== []) {
        $lines[] = '**Answer concepts already used in this batch (do NOT repeat wording or clinical idea):**';
        foreach ($usage['used_concepts'] as $concept) {
            $lines[] = '- “' . $concept . '”';
        }
    }
    $lines[] = '';
    $lines[] = '**Required stem cues:**';
    foreach ($item['stem_cues'] as $cue) {
        $lines[] = '- ' . $cue;
    }
    $lines[] = '**Required lead-in focus:** ' . (string) $item['lead_in_focus'];
    $lines[] = '**Required correct-answer concept:** ' . (string) $item['required_key_concept'];
    $lines[] = '**Prohibited keyed concepts:**';
    foreach ($item['prohibited_key_concepts'] as $ban) {
        $lines[] = '- ' . $ban;
    }
    $lines[] = '**Forbidden distractor lines:**';
    foreach ($item['forbidden_distractor_lines'] as $fd) {
        $lines[] = '- ' . $fd;
    }
    $lines[] = '**Distractor guidance:**';
    foreach ($item['distractor_guidance'] as $dg) {
        $lines[] = '- ' . $dg;
    }
    $lines[] = '**Style:**';
    foreach ($item['style_notes'] as $sn) {
        $lines[] = '- ' . $sn;
    }

    $full = sbaPeBuildFullEmergencyJobScenarioPlan($job);
    if (count($full) > 1) {
        $lines[] = '';
        $lines[] = '**Full job PE emergency plan:**';
        foreach ($full as $pq => $pItem) {
            $marker = ((int) $pq === $qNum) ? ' ← **THIS ITEM**' : '';
            $lines[] = '- Q' . $pq . ': `' . (string) $pItem['slot'] . '`' . $marker;
        }
    }

    return "\n" . embedPromptField(implode("\n", $lines), 9000) . "\n";
}

function sbaPeEmergencyScenarioPlanTagFragment(?string $slot): string
{
    if ($slot === null || $slot === '') {
        return '';
    }

    return 'pe_emergency_slot=' . sbaPeNormalizeScenarioSlotKey($slot);
}

/**
 * @param list<array<string, mixed>> $priorRows
 * @return array{used_slots: list<string>, used_concepts: list<string>, slot_counts: array<string, int>}
 */
function sbaPeEmergencyBatchUsageSummary(array $priorRows): array
{
    $usedSlots = [];
    $usedConcepts = [];
    $slotCounts = [];
    foreach ($priorRows as $p) {
        if (!is_array($p) || !preg_match('/^emergency\s+management$/i', trim((string) ($p['skill'] ?? '')))) {
            continue;
        }
        $tags = (string) ($p['tags'] ?? '');
        $keyed = trim((string) ($p['correct_answer'] ?? ''));
        $slot = sbaPeEmergencySlotFromPriorRow($p);
        if ($slot === null) {
            continue;
        }
        $usedSlots[] = $slot;
        $slotCounts[$slot] = ($slotCounts[$slot] ?? 0) + 1;
        if ($keyed !== '') {
            $usedConcepts[] = $keyed;
        }
    }

    return [
        'used_slots' => $usedSlots,
        'used_concepts' => $usedConcepts,
        'slot_counts' => $slotCounts,
    ];
}
