<?php

declare(strict_types=1);

/**
 * MS AKT deterministic style repair (repairSbaStyle) — conservative mechanical fixes.
 */

require_once dirname(__DIR__) . '/validators/style_validator.php';

$failures = 0;

function msAktAssert(bool $ok, string $label): void
{
    global $failures;
    if ($ok) {
        echo "OK: {$label}\n";

        return;
    }
    echo "FAIL: {$label}\n";
    $failures++;
}

function msAktRepairText(string $in): string
{
    return repairSbaStyleText($in);
}

function msAktRepairQuestion(array $q): array
{
    return repairSbaStyle($q);
}

// 1. Age de-hyphenation
msAktAssert(
    msAktRepairText('A 65-year-old man has chest pain.') === 'A 65 year old man has chest pain.',
    'age: 65-year-old man'
);
msAktAssert(
    msAktRepairText('A 65-year old man has cough.') === 'A 65 year old man has cough.',
    'age: 65-year old man'
);
msAktAssert(
    msAktRepairText('A 65 year-old man has cough.') === 'A 65 year old man has cough.',
    'age: 65 year-old man'
);
msAktAssert(
    msAktRepairText('A 6-month-old infant is febrile.') === 'A 6 month old infant is febrile.',
    'age: 6-month-old infant'
);

// 2. Predicative age unchanged
msAktAssert(
    msAktRepairText('The patient is 65 years old.') === 'The patient is 65 years old.',
    'predicative age unchanged'
);
msAktAssert(
    msAktRepairText('She is 30 years old.') === 'She is 30 years old.',
    'she is 30 years old unchanged'
);

// 3. Presenting complaint
msAktAssert(
    msAktRepairText('A 45 year old man presents with melaena.') === 'A 45 year old man has melaena.',
    'presents with → has'
);
msAktAssert(
    msAktRepairText('A 30 year old woman complains of breathlessness.') === 'A 30 year old woman has breathlessness.',
    'complains of → has'
);

// 4–5. Oxygen saturation
msAktAssert(
    msAktRepairText('oxygen saturation is 92% on air') === 'oxygen saturation is 92% breathing air',
    'oxygen saturation on air'
);
msAktAssert(
    msAktRepairText('oxygen saturation is 92% on room air') === 'oxygen saturation is 92% breathing air',
    'oxygen saturation on room air'
);
msAktAssert(
    msAktRepairText('oxygen saturation 92% breathing air') === 'oxygen saturation is 92% breathing air',
    'oxygen saturation missing is'
);
msAktAssert(
    msAktRepairText('oxygen saturation is 98% on breathing air') === 'oxygen saturation is 98% breathing air',
    'oxygen saturation remove on before breathing air'
);

// 6. PMH phrase
msAktAssert(
    msAktRepairText('He has no significant past medical history.') === 'He has no other medical conditions.',
    'no significant PMH'
);

// 7. Current condition
msAktAssert(
    msAktRepairText('A 30 year old woman with a history of asthma') === 'A 30 year old woman has asthma',
    'with a history of asthma'
);

// 8. Genuine historical — smoking unchanged
msAktAssert(
    msAktRepairText('He has a history of smoking.') === 'He has a history of smoking.',
    'history of smoking unchanged'
);

// 9. Duplicated option label
$optQ = msAktRepairQuestion(['option_a' => 'A. A. Salbutamol']);
msAktAssert(($optQ['option_a'] ?? '') === 'Salbutamol', 'duplicate option label');

// 9b. Single leading option letter stripped
$optB = msAktRepairQuestion([
    'option_b' => 'B. CT scan of the head',
    'correct_answer_letter' => 'B',
    'correct_answer' => 'B. CT scan of the head',
]);
msAktAssert(($optB['option_b'] ?? '') === 'CT scan of the head', 'strip B. from option');
msAktAssert(($optB['correct_answer'] ?? '') === 'CT scan of the head', 'sync correct_answer after strip');

// Grammar: double has
msAktAssert(
    msAktRepairText(
        'A 75 year old man has chronic obstructive pulmonary disease (COPD) has increasing breathlessness.'
    ) === 'A 75 year old man has chronic obstructive pulmonary disease (COPD) and increasing breathlessness.',
    'double has clause'
);

// Affect label removal
msAktAssert(
    str_contains(msAktRepairText('A 60 year old woman has chest pain, and appears anxious.'), 'appears anxious') === false,
    'remove and appears anxious'
);

// Blood gas notation
msAktAssert(
    str_contains(msAktRepairText('Arterial blood gas shows PaO2 8 kPa and PaCO2 6 kPa.'), 'PaO2') === false
    && str_contains(msAktRepairText('Arterial blood gas shows PaO2 8 kPa and PaCO2 6 kPa.'), 'PO2'),
    'PaO2 to PO2'
);
msAktAssert(
    msAktRepairText('bicarbonate - 28 mmol/L (22-26)') === 'bicarbonate 28 mmol/L (22-26)',
    'bicarbonate dash removed'
);
msAktAssert(
    str_contains(msAktRepairText('HCO3- 28 mmol/L'), 'bicarbonate'),
    'HCO3- to bicarbonate'
);

// UK GI / haematology spellings
msAktAssert(msAktRepairText('melena and malaena') === 'melaena and melaena', 'melena/malaena → melaena');
msAktAssert(msAktRepairText('esophageal varices') === 'oesophageal varices', 'esophageal → oesophageal');
msAktAssert(msAktRepairText('hematemesis') === 'haematemesis', 'hematemesis → haematemesis');
msAktAssert(msAktRepairText('anemia and edema') === 'anaemia and oedema', 'anemia/edema UK spelling');

// COPD-style ABG stem (numeric values unchanged)
$copdAbg = msAktRepairText(
    'Arterial blood gas: pH 7.32 (7.35-7.45), PCO2 7.8 kPa (4.7-6.0), bicarbonate - 32 mmol/L (22-26).'
);
msAktAssert(str_contains($copdAbg, 'bicarbonate 32 mmol/L'), 'COPD ABG: bicarbonate spacing');
msAktAssert(str_contains($copdAbg, '7.32') && str_contains($copdAbg, '7.8'), 'COPD ABG: values unchanged');

// Upper GI bleed spelling repair
$giStem = msAktRepairText(
    'A 58 year old man has hematemesis and melena. He has esophageal varices on past endoscopy.'
);
msAktAssert(str_contains($giStem, 'haematemesis') && str_contains($giStem, 'melaena') && str_contains($giStem, 'oesophageal'),
    'upper GI UK spellings');

// ABG justification warning
$abgWarnQ = [
    'skill' => 'Interpretation',
    'stem' => 'Arterial blood gas: pH 7.32 (7.35-7.45), PCO2 7.8 kPa (4.7-6.0), bicarbonate 32 mmol/L (22-26).',
    'justification' => 'Bicarbonate is within normal range so there is no metabolic compensation. Primary disorder is respiratory acidosis.',
];
$abgCodes = array_column(validateSbaStyle($abgWarnQ), 'code');
msAktAssert(in_array('interpretation.abg_bicarbonate_compensation_denied', $abgCodes, true),
    'ABG bicarbonate compensation warning');

// Overspecific gastric ulcer warning
$giWarnQ = [
    'skill' => 'Diagnosis',
    'stem' => 'A 62 year old man has haematemesis and melaena. He takes ibuprofen.',
    'correct_answer_letter' => 'C',
    'option_c' => 'Gastric ulcer',
    'correct_answer' => 'Gastric ulcer',
];
$giCodes = array_column(validateSbaStyle($giWarnQ), 'code');
msAktAssert(in_array('diagnosis.overspecific_gastric_ulcer_unsupported_by_stem', $giCodes, true),
    'overspecific gastric ulcer warning');

// Known current condition → has
msAktAssert(
    msAktRepairText('A 65 year old man with known chronic obstructive pulmonary disease has worsening breathlessness.')
    === 'A 65 year old man has chronic obstructive pulmonary disease and worsening breathlessness.',
    'with known COPD → has COPD'
);

// Pulse phrasing
msAktAssert(
    msAktRepairText('His pulse is 98 beats per minute.') === 'His pulse 98 bpm.',
    'pulse is beats per minute → pulse bpm'
);
msAktAssert(
    msAktRepairText('pulse rate is 98 bpm') === 'pulse 98 bpm',
    'pulse rate is → pulse'
);
msAktAssert(
    msAktRepairText('pulse rate 98 bpm') === 'pulse 98 bpm',
    'pulse rate → pulse'
);
msAktAssert(
    msAktRepairText('pulse is 98 bpm') === 'pulse 98 bpm',
    'pulse is bpm → pulse bpm'
);

// Blood pressure phrasing
msAktAssert(
    msAktRepairText('His blood pressure is 145/90 mmHg.') === 'His BP 145/90 mmHg.',
    'blood pressure is → BP'
);
msAktAssert(
    msAktRepairText('BP is 145/90 mmHg') === 'BP 145/90 mmHg',
    'BP is → BP'
);

// Respiratory rate phrasing
msAktAssert(
    msAktRepairText('respiratory rate is 20 breaths per minute') === 'respiratory rate 20 breaths per minute',
    'respiratory rate is → respiratory rate'
);

// Oxygen saturation of → is (breathing air)
msAktAssert(
    msAktRepairText('oxygen saturation of 95% breathing air') === 'oxygen saturation is 95% breathing air',
    'oxygen saturation of breathing air'
);

// Grouped observation list without is/of (live-test gap)
$groupedObs = msAktRepairText(
    'His temperature is 36.5°C, pulse 98 beats per minute, blood pressure 150/90 mmHg, respiratory rate 20 breaths per minute and oxygen saturation is 98% breathing air.'
);
msAktAssert(
    str_contains($groupedObs, 'pulse 98 bpm')
    && str_contains($groupedObs, 'BP 150/90 mmHg')
    && !preg_match('/\bpulse\s+98\s+beats\s+per\s+minute\b/i', $groupedObs)
    && !preg_match('/\bblood\s+pressure\s+150/i', $groupedObs),
    'grouped observation list: pulse bpm and BP abbreviation'
);

// ACS-style observations sentence
$acsObs = msAktRepairText(
    'His pulse 98 beats per minute, blood pressure 145/90 mmHg and oxygen saturation is 96% on air.'
);
msAktAssert(
    str_contains($acsObs, 'pulse 98 bpm')
    && str_contains($acsObs, 'BP 145/90 mmHg')
    && str_contains($acsObs, 'oxygen saturation is 96% breathing air'),
    'ACS observations: compact pulse/BP and breathing air'
);

// COPD oxygen target wording unchanged by observation repair
$copdOxygen = msAktRepairText(
    'Give controlled oxygen via Venturi mask targeting SpO2 88–92% in COPD.'
);
msAktAssert(
    str_contains($copdOxygen, '88–92%')
    && str_contains($copdOxygen, 'Venturi'),
    'COPD oxygen target sentence unchanged by observation repair'
);

// Breathing air must not trigger uk.oxygen_saturation_is
$breathingAirWarns = array_filter(
    validateSbaStyle(['stem' => 'Oxygen saturation is 98% breathing air.']),
    static fn ($w) => ($w['code'] ?? '') === 'uk.oxygen_saturation_is'
);
msAktAssert(count($breathingAirWarns) === 0, 'breathing air saturation not flagged as style error');

$onAirWarns = array_filter(
    validateSbaStyle(['stem' => 'Oxygen saturation is 98% on air.']),
    static fn ($w) => ($w['code'] ?? '') === 'uk.oxygen_saturation_on_air'
);
msAktAssert(count($onAirWarns) > 0, 'on air saturation flagged');

// Full observation list (user example E)
$fullObs = msAktRepairText(
    'His temperature is 37.0°C, pulse is 98 beats per minute, blood pressure is 145/90 mmHg, respiratory rate is 20 breaths per minute, and oxygen saturation is 95% on air.'
);
msAktAssert(
    $fullObs === 'His temperature is 37.0°C, pulse 98 bpm, BP 145/90 mmHg, respiratory rate 20 breaths per minute, and oxygen saturation is 95% breathing air.',
    'full MS AKT observation list phrasing'
);

// Safe observation reorder (SpO2 before pulse → MS AKT order)
$reorderedObs = msAktRepairText(
    'His oxygen saturation is 95% on air, pulse is 88 beats per minute, and blood pressure is 130/80 mmHg.'
);
msAktAssert(
    str_starts_with($reorderedObs, 'His pulse 88 bpm')
    && str_contains($reorderedObs, 'BP 130/80 mmHg')
    && str_contains($reorderedObs, 'oxygen saturation is 95% breathing air'),
    'out-of-order observation list reordered safely'
);

// Do not alter non-observation beats per minute (conservative: no pulse prefix)
msAktAssert(
    msAktRepairText('The ward round is 98 beats per minute away.') === 'The ward round is 98 beats per minute away.',
    'beats per minute without pulse unchanged'
);

// ACS grouped observations — “observations are:” wrapper (pilot test 1)
$acsGrouped = msAktRepairText(
    'His observations are: temperature 36.5°C, pulse 98 beats per minute, blood pressure 150/90 mmHg, respiratory rate 20 breaths per minute, and oxygen saturation is 98% breathing air.'
);
msAktAssert(
    $acsGrouped === 'His temperature is 36.5°C, pulse 98 bpm, BP 150/90 mmHg, respiratory rate 20 breaths per minute and oxygen saturation is 98% breathing air.',
    'ACS observations are: wrapper → direct MS AKT line'
);

$onBreathingAir = msAktRepairText('oxygen saturation is 98% on breathing air');
msAktAssert(
    $onBreathingAir === 'oxygen saturation is 98% breathing air',
    'on breathing air corrected to breathing air'
);

$saturatingRoomAir = msAktRepairText('saturating 98% on room air');
msAktAssert(
    $saturatingRoomAir === 'oxygen saturation is 98% breathing air',
    'saturating on room air → oxygen saturation is breathing air'
);

// Observations show block → MS AKT order
$obsRepaired = msAktRepairText(
    'His observations show a pulse of 98 beats per minute, blood pressure of 145/90 mmHg, respiratory rate of 20 breaths per minute, and oxygen saturation of 95% breathing air. He is afebrile.'
);
msAktAssert(
    str_contains($obsRepaired, 'temperature is 37.0°C')
    && str_contains($obsRepaired, 'pulse 98 bpm')
    && str_contains($obsRepaired, 'BP 145/90 mmHg')
    && str_contains($obsRepaired, 'oxygen saturation is 95% breathing air')
    && !preg_match('/\bobservations\s+show\b/i', $obsRepaired),
    'observations show → direct MS AKT order'
);

// ACS investigation-style stem with observations + troponin
$acsInvestigation = msAktRepairText(
    'A 68 year old man has central chest pain. His observations show a pulse of 88 beats per minute, blood pressure of 130/80 mmHg, respiratory rate of 18 breaths per minute, and oxygen saturation of 97% breathing air. He is afebrile. Troponin is 450 ng/L (0-15).'
);
msAktAssert(
    str_contains($acsInvestigation, 'pulse 88 bpm')
    && str_contains($acsInvestigation, '450 ng/L')
    && !preg_match('/\bobservations\s+show\b/i', $acsInvestigation),
    'ACS investigation observations repair'
);

// ACS-style stem: age + breathing air repair, troponin unchanged
$acsStem = msAktRepairText(
    'A 68-year-old man has central chest pain. Oxygen saturation is 96% on air. Troponin is 450 ng/L (0-15).'
);
msAktAssert(str_contains($acsStem, '68 year old') && str_contains($acsStem, '450 ng/L'), 'ACS style repair preserves troponin');

// COPD ABG interpretation — type 2 wording warning
$copdAbgWarnQ = [
    'skill' => 'Interpretation',
    'stem' => 'A 75 year old man has COPD. Arterial blood gas: pH 7.32 (7.35-7.45), PCO2 7.8 kPa (4.7-6.0), bicarbonate 32 mmol/L (22-26).',
    'correct_answer_letter' => 'B',
    'option_b' => 'Acute respiratory acidosis with metabolic compensation',
    'correct_answer' => 'Acute respiratory acidosis with metabolic compensation',
];
$copdAbgCodes = array_column(validateSbaStyle($copdAbgWarnQ), 'code');
msAktAssert(
    in_array('interpretation.abg_copd_type2_respiratory_failure_wording', $copdAbgCodes, true),
    'COPD ABG type 2 wording warning'
);

// 10. X-ray
msAktAssert(msAktRepairText('Chest x ray shows consolidation.') === 'Chest X-ray shows consolidation.', 'x ray → X-ray');

// 11. Clinical content unchanged (letter + answer text)
$clinical = msAktRepairQuestion([
    'stem' => 'A 65 year old man has chest pain. Troponin is 450 ng/L.',
    'correct_answer_letter' => 'B',
    'correct_answer' => 'Aspirin 300 mg',
    'option_b' => 'Aspirin 300 mg',
]);
msAktAssert(($clinical['correct_answer_letter'] ?? '') === 'B', 'correct_answer_letter unchanged');
msAktAssert(str_contains((string) ($clinical['stem'] ?? ''), '450 ng/L'), 'numeric result unchanged');
msAktAssert(($clinical['correct_answer'] ?? '') === 'Aspirin 300 mg', 'correct answer text unchanged');

// Validator: no hyphenated-age warning after repair
$repaired = repairSbaStyle(['stem' => 'A 65-year-old man has wheeze.']);
$ageWarnings = array_filter(
    validateSbaStyle($repaired),
    static fn ($w) => ($w['code'] ?? '') === 'style.hyphenated_age_adjective'
);
msAktAssert(count($ageWarnings) === 0, 'no hyphenated-age warning after repair');

echo PHP_EOL;
if ($failures > 0) {
    echo "{$failures} test(s) failed.\n";
    exit(1);
}

echo 'All MS AKT style repair tests passed.' . PHP_EOL;
