<?php

/**
 * Sepsis batch skill-concept map for the form-based SBA generator only.
 * Separates Management (stable / antibiotics) from Emergency Management (unstable / resuscitation bundle).
 */

function sbaGeneratorConditionLooksSepsis(string $conditionName): bool
{
    $c = mb_strtolower(trim($conditionName));

    return (bool) preg_match('/\b(?:sepsis|septic\s+shock|severe\s+sepsis)\b/i', $c);
}

/**
 * @return list<int>
 */
function sbaSepsisStemSpO2PercentValues(string $stem): array
{
    $values = [];
    if (preg_match_all(
        '/(?:oxygen\s+saturation|spo2|spo\s*2|sao2|saturation(?:\s+levels?)?)[^\d%]{0,50}(\d{1,3})\s*%/i',
        $stem,
        $m
    )) {
        foreach ($m[1] as $v) {
            $n = (int) $v;
            if ($n >= 50 && $n <= 100) {
                $values[] = $n;
            }
        }
    }

    return $values;
}

function sbaSepsisStemSuggestsAcuteRespiratoryCompromise(string $stem): bool
{
    return (bool) preg_match(
        '/\b(?:respiratory\s+rate\s*(?:is|of|was)?\s*(?:2[4-9]|[3-6]\d)|tachypnoea|tachypnea|breathless|shortness\s+of\s+breath|'
        . 'dyspnoea|dyspnea|accessory\s+muscle|unable\s+to\s+complete\s+sentences|increased\s+work\s+of\s+breathing)\b/i',
        $stem
    );
}

/**
 * Clinically significant hypoxia: SpO₂ <92%, or <94% with acute respiratory compromise — not merely mentioning saturation.
 */
function sbaSepsisStemHasClinicallySignificantHypoxia(string $stem): bool
{
    if ($stem === '') {
        return false;
    }
    if (preg_match('/\b(?:hypoxaemia|hypoxemia|hypoxic)\b/i', $stem)
        && !preg_match('/\b(?:no|not|without|non[\s-])\s+(?:significant\s+)?(?:hypox|hypoxaemia|hypoxemia)\b/i', $stem)
        && !preg_match('/\bnon[\s-]hypoxic\b/i', $stem)) {
        return true;
    }
    $values = sbaSepsisStemSpO2PercentValues($stem);
    if ($values === []) {
        return false;
    }
    foreach ($values as $n) {
        if ($n < 92) {
            return true;
        }
        if ($n < 94 && sbaSepsisStemSuggestsAcuteRespiratoryCompromise($stem)) {
            return true;
        }
    }

    return false;
}

/**
 * Recognise equivalent sepsis emergency resuscitation keys (fluids + broad-spectrum IV antibiotics).
 */
function sbaSepsisEmergencyKeyIncludesIvFluidsAndBroadSpectrumAntibiotics(string $text): bool
{
    $t = trim($text);
    if ($t === '') {
        return false;
    }

    $hasAntibiotics = (bool) preg_match(
        '/\b(?:broad[\s-]spectrum\s+(?:iv\s+)?antibiotics|(?:iv\s+)?(?:antibiotic|antimicrobial)s?|empirical\s+(?:iv\s+)?(?:antibiotic|antimicrobial))\b/i',
        $t
    );
    $hasFluids = (bool) preg_match(
        '/\b(?:'
        . '(?:start|give|commence|administer|begin|initiate)\s+(?:iv\s+|intravenous\s+)?(?:fluid|fluids|crystalloid)'
        . '|(?:iv\s+|intravenous\s+)(?:fluid|fluids)\b'
        . '|(?:iv\s+|intravenous\s+)?fluid\s+resuscitat'
        . '|fluid\s+resuscitation\s+(?:and|plus|with)'
        . '|(?:start|give|commence|administer|begin|initiate)\s+intravenous\s+fluids'
        . ')\b/i',
        $t
    );

    if ($hasAntibiotics && $hasFluids) {
        return true;
    }

    return (bool) preg_match(
        '/\b(?:sepsis\s+(?:six|6|resuscitation)\s+bundle|resuscitation\s+bundle|begin\s+sepsis\s+resuscitation)\b/i',
        $t
    );
}

function sbaSepsisKeyedAnswerIncludesOxygenSupport(string $text): bool
{
    return (bool) preg_match(
        '/\b(?:oxygen\s+therapy|supplemental\s+oxygen|high[\s-]?flow\s+oxygen|give\s+oxygen|start\s+oxygen|administer\s+oxygen)\b/i',
        trim($text)
    );
}

function sbaGeneratorJobSkillsIncludeManagementAndEmergency(string $jobSkillsCsv): bool
{
    if ($jobSkillsCsv === '') {
        return false;
    }

    return preg_match('/\bmanagement\b/i', $jobSkillsCsv)
        && preg_match('/\bemergency\s+management\b/i', $jobSkillsCsv);
}

function sbaGeneratorTutorRequestsSepticShockManagement(string $tutorComments): bool
{
    $t = trim($tutorComments);
    if ($t === '') {
        return false;
    }

    return (bool) preg_match(
        '/\b(?:septic\s+shock|shock(?:ed)?\s+sepsis|unstable\s+sepsis|resuscitation\s+bundle|sepsis\s+six|hour[\s-]?1\s+bundle|fluid[\s-]?resuscit|iv\s+fluid.{0,40}antibiotic|combined\s+resuscitation)\b/i',
        $t
    );
}

/**
 * @param array<string, mixed> $job
 */
function sbaGeneratorSepsisBatchUsesMgmtEmergencyConceptMap(array $job): bool
{
    $condition = (string) ($job['condition_name'] ?? '');
    $skills = (string) ($job['skills'] ?? $job['_job_skills'] ?? '');

    return sbaGeneratorConditionLooksSepsis($condition)
        && sbaGeneratorJobSkillsIncludeManagementAndEmergency($skills);
}

/**
 * @param array<string, mixed> $question
 */
function sbaGeneratorQuestionUsesSepsisMgmtEmergencyConceptMap(array $question): bool
{
    $condition = (string) ($question['_condition_name'] ?? '');
    $skills = (string) ($question['_job_skills'] ?? '');

    return sbaGeneratorConditionLooksSepsis($condition)
        && sbaGeneratorJobSkillsIncludeManagementAndEmergency($skills);
}

/**
 * Binding prompt block assigned before generation for the Current skill.
 *
 * @param array<string, mixed> $job
 */
function buildSbaGeneratorSepsisSkillConceptMapBlock(array $job): string
{
    if (!sbaGeneratorSepsisBatchUsesMgmtEmergencyConceptMap($job)) {
        return '';
    }

    $skill = trim((string) ($job['skill'] ?? ''));
    $tutor = trim((string) ($job['tutor_comments'] ?? ''));
    $tutorShockMgmt = sbaGeneratorTutorRequestsSepticShockManagement($tutor);

    $lines = [];
    $lines[] = 'BINDING — SEPSIS BATCH SKILL-CONCEPT MAP (Management + Emergency Management in this job)';
    $lines[] = 'This job tests **both** Management and Emergency Management for **sepsis**. Each skill has a **pre-assigned clinical phase and keyed-concept family** — do **not** solve batch duplicate avoidance by writing a **clinically incomplete** emergency answer (e.g. antibiotics plus “monitor closely” without fluid resuscitation when the patient is hypotensive).';
    $lines[] = '';

    if (preg_match('/^management$/i', $skill)) {
        $lines[] = '**Current skill = Management — assigned concept (mandatory):**';
        $lines[] = '- **Phase:** **stable** or **non-shocked** suspected sepsis after appropriate initial assessment (fever, infection source, NEWS concern — **without** septic shock resuscitation as the learning point).';
        $lines[] = '- **Preferred key:** timely **broad-spectrum IV antibiotics** (explicit IV route) once assessment supports empirical antimicrobial therapy.';
        $lines[] = '- **Stem:** do **not** include **hypotension**, **shock**, or **lactate above 2.0 mmol/L** unless the keyed answer explicitly includes **full resuscitation** (fluids plus antibiotics / bundle) and Tutor comments require that scenario for Management.';
        if (!$tutorShockMgmt) {
            $lines[] = '- **Forbidden for Management in this batch** (reserve for Emergency Management): combined **“IV fluids and broad-spectrum IV antibiotics”**, **sepsis resuscitation bundle**, or any answer whose main teaching point is **shock resuscitation** — unless **Tutor comments** explicitly request **septic shock / resuscitation** management for this Management item.';
        } else {
            $lines[] = '- **Tutor comments** request septic-shock-style management — you may use resuscitation-level keys for Management only as directed there; otherwise keep Management on **stable suspected sepsis → IV antibiotics**.';
        }
        $lines[] = '- **Emergency Management** in the same job is pre-assigned **unstable sepsis / septic shock** with a **combined resuscitation or escalation** key — do **not** consume that concept here.';
    } elseif (preg_match('/^emergency\s+management$/i', $skill)) {
        $lines[] = '**Current skill = Emergency Management — assigned concept (mandatory):**';
        $lines[] = '- Follow **BINDING — SEPSIS EMERGENCY MANAGEMENT** scenario categories — **rotate category per item**; **at most one** initial **fluids + IV antibiotics** bundle per batch.';
        $lines[] = '- **Phase:** **unstable** suspected sepsis / septic shock (**hypotension**, **shock**, **raised lactate**, or **hypoxia** as the category requires).';
        $lines[] = '- **Do not** weaken keys to avoid duplication (e.g. fluids plus monitor without antibiotics). If antibiotics are omitted, the stem must state they are **already given** or the lead-in must **narrow** the task.';
        $lines[] = '- **Management** in the same job is pre-assigned **stable suspected sepsis → broad-spectrum IV antibiotics** — emergency items must test **distinct shock-phase** concepts (vasopressor, source control, hypoxic resuscitation, escalation), not a duplicate antibiotic-only line.';
        $lines[] = '- **Batch duplicate rule:** a **combined fluids-plus-antibiotics** emergency key is **not** a duplicate of Management’s **antibiotics-only** stable key — it is the **correct separation** of concepts.';
        $lines[] = '- **Multiple Emergency Management items:** achieve diversity by **different scenario category** and **different keyed concept** — **do not** weaken the answer by dropping **IV antibiotics** or **fluids** to avoid duplication.';
        $lines[] = '- **General immediate-management lead-in** (e.g. immediate management of septic shock): the keyed answer must normally include **IV fluid resuscitation plus prompt broad-spectrum IV antibiotics** (or an explicit **resuscitation bundle**).';
        $lines[] = '- **Narrow lead-in required** if the key omits antibiotics or fluids — e.g. “Which immediate **circulatory** intervention…?” after fluids/antibiotics already given, or “next step after initial resuscitation”.';
    } else {
        $lines[] = '**Other skills in this sepsis batch:** keep items consistent with the map above — **Management** = stable + antibiotics; **Emergency Management** = unstable + resuscitation bundle / fluids plus IV antibiotics / escalation when refractory.';
    }

    $block = implode("\n", $lines);

    return "\n" . embedPromptField($block, 5000) . "\n";
}

/**
 * Binding block for Investigation skill in sepsis (initial vs severity-marker drift).
 *
 * @param array<string, mixed> $job
 */
function buildSbaGeneratorSepsisInvestigationPromptBlock(array $job): string
{
    $condition = (string) ($job['condition_name'] ?? '');
    $skill = trim((string) ($job['skill'] ?? ''));
    if (!sbaGeneratorConditionLooksSepsis($condition) || !preg_match('/^investigation(?:\s+selection)?$/i', $skill)) {
        return '';
    }

    $lines = [];
    $lines[] = 'BINDING — SEPSIS INVESTIGATION (Current skill = Investigation)';
    $lines[] = 'Distinguish **investigation purpose** — do **not** drift from **initial diagnostic/source investigation** toward **severity or prognostic markers** unless the lead-in and stem explicitly target that.';
    $lines[] = '';
    $lines[] = '**Separate investigation purposes (do not conflate in one item):**';
    $lines[] = '- **Bedside urinalysis / urine dipstick** — rapid screening (nitrites, leukocytes, blood).';
    $lines[] = '- **Urine culture (MSU)** — organism identification and sensitivities (after dipstick or when culture is the learning point).';
    $lines[] = '- **Blood cultures** — before IV antibiotics when feasible; often the **next** step after dipstick-positive suspected urosepsis.';
    $lines[] = '- **Serum lactate** — severity / shock assessment — **not** interchangeable with source investigation unless the lead-in asks for severity.';
    $lines[] = '- **Imaging** (e.g. renal tract, abdomen) — obstruction, abscess, or source control when clinically indicated.';
    $lines[] = '';
    $lines[] = '**Urinary sepsis / urosepsis:** if the stem already includes **urinalysis** or **urine dipstick** results, the lead-in must ask for the **next** / **most appropriate further investigation** (e.g. **blood cultures**, **urine culture**, imaging) — **not** the **initial** investigation. Alternatively **remove** the dipstick/urinalysis result from the stem.';
    $lines[] = '';
    $lines[] = '**When the lead-in asks for the initial / first / most appropriate initial investigation:**';
    $lines[] = '- Key a test that matches the **stated purpose** and **has not already been reported** in the stem (commonly **blood cultures** or **urinalysis** when not yet done).';
    $lines[] = '- **Stem / lead-in alignment:** if the stem already reports a **first-line result** (urinalysis, dipstick, chest X-ray, etc.), use a **next/further** lead-in or omit the result from the stem.';
    $lines[] = '- **Do not** key **lactate alone** (or “lactate measurement”) as the **initial** investigation when the vignette is general **suspected sepsis** work-up — reserve **lactate** for lead-ins about **severity**, **perfusion**, **shock assessment**, **prognosis**, or **risk evaluation**, or when the stem explicitly centres on **tissue hypoperfusion** / **shock severity**.';
    $lines[] = '- Options may include baseline bloods (**FBC**, **U&E**, **CRP**) as distractors, but the **key** should match the **initial diagnostic/source** task — not “best severity marker”.';
    $lines[] = '- **Lactate** may appear as a **distractor** in an initial-investigation item; it should not be the **correct** answer unless severity/perfusion is the stated learning point.';

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

/**
 * @param array<string, mixed> $job
 */
function buildSbaGeneratorSepsisDiagnosisPromptBlock(array $job): string
{
    $condition = (string) ($job['condition_name'] ?? '');
    $skill = trim((string) ($job['skill'] ?? ''));
    if (!sbaGeneratorConditionLooksSepsis($condition) || !preg_match('/^diagnosis$/i', $skill)) {
        return '';
    }

    $lines = [];
    $lines[] = 'BINDING — SEPSIS DIAGNOSIS (Current skill = Diagnosis)';
    $lines[] = 'Separate **syndrome diagnosis** from **infection source** — do **not** blur them in options or justification.';
    $lines[] = '';
    $lines[] = '**Syndrome diagnosis (default for “most likely diagnosis” / “working diagnosis” lead-ins):**';
    $lines[] = '- When the lead-in asks for the **diagnosis** / **working diagnosis**, key **Sepsis** for undifferentiated suspected sepsis, or **Septic shock** only when the stem clearly supports **shock** (hypotension, lactate, refractory hypotension).';
    $lines[] = '- **Option homogeneity (mandatory):** all five options must be **one family only** — **competing acute diagnoses**, not a mix of **syndrome labels**, **infection sources**, and **severity labels**.';
    $lines[] = '- **Do not** mix **Sepsis**, **severe sepsis**, **septic shock**, **pneumonia**, and **ARDS** in the same option set. Avoid **severe sepsis** as a routine distractor (outdated tiering).';
    $lines[] = '- When the key is **Sepsis**, parallel options should be **other plausible acute diagnoses**, e.g. **Anaphylaxis**, **Cardiogenic shock**, **Hypovolaemia**, **Pulmonary embolism**, **Adrenal crisis**, **Acute pancreatitis** — **not** pneumonia / UTI / pyelonephritis (those are **sources**, not syndrome competitors).';
    $lines[] = '- **Do not** list **likely infection sources** unless the lead-in **explicitly** asks for **source of infection**.';
    $lines[] = '';
    $lines[] = '**Source-of-infection diagnosis (only when the lead-in asks for source):**';
    $lines[] = '- If the lead-in asks for the **source of infection**, **likely source**, or **focus of infection**, **all five options** must be **infection sources** (e.g. **Pneumonia**, **Urinary tract infection**, **Pyelonephritis**, **Cholangitis**, **Cellulitis**).';
    $lines[] = '- **Do not** include **Sepsis** / **septic shock** as an option when testing **source** identification.';
    $lines[] = '';
    $lines[] = '**Style:** avoid **“history of”** and **“observations show”**; use direct measurements; keep options in a **similar length band**.';

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

/**
 * @param list<string> $lines
 * @param list<array<string, mixed>> $priorRows
 */
/**
 * @return list<string>
 */
function sbaSepsisEmergencyScenarioSlotKeys(): array
{
    return [
        'initial_resuscitation',
        'refractory_hypotension',
        'source_control',
        'abdominal_source_control',
        'hypoxic_escalation',
    ];
}

function sbaSepsisEmergencyScenarioDisplayLabel(string $slot): string
{
    return match ($slot) {
        'initial_resuscitation' => 'initial septic shock resuscitation (fluids + IV antibiotics)',
        'refractory_hypotension' => 'persistent hypotension after adequate fluids (vasopressor / escalation)',
        'source_control' => 'suspected obstructed infected kidney or source control (e.g. drainage)',
        'abdominal_source_control' => 'abdominal source / source control escalation',
        'lactate_abdominal_source' => 'abdominal source / source control escalation',
        'hypoxic_escalation' => 'hypoxic sepsis needing oxygen plus resuscitation/escalation',
        default => $slot,
    };
}

/**
 * Stem states initial IV fluids and/or antibiotics have not yet been given (treat as pre-resuscitation).
 */
function sbaSepsisStemIndicatesInitialResuscitationNotYetGiven(string $stem): bool
{
    $s = mb_strtolower(trim($stem));
    if ($s === '') {
        return false;
    }

    return (bool) preg_match(
        '/\b(?:'
        . 'no\s+(?:iv\s+)?(?:fluid|antibiotics?|antimicrobials?)(?:\s+or\s+(?:iv\s+)?(?:antibiotics?|fluid|antimicrobials?))?\s+(?:yet|have\s+been)'
        . '|(?:iv\s+)?(?:fluid|antibiotics?|antimicrobials?).{0,50}have\s+not\s+(?:yet\s+)?(?:been\s+)?(?:given|administered|received|started)'
        . '|not\s+(?:yet\s+)?(?:received|been\s+given|administered|started).{0,50}(?:iv\s+)?(?:fluid|antibiot|antimicrobial)'
        . '|(?:have|has)\s+not\s+(?:yet\s+)?(?:received|been\s+given|administered).{0,60}(?:fluid|antibiot|antimicrobial)'
        . '|awaiting\s+(?:iv\s+)?(?:fluid|antibiot|antimicrobial)'
        . '|before\s+(?:any\s+)?(?:iv\s+)?(?:fluid|antibiot|antimicrobial)'
        . ')\b/i',
        $s
    );
}

/**
 * Stem clearly states IV fluid resuscitation has already been given (not merely planned).
 */
function sbaSepsisStemIndicatesIvFluidsAlreadyGiven(string $stem): bool
{
    if (sbaSepsisStemIndicatesInitialResuscitationNotYetGiven($stem)) {
        return false;
    }
    if (sbaSepsisStemReportsInitialFluidsAndAntibioticsGiven($stem)) {
        return true;
    }
    $s = mb_strtolower(trim($stem));

    return (bool) preg_match(
        '/\b(?:'
        . 'after\s+(?:adequate\s+)?(?:iv\s+)?(?:fluid|resuscitat)'
        . '|following\s+(?:adequate\s+)?(?:fluid|resuscitat)'
        . '|(?:already|has\s+been|have\s+been|were|was)\s+(?:given|administered|received|commenced|started).{0,40}(?:iv\s+)?(?:fluid|resuscitat|crystalloid)'
        . '|(?:iv\s+)?fluids?\s+(?:already|have\s+been|were)\s+(?:given|administered)'
        . '|adequate\s+(?:fluid|resuscitat).{0,30}(?:already|given|completed)'
        . '|despite\s+adequate\s+fluids'
        . '|fluid[\s-]refractory'
        . ')\b/i',
        $s
    );
}

/**
 * Persistent/refractory hypotension after initial fluids (and ideally antibiotics) — not before first-line resuscitation.
 */
function sbaSepsisStemIndicatesRefractoryHypotensionAfterInitialTreatment(string $stem, string $lead = '', string $keyed = ''): bool
{
    if (sbaSepsisStemIndicatesInitialResuscitationNotYetGiven($stem)) {
        return false;
    }
    if (sbaSepsisStemReportsInitialFluidsAndAntibioticsGiven($stem)) {
        return true;
    }
    if (!sbaSepsisStemIndicatesIvFluidsAlreadyGiven($stem)) {
        return false;
    }
    $blob = mb_strtolower(trim($stem . "\n" . $lead));
    if (preg_match(
        '/\b(?:persistent\s+hypotension|hypotension\s+persists|refractory\s+(?:shock|hypotension)|fluid[\s-]refractory|despite\s+adequate\s+fluids|following\s+adequate\s+(?:fluid|resuscitation))\b/i',
        $blob
    )) {
        return true;
    }

    return sbaSepsisKeyedAnswerIsRefractoryCirculatorySupport($keyed);
}

function sbaSepsisStemReportsInitialFluidsAndAntibioticsGiven(string $stem): bool
{
    $s = mb_strtolower(trim($stem));
    if ($s === '') {
        return false;
    }

    if (preg_match(
        '/\b(?:adequate\s+)?(?:iv\s+)?fluids?.{0,80}(?:and|plus|with).{0,80}(?:broad[\s-]?spectrum\s+)?(?:iv\s+)?antibiot/i',
        $s
    ) && preg_match('/\b(?:already|have\s+been|were|has\s+been|been\s+given|administered|commenced|started)\b/i', $s)) {
        return true;
    }
    if (preg_match(
        '/\b(?:antibiot|empirical\s+(?:iv\s+)?antimicrobial).{0,80}(?:and|plus|with).{0,80}(?:iv\s+)?fluids?/i',
        $s
    ) && preg_match('/\b(?:already|have\s+been|were|has\s+been|been\s+given|administered|commenced|started)\b/i', $s)) {
        return true;
    }

    $hasFluids = (bool) preg_match(
        '/\b(?:iv\s+fluids?|fluid\s+resuscitat|crystalloid|(?:already|has)\s+(?:received|had|been\s+given)\s+(?:adequate\s+)?(?:iv\s+)?fluids?)\b/i',
        $s
    );
    $hasAbx = (bool) preg_match(
        '/\b(?:broad[\s-]?spectrum\s+iv\s+antibiot|iv\s+antibiot|empirical\s+(?:iv\s+)?antimicrobial|antibiotics?\s+(?:already|have\s+been)\s+(?:started|given|commenced))\b/i',
        $s
    );

    return $hasFluids && $hasAbx;
}

function sbaSepsisKeyedAnswerIsRefractoryCirculatorySupport(string $keyed): bool
{
    $k = mb_strtolower(trim($keyed));
    if ($k === '') {
        return false;
    }

    return (bool) preg_match(
        '/\b(?:noradrenaline|norepinephrine|vasopressor|metaraminol|phenylephrine|inotrop)\b/i',
        $k
    );
}

function sbaSepsisKeyedAnswerIsSourceControlManagement(string $keyed): bool
{
    $k = mb_strtolower(trim($keyed));
    if ($k === '') {
        return false;
    }

    return (bool) preg_match(
        '/\b(?:source[\s-]control|nephrostomy|percutaneous\s+drain|drainage|decompress|relief\s+of\s+obstruction|urgent\s+(?:urology|surgical)|surgical\s+review|intervention\s+for)\b/i',
        $k
    );
}

function sbaSepsisEmergencyPostInitialResuscitationScenario(string $stem, string $lead, string $keyed): bool
{
    if (sbaSepsisStemIndicatesRefractoryHypotensionAfterInitialTreatment($stem, $lead, $keyed)) {
        return true;
    }

    if (sbaSepsisKeyedAnswerIsSourceControlManagement($keyed)
        && (sbaSepsisStemIndicatesIvFluidsAlreadyGiven($stem) || sbaSepsisStemReportsInitialFluidsAndAntibioticsGiven($stem))) {
        return true;
    }

    return sbaSepsisKeyedAnswerIsRefractoryCirculatorySupport($keyed)
        && sbaSepsisStemIndicatesIvFluidsAlreadyGiven($stem);
}

function sbaSepsisLeadInIsGenericImmediateManagement(string $leadIn): bool
{
    if ($leadIn === '') {
        return false;
    }

    if (!preg_match('/\b(?:immediate|most\s+appropriate)\s+management\b/i', $leadIn)) {
        return false;
    }

    return !preg_match(
        '/\b(?:next|further|additional|circulatory|vasopressor|source\s+control|oxygen|persistent\s+hypotension|after\s+(?:initial|fluids|antibiotics))\b/i',
        $leadIn
    );
}

function sbaSepsisPickNextEmergencyScenarioSlot(array $usedSlots): string
{
    $counts = [];
    foreach ($usedSlots as $slot => $flag) {
        if ($flag) {
            $counts[$slot] = 1;
        }
    }
    if (!function_exists('sbaSepsisSelectNextUnusedEmergencySlot')) {
        require_once __DIR__ . '/sba_sepsis_emergency_scenario_plan.php';
    }

    return sbaSepsisSelectNextUnusedEmergencySlot('', $counts)['slot'];
}

/**
 * Normalised emergency answer bucket for batch duplicate detection (not for display).
 */
function sbaSepsisEmergencyAnswerConceptBucket(string $keyed): string
{
    $k = mb_strtolower(trim($keyed));
    if ($k === '') {
        return '';
    }
    if (preg_match('/\b(?:iv\s+fluids?\s+and\s+broad|fluids?\s+and\s+broad[\s-]?spectrum|sepsis\s+resuscitation\s+bundle)\b/i', $k)) {
        return 'initial_bundle';
    }
    if (preg_match('/\b(?:noradrenaline|norepinephrine|vasopressor|metaraminol|phenylephrine)\b/i', $k)) {
        return 'vasopressor';
    }
    if (sbaSepsisKeyedAnswerIsSourceControlManagement($keyed)) {
        return 'source_control';
    }
    if (preg_match('/\b(?:repeat\s+lactate|recheck\s+lactate|lactate\s+(?:level|measurement)|monitor\s+saturation\s+only)\b/i', $k)
        && !preg_match('/\b(?:noradrenaline|vasopressor|source[\s-]control|drainage|urology)\b/i', $k)) {
        return 'lactate_or_monitor_only';
    }

    return 'other';
}

/**
 * @return list<string>
 */
function sbaSepsisEmergencyScenarioCategoryGuidanceLines(string $slot): array
{
    return match ($slot) {
        'initial_resuscitation' => [
            '**Category: initial septic shock resuscitation**',
            '- **Stem:** unstable suspected infection / septic shock — **no** IV fluids or IV antibiotics started yet (may have observations only).',
            '- **Lead-in:** general **immediate** / **most appropriate immediate** management.',
            '- **Correct answer concept:** **IV fluid resuscitation** plus **prompt broad-spectrum IV antibiotics** (or explicit **sepsis resuscitation bundle**).',
            '- **Avoid as keyed concepts:** oxygen alone; oral antibiotics; waiting for cultures before treatment; vasopressors before adequate fluids.',
            '- **Distractors:** plausible **wrong sequencing** (e.g. further boluses without reassessing fluid responsiveness, vasopressor before adequate fluids, ward review instead of critical care) — **not** throwaway lines (withhold antibiotics until cultures, oral antibiotics and observe, oxygen only, monitor saturation only).',
        ],
        'refractory_hypotension' => [
            '**Category: refractory hypotension after initial treatment**',
            '- **Stem must state** adequate **IV fluids** and **broad-spectrum IV antibiotics** have **already** been given with **persistent hypotension** / shock.',
            '- **Lead-in (prefer):** “What is the most appropriate **next circulatory support** step?” or “What is the most appropriate **next step for persistent hypotension**?” — **avoid** generic “most appropriate **immediate management**?”.',
            '- **Correct answer concept:** **Administer noradrenaline infusion** / **Start noradrenaline with critical care support** — acceptable **without** repeating antibiotics in the key.',
            '- **Options:** all **immediate circulatory / escalation** actions — similar **length** to the keyed line; subtler errors (e.g. start vasopressor without critical care, repeat lactate before treating persistent hypotension, further boluses without reassessing responsiveness).',
        ],
        'source_control' => [
            '**Category: suspected infected obstructed kidney / urgent source control**',
            '- **Stem:** septic shock plus obstruction/source cues — prefer **hydronephrosis**, obstructed kidney, collection, perforation concern, or abscess **already identified** in the stem when imaging is relevant.',
            '- **Lead-in (prefer):** “In addition to sepsis resuscitation, what is the **most important next step**?” or “What **additional urgent management** is required?” — **avoid** generic immediate management unless the key is a **full resuscitation bundle**.',
            '- **Correct answer concept (management wording):** arrange **urgent urological intervention**, **urgent surgical review for source control**, **escalate for urgent source control**, **drainage/decompression** — **not** “request urgent abdominal ultrasound to assess source control” unless Current skill is Investigation.',
            '- **Options:** homogeneous **source-control / escalation** actions — not mixed passive waiting or unrelated investigations.',
        ],
        'hypoxic_escalation' => [
            '**Category: hypoxic sepsis**',
            '- **Stem:** septic patient with **low oxygen saturation** (numeric SpO₂).',
            '- If lead-in is **general immediate management:** key must include **oxygen** plus sepsis resuscitation — **not** fluids/antibiotics alone while SpO₂ is low.',
            '- **Options:** if **oxygen therapy** appears, the keyed line must include oxygen **or** the lead-in must narrow away from oxygen (e.g. vasopressor after treatment given). **Do not** use **oxygen therapy only** as a distractor when the key ignores hypoxia.',
            '- **Distractors:** plausible sequencing errors (e.g. fluids/antibiotics without oxygen when hypoxic, ward review not ICU) — **not** oxygen therapy only / monitor saturation only / repeat lactate only as throwaway lines.',
        ],
        'lactate_abdominal_source' => [
            '**Category: raised lactate / poor perfusion — reassessment and escalation**',
            '- **Stem:** septic patient **after** initial fluids/antibiotics with **persistent raised lactate** or **poor perfusion** (abdominal cues optional).',
            '- **Lead-in:** **next step** in **reassessment / escalation** — not generic untreated-shock immediate management.',
            '- **Correct answer concept:** escalation, repeat lactate, critical care, or **urgent source-control management** (not investigation-only wording).',
            '- **Options:** homogeneous emergency **management/escalation** actions only.',
        ],
        default => ['**Category:** ' . sbaSepsisEmergencyScenarioDisplayLabel($slot)],
    };
}

function sbaSepsisInferEmergencyScenarioCategory(string $stem, string $lead, string $keyed): string
{
    $key = mb_strtolower(trim($keyed));
    if (preg_match('/\b(?:noradrenaline|norepinephrine|vasopressor|metaraminol|phenylephrine)\b/i', $key)) {
        return 'refractory_hypotension';
    }
    if (preg_match('/\b(?:percutaneous\s+drain|nephrostomy|relief\s+of\s+obstruction|source[\s-]control|urgent\s+urology|cholecystectomy|abscess\s+drain)\b/i', $key)) {
        return 'source_control';
    }
    if (preg_match('/\b(?:urgent\s+surgical|surgical\s+review|laparotom|source[\s-]control)\b/i', $key)
        && preg_match('/\b(?:abdominal|periton|rigidity|RUQ|RLQ|collection|perforat)\b/i', mb_strtolower($stem))) {
        return 'abdominal_source_control';
    }

    if (sbaSepsisStemIndicatesRefractoryHypotensionAfterInitialTreatment($stem, $lead, $keyed)) {
        return 'refractory_hypotension';
    }

    $blob = mb_strtolower(trim($stem . "\n" . $lead . "\n" . $keyed));
    if (preg_match('/\b(?:obstruct(?:ed|ion)?|hydronephrosis|flank\s+pain|percutaneous\s+drain|source[\s-]control|cholecystitis|abscess\s+drain|relief\s+of\s+obstruction|biliary|pyonephrosis)\b/i', $blob)) {
        return 'source_control';
    }
    if (preg_match('/\b(?:abdominal|periton|rigidity|RUQ|RLQ|collection|perforat|post[\s-]?operative\s+abdominal)\b/i', $blob)
        && !preg_match('/\b(?:hydronephrosis|obstructed\s+kidney|flank\s+pain|pyelonephritis)\b/i', $blob)) {
        return 'abdominal_source_control';
    }
    if (sbaSepsisStemHasClinicallySignificantHypoxia($stem)) {
        return 'hypoxic_escalation';
    }
    if (preg_match('/\b(?:raised\s+lactate|lactate\s+(?:remains|still|persist)|poor\s+perfusion|ongoing\s+perfusion)\b/i', $blob)
        && preg_match('/\b(?:abdominal|periton)\b/i', $blob)
        && !preg_match('/\b(?:noradrenaline|vasopressor)\b/i', $key)) {
        return 'abdominal_source_control';
    }

    return 'initial_resuscitation';
}

/**
 * @param array<string, mixed> $job
 */
function buildSbaGeneratorSepsisEmergencyPromptBlock(array $job): string
{
    $condition = (string) ($job['condition_name'] ?? '');
    $skill = trim((string) ($job['skill'] ?? ''));
    if (!sbaGeneratorConditionLooksSepsis($condition) || !preg_match('/^emergency\s+management$/i', $skill)) {
        return '';
    }

    if (!empty($job['_sepsis_emergency_scenario_plan']) && is_array($job['_sepsis_emergency_scenario_plan'])) {
        $lines = [];
        $lines[] = 'BINDING — SEPSIS EMERGENCY MANAGEMENT (general rules; **this item** is governed by **BINDING — ASSIGNED SEPSIS EMERGENCY SCENARIO** above)';
        $lines[] = 'Write only the **assigned** scenario category. Keep options homogeneous **management/escalation** actions in a **similar length band**.';
        $lines[] = 'Observations in **MS AKT order** where used: temperature, pulse, blood pressure, respiratory rate, oxygen saturation.';

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

    $lines = [];
    $lines[] = 'BINDING — SEPSIS EMERGENCY MANAGEMENT (Current skill = Emergency Management)';
    $lines[] = '**Batch planning (mandatory):** for a **5-item** Emergency batch use **five distinct scenario categories** (initial resuscitation → refractory hypotension → source control → hypoxic escalation → lactate/perfusion escalation) **before** repeating any category. **Do not** repeat **noradrenaline/vasopressor** or the **fluids-plus-antibiotics bundle** across items. If a category must repeat, use a **substantially different** clinical focus and keyed concept.';
    $lines[] = '**Only one item** should use **initial resuscitation** (fluids + IV antibiotics) unless **Tutor comments** say otherwise.';
    $lines[] = '**Lead-ins:** vary wording per item (MS AKT-style questions) — e.g. next circulatory step, additional urgent management, next escalation step — **avoid** repeating the same lead-in template.';
    $lines[] = '**Titles:** short **neutral clinical labels** (e.g. “Persistent hypotension in septic shock”, “Obstructed urinary sepsis”) — **not** “Management of …” or repeated “Escalation in septic shock management”.';
    $lines[] = '**Justification:** do **not** use “underlying issue”; explain why the keyed emergency action is best now.';
    $lines[] = '**Options:** five **homogeneous** management/escalation actions in a **similar length band** (keyed line not much longer than distractors). Use **plausible wrong sequencing**, not throwaway unsafe lines.';
    $lines[] = '**Subtler distractor examples:** further crystalloid without reassessing fluid responsiveness; broaden antibiotics without addressing shock/source; ward review instead of critical care; delay source control until BP normalises; repeat lactate before treating persistent hypotension; vasopressor without critical care; continue current treatment without escalation.';
    $lines[] = '**Forbidden throwaway distractors:** withhold antibiotics until cultures; oral antibiotics and observe; oxygen therapy only; monitor saturation only; repeat lactate only; monitor without treatment.';
    $lines[] = '';
    $lines[] = '**Scenario categories (flexible stems — do not copy fixed vignettes):**';
    foreach (sbaSepsisEmergencyScenarioSlotKeys() as $slot) {
        $lines[] = '';
        foreach (sbaSepsisEmergencyScenarioCategoryGuidanceLines($slot) as $gLine) {
            $lines[] = $gLine;
        }
    }
    $lines[] = '';
    $lines[] = '**Style:** avoid “history of”, “observations show”, “vital signs”, “underlying issue”, formulaic titles, repeated lead-ins, out-of-order vitals.';

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

/**
 * @param list<string> $lines
 * @param list<array<string, mixed>> $priorRows
 */
function sbaSepsisAppendBatchEmergencyAntiRepetitionLines(
    array &$lines,
    array $priorRows,
    string $currentSkill,
    string $conditionName,
    array $job = []
): void {
    if (!sbaGeneratorConditionLooksSepsis($conditionName) || !preg_match('/^emergency\s+management$/i', $currentSkill)) {
        return;
    }

    $usedSlots = [];
    $slotCounts = [];
    $priorEmergency = [];
    $priorLeadIns = [];
    foreach ($priorRows as $p) {
        if (!is_array($p) || !preg_match('/^emergency\s+management$/i', trim((string) ($p['skill'] ?? '')))) {
            continue;
        }
        $stem = trim((string) ($p['stem'] ?? ''));
        $lead = trim((string) ($p['lead_in'] ?? ''));
        $keyed = trim((string) ($p['correct_answer'] ?? ''));
        if ($keyed === '') {
            continue;
        }
        $slot = sbaSepsisInferEmergencyScenarioCategory($stem, $lead, $keyed);
        $usedSlots[$slot] = true;
        $slotCounts[$slot] = ($slotCounts[$slot] ?? 0) + 1;
        $priorEmergency[] = [
            'keyed' => $keyed,
            'slot' => $slot,
            'bucket' => sbaSepsisEmergencyAnswerConceptBucket($keyed),
            'lead' => $lead,
        ];
        if ($lead !== '') {
            $priorLeadIns[] = $lead;
        }
    }
    $nextSlot = sbaSepsisPickNextEmergencyScenarioSlot($usedSlots);
    $bundleUsed = false;
    foreach ($priorEmergency as $item) {
        if (preg_match(
            '/\b(?:iv\s+fluids?\s+and\s+broad|fluids?\s+and\s+broad[\s-]?spectrum|sepsis\s+resuscitation\s+bundle|begin\s+sepsis\s+resuscitation)\b/i',
            (string) $item['keyed']
        )) {
            $bundleUsed = true;
            break;
        }
    }

    if (!empty($job['_sepsis_emergency_scenario_plan']) && is_array($job['_sepsis_emergency_scenario_plan'])) {
        $lines[] = '**Sepsis Emergency Management — batch context:** follow **BINDING — ASSIGNED SEPSIS EMERGENCY SCENARIO** in the main prompt; do **not** change category or repeat prior keyed lines listed there.';
        $lines[] = '';

        return;
    }

    $lines[] = '**Sepsis Emergency Management — batch diversity (mandatory):**';
    $lines[] = '- **Assign scenario category first**, then write stem, lead-in, key, and five options. **Do not** repeat **IV fluids plus broad-spectrum IV antibiotics** / **sepsis resuscitation bundle** if already used in this batch.';
    if ($bundleUsed) {
        $lines[] = '- **A prior item already keyed initial resuscitation (fluids + IV antibiotics).** This item **must** use a **different category** below — **not** the same bundle line.';
    }
    if ($priorEmergency !== []) {
        foreach ($priorEmergency as $i => $item) {
            $lines[] = '- Prior Emergency #' . ($i + 1) . ': scenario **' . sbaSepsisEmergencyScenarioDisplayLabel((string) $item['slot'])
                . '**; keyed “' . (string) $item['keyed'] . '”.';
        }
    } else {
        $lines[] = '- **First Emergency item in this batch:** if multiple Emergency items are planned, reserve **initial resuscitation (fluids + IV antibiotics)** for **at most one** item; assign later items to other categories.';
    }
    if ($nextSlot !== '') {
        $lines[] = '- **This item — use scenario category:** **' . sbaSepsisEmergencyScenarioDisplayLabel($nextSlot) . '** (`' . $nextSlot . '`).';
        foreach (sbaSepsisEmergencyScenarioCategoryGuidanceLines($nextSlot) as $gLine) {
            $lines[] = $gLine;
        }
    } else {
        $lines[] = '- **All five scenario categories are already used in this batch.** Choose a **different clinical focus and answer concept** — **do not** repeat **noradrenaline/vasopressor** or any prior keyed line.';
    }
    foreach ($slotCounts as $slot => $count) {
        $lines[] = '- **Category already used (' . $count . '×) — do not repeat:** ' . sbaSepsisEmergencyScenarioDisplayLabel($slot)
            . ($slot === 'refractory_hypotension' ? ' (no second **noradrenaline** item)' : '') . '.';
    }
    if (in_array('vasopressor', array_column($priorEmergency, 'bucket'), true)) {
        $lines[] = '- **Noradrenaline/vasopressor already keyed** in this batch — use **source control**, **hypoxic escalation**, or **lactate/perfusion escalation** instead.';
    }
    if ($priorLeadIns !== []) {
        $lines[] = '- **Prior lead-ins (vary wording):** ' . implode(' | ', array_slice($priorLeadIns, -4));
    }
    if ($nextSlot !== 'initial_resuscitation') {
        $lines[] = '- **Forbidden for this item:** keyed **“Start IV fluids and broad-spectrum IV antibiotics”** unless category is initial resuscitation with untreated shock.';
    }
    $lines[] = '- **Options:** similar length band; subtler sequencing distractors (see BINDING block); **no** throwaway unsafe lines; **no** recycled option text from prior emergency items.';
    $lines[] = '- **Title:** neutral label only — not “Management of …” or “Escalation in septic shock management”.';
    $lines[] = '';
}
