<?php

declare(strict_types=1);

require_once dirname(__DIR__) . '/prompts/sba_combined_tutor_reviewer_prompt.php';

$failures = 0;

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

$question = [
    'stem' => 'A 62 year old man has haematemesis.',
    'correct_answer_letter' => 'C',
    'correct_answer' => 'Gastric ulcer',
    'option_a' => 'Mallory-Weiss tear',
    'option_b' => 'Oesophageal varices',
    'option_c' => 'Gastric ulcer',
    'option_d' => 'Duodenal ulcer',
    'option_e' => 'GORD',
];

$raw = [
    'ok' => true,
    'overall_judgement' => 'needs_edits',
    'summary' => 'Stem supports broad upper GI bleed only.',
    'scores' => [
        'clinical_correctness' => 70,
        'one_best_answer' => 65,
        'ms_akt_style' => 80,
        'skill_alignment' => 90,
        'overall_tutor_readiness' => 68,
    ],
    'status' => [
        'clinical_safety' => 'needs_review',
        'suggestions_available' => true,
        'content_changes_require_tutor_review' => true,
    ],
    'concerns' => [
        'clinical' => ['Over-specific gastric ulcer key'],
        'one_best_answer' => [],
        'style' => [],
        'skill_alignment' => [],
        'tutor_comments' => [],
        'safety' => [],
    ],
    'suggested_edits' => [
        [
            'field' => 'option_b',
            'current' => 'Oesophageal varices',
            'suggested' => 'Oesophageal variceal bleed',
            'reason' => 'Clearer distractor wording.',
            'change_type' => 'educational_content',
            'risk' => 'medium',
            'requires_tutor_review' => true,
        ],
        [
            'field' => 'option_b',
            'current' => 'Oesophageal varices',
            'suggested' => 'Oesophageal varices',
            'reason' => 'No change',
            'change_type' => 'style',
            'risk' => 'low',
            'requires_tutor_review' => false,
        ],
    ],
    'do_not_auto_apply' => true,
];

$norm = normalizeSbaCombinedTutorReviewResponse($raw, $question);
assertOk($norm['ok'] === true, 'normalize ok');
assertOk(is_array($norm['data']), 'data array');
assertOk(count($norm['data']['suggested_edits'] ?? []) === 1, 'filters unchanged duplicate edit');

$unsafe = normalizeSbaCombinedTutorReviewResponse([
    'summary' => 'Unsafe key.',
    'overall_judgement' => 'ready_for_tutor_review',
    'status' => ['clinical_safety' => 'unsafe'],
    'scores' => [],
    'concerns' => [],
    'suggested_edits' => [],
], $question);
assertOk(($unsafe['data']['overall_judgement'] ?? '') === 'do_not_approve_yet', 'unsafe forces do_not_approve_yet');

$tgaQuestion = [
    'option_a' => 'Migraine',
    'option_b' => 'Epilepsy',
    'option_c' => 'Stroke',
    'option_d' => 'Subdural haematoma',
    'option_e' => 'Transient global amnesia',
    'correct_answer_letter' => 'A',
    'correct_answer' => 'Migraine',
    'justification' => 'Migraine is most likely. Transient global amnesia is wrong because episodes are typically brief and resolve. Lewy body dementia does not present this way.',
];

$tgaRaw = [
    'summary' => 'Option E should change.',
    'overall_judgement' => 'ready_for_tutor_review',
    'status' => ['clinical_safety' => 'no_major_concerns'],
    'scores' => [
        'clinical_correctness' => 80,
        'one_best_answer' => 80,
        'ms_akt_style' => 85,
        'skill_alignment' => 90,
        'overall_tutor_readiness' => 80,
    ],
    'concerns' => [
        'clinical' => [],
        'one_best_answer' => [],
        'style' => [],
        'skill_alignment' => [],
        'tutor_comments' => [],
        'safety' => [],
    ],
    'suggested_edits' => [
        [
            'field' => 'option_e',
            'current' => 'Transient global amnesia',
            'suggested' => 'Lewy body dementia',
            'reason' => 'Better distractor for this stem.',
            'change_type' => 'educational_content',
            'risk' => 'low',
            'requires_tutor_review' => false,
        ],
    ],
];

$tgaNorm = normalizeSbaCombinedTutorReviewResponse($tgaRaw, $tgaQuestion);
assertOk($tgaNorm['ok'] === true, 'TGA normalize ok');
$staleRefs = $tgaNorm['data']['status']['stale_distractor_references'] ?? [];
assertOk(is_array($staleRefs) && count($staleRefs) > 0, 'detects stale TGA in justification');
assertOk(
    ($tgaNorm['data']['status']['justification_option_consistency'] ?? '') === 'stale_distractor_references',
    'consistency status flagged'
);
$optionEdit = $tgaNorm['data']['suggested_edits'][0] ?? [];
assertOk(!empty($optionEdit['requires_tutor_review']), 'option edit requires tutor review after consistency check');
assertOk(($optionEdit['risk'] ?? '') !== 'low', 'option edit risk raised');

$copdOxygenQuestion = [
    'stem' => 'Type 2 respiratory failure risk during COPD exacerbation.',
    'lead_in' => 'What is the most appropriate oxygen prescription?',
    'option_a' => 'Oxygen 2 L/min via nasal cannula with target SpO2 88–92%',
    'option_b' => 'Oxygen 15 L/min via non-rebreather mask',
    'option_c' => 'Room air only',
    'option_d' => 'High-flow oxygen without target range',
    'option_e' => 'Discharge without oxygen assessment',
    'correct_answer_letter' => 'A',
    'correct_answer' => 'Oxygen 2 L/min via nasal cannula with target SpO2 88–92%',
    'justification' => 'Controlled oxygen in COPD reduces hypercapnia risk. High-flow uncontrolled oxygen is unsafe.',
];

$copdKeyedMismatchRaw = [
    'summary' => 'Use controlled oxygen wording for the keyed option.',
    'overall_judgement' => 'needs_edits',
    'status' => ['clinical_safety' => 'needs_review'],
    'scores' => [
        'clinical_correctness' => 60,
        'one_best_answer' => 55,
        'ms_akt_style' => 75,
        'skill_alignment' => 85,
        'overall_tutor_readiness' => 58,
    ],
    'concerns' => [
        'clinical' => ['Uncontrolled high-flow oxygen in COPD'],
        'one_best_answer' => [],
        'style' => [],
        'skill_alignment' => [],
        'tutor_comments' => [],
        'safety' => [],
    ],
    'suggested_edits' => [
        [
            'field' => 'option_a',
            'current' => 'Oxygen 2 L/min via nasal cannula with target SpO2 88–92%',
            'suggested' => 'Controlled oxygen via Venturi 24% with target SpO2 88–92%',
            'reason' => 'Controlled oxygen wording for COPD.',
            'change_type' => 'educational_content',
            'risk' => 'medium',
            'requires_tutor_review' => true,
        ],
        [
            'field' => 'correct_answer',
            'current' => 'Oxygen 2 L/min via nasal cannula with target SpO2 88–92%',
            'suggested' => '—',
            'reason' => 'Incomplete keyed answer in model output.',
            'change_type' => 'style',
            'risk' => 'low',
            'requires_tutor_review' => false,
        ],
    ],
];

$copdDashNorm = normalizeSbaCombinedTutorReviewResponse($copdKeyedMismatchRaw, $copdOxygenQuestion);
assertOk($copdDashNorm['ok'] === true, 'COPD keyed mismatch normalize returns ok');
assertOk(count($copdDashNorm['data']['suggested_edits'] ?? []) === 0, 'COPD keyed mismatch blocks suggested edits');
assertOk(
    ($copdDashNorm['data']['status']['suggested_edits_valid'] ?? true) === false,
    'COPD dash marks suggested_edits_valid false'
);
assertOk(
    str_contains(
        (string) ($copdDashNorm['data']['status']['suggested_edits_blocked_reason'] ?? ''),
        'incomplete'
    ),
    'COPD dash blocked reason mentions incomplete'
);

$copdDistractorOnlyRaw = [
    'summary' => 'Safer distractor only.',
    'overall_judgement' => 'needs_edits',
    'status' => ['clinical_safety' => 'needs_review'],
    'scores' => [
        'clinical_correctness' => 75,
        'one_best_answer' => 70,
        'ms_akt_style' => 80,
        'skill_alignment' => 85,
        'overall_tutor_readiness' => 72,
    ],
    'concerns' => [
        'clinical' => [],
        'one_best_answer' => [],
        'style' => [],
        'skill_alignment' => [],
        'tutor_comments' => [],
        'safety' => [],
    ],
    'suggested_edits' => [
        [
            'field' => 'option_b',
            'current' => 'Oxygen 15 L/min via non-rebreather mask',
            'suggested' => 'Oxygen 4 L/min via Venturi mask at 28%',
            'reason' => 'Safer distractor wording for COPD.',
            'change_type' => 'educational_content',
            'risk' => 'medium',
            'requires_tutor_review' => true,
        ],
    ],
];
$copdDistractorNorm = normalizeSbaCombinedTutorReviewResponse($copdDistractorOnlyRaw, $copdOxygenQuestion);
assertOk(count($copdDistractorNorm['data']['suggested_edits'] ?? []) === 1, 'COPD distractor-only edit remains valid');

$copdLetterRaw = $copdKeyedMismatchRaw;
$copdLetterRaw['suggested_edits'] = [
    [
        'field' => 'correct_answer',
        'current' => 'A',
        'suggested' => 'A',
        'reason' => 'Controlled oxygen remains the key.',
        'change_type' => 'clinical_safety',
        'risk' => 'high',
        'requires_tutor_review' => true,
    ],
];
$copdLetterNorm = normalizeSbaCombinedTutorReviewResponse($copdLetterRaw, $copdOxygenQuestion);
assertOk(count($copdLetterNorm['data']['suggested_edits'] ?? []) === 0, 'unchanged letter-only correct_answer edit filtered');

$deliriumQuestion = [
    'option_a' => 'Delirium',
    'option_b' => 'Urinary tract infection',
    'option_c' => 'Stroke',
    'option_d' => 'Lewy body dementia',
    'option_e' => 'Transient global amnesia',
    'correct_answer_letter' => 'A',
    'correct_answer' => 'Delirium',
    'justification' => 'Delirium is most likely. Lewy body dementia is less likely in this acute presentation.',
];

$deliriumDupRaw = [
    'summary' => 'Replace option E with a distinct dementia distractor.',
    'overall_judgement' => 'needs_edits',
    'status' => ['clinical_safety' => 'needs_review'],
    'scores' => [
        'clinical_correctness' => 70,
        'one_best_answer' => 60,
        'ms_akt_style' => 75,
        'skill_alignment' => 85,
        'overall_tutor_readiness' => 65,
    ],
    'concerns' => [
        'clinical' => [],
        'one_best_answer' => [],
        'style' => [],
        'skill_alignment' => [],
        'tutor_comments' => [],
        'safety' => [],
    ],
    'suggested_edits' => [
        [
            'field' => 'option_e',
            'current' => 'Transient global amnesia',
            'suggested' => 'Dementia with Lewy bodies',
            'reason' => 'More relevant dementia distractor.',
            'change_type' => 'educational_content',
            'risk' => 'medium',
            'requires_tutor_review' => true,
        ],
    ],
];

$deliriumDupNorm = normalizeSbaCombinedTutorReviewResponse($deliriumDupRaw, $deliriumQuestion);
assertOk($deliriumDupNorm['ok'] === true, 'delirium duplicate normalize ok');
assertOk(count($deliriumDupNorm['data']['suggested_edits'] ?? []) === 0, 'Lewy duplicate options block suggestions');
assertOk(
    ($deliriumDupNorm['data']['status']['duplicate_options_detected'] ?? false) === true,
    'duplicate_options_detected flagged'
);
assertOk(
    ($deliriumDupNorm['data']['status']['suggested_edits_valid'] ?? true) === false,
    'delirium duplicate marks suggested_edits_valid false'
);
assertOk(
    str_contains((string) ($deliriumDupNorm['data']['status']['suggested_edits_blocked_reason'] ?? ''), 'overlap'),
    'delirium duplicate tutor message mentions overlap'
);
assertOk(
    str_contains((string) ($deliriumDupNorm['data']['status']['suggested_edits_blocked_reason'] ?? ''), 'were not applied'),
    'delirium duplicate tutor message uses not-applied wording'
);
assertOk(
    ($deliriumDupNorm['data']['status']['duplicate_options_detail'] ?? '') !== '',
    'delirium duplicate stores technical overlap detail'
);
$tutorComments = $deliriumDupNorm['data']['concerns']['tutor_comments'] ?? [];
$dupBannerInComments = false;
foreach ($tutorComments as $line) {
    if (is_string($line) && str_contains($line, 'were not applied') && str_contains($line, 'overlap')) {
        $dupBannerInComments = true;
        break;
    }
}
assertOk(!$dupBannerInComments, 'delirium duplicate does not repeat banner in tutor_comments');

$breathingAirStyleRaw = [
    'summary' => 'Style check.',
    'overall_judgement' => 'needs_edits',
    'status' => ['clinical_safety' => 'no_major_concerns'],
    'scores' => [
        'clinical_correctness' => 85,
        'one_best_answer' => 85,
        'ms_akt_style' => 80,
        'skill_alignment' => 90,
        'overall_tutor_readiness' => 82,
    ],
    'concerns' => [
        'clinical' => [],
        'one_best_answer' => [],
        'style' => ['Prefer oxygen saturation phrasing without “oxygen saturation is 98% breathing air”.'],
        'skill_alignment' => [],
        'tutor_comments' => [],
        'safety' => [],
    ],
    'suggested_edits' => [],
];
$breathingAirNorm = normalizeSbaCombinedTutorReviewResponse($breathingAirStyleRaw, $copdOxygenQuestion);
assertOk(
    count($breathingAirNorm['data']['concerns']['style'] ?? []) === 0,
    'false-positive breathing air style concern filtered'
);

$deliriumDistinctRaw = $deliriumDupRaw;
$deliriumDistinctRaw['suggested_edits'][0]['suggested'] = 'Normal pressure hydrocephalus';
$deliriumDistinctNorm = normalizeSbaCombinedTutorReviewResponse($deliriumDistinctRaw, $deliriumQuestion);
assertOk(count($deliriumDistinctNorm['data']['suggested_edits'] ?? []) === 1, 'distinct replacement option allowed');

echo PHP_EOL;
if ($failures > 0) {
    echo "{$failures} test(s) failed.\n";
    exit(1);
}
echo "All combined tutor review normalize tests passed.\n";
