<?php

/**
 * Shared style validation context for main SBA generator review/repair/safety passes.
 */

require_once __DIR__ . '/generator_batch_context.php';
require_once __DIR__ . '/form_generator_retry.php';

/**
 * Apply deterministic MS AKT style repair to question text fields (display, review, validation).
 *
 * @param array<string, mixed> $question
 * @return array<string, mixed>
 */
function formGeneratorApplyStyleRepairToQuestionRow(array $question): array
{
    $validatorPath = dirname(__DIR__, 2) . '/validators/style_validator.php';
    if (!is_readable($validatorPath)) {
        return $question;
    }
    require_once $validatorPath;
    if (!function_exists('repairSbaStyle')) {
        return $question;
    }

    $repaired = repairSbaStyle(formGeneratorQuestionContentSnapshot($question));
    if (!is_array($repaired)) {
        return $question;
    }

    foreach ($repaired as $key => $value) {
        if (is_string($key) && array_key_exists($key, $question)) {
            $question[$key] = $value;
        }
    }

    return $question;
}

function formGeneratorIsPlaceholderAnswerText(string $text): bool
{
    $t = trim($text);
    if ($t === '') {
        return true;
    }
    if (preg_match('/^[\-–—‐‑‒―]+$/u', $t)) {
        return true;
    }
    $lower = mb_strtolower($t);
    $placeholders = [
        'none', 'n/a', 'na', 'null', 'unknown', 'tbd', 'todo', 'not applicable',
        'not set', 'unset', 'placeholder', 'missing', 'blank', 'empty',
    ];

    return in_array($lower, $placeholders, true);
}

function formGeneratorNormalizeAnswerText(string $text): string
{
    $t = mb_strtolower(trim($text));
    $t = preg_replace('/\s+/u', ' ', $t) ?? $t;

    return trim($t);
}

function formGeneratorAnswerTextsMatch(string $a, string $b): bool
{
    $na = formGeneratorNormalizeAnswerText($a);
    $nb = formGeneratorNormalizeAnswerText($b);
    if ($na === '' || $nb === '') {
        return false;
    }
    if ($na === $nb) {
        return true;
    }

    return str_contains($na, $nb) || str_contains($nb, $na);
}

/**
 * @param array<string, mixed> $snapshot
 * @return array{ok: bool, letter: string, correct_answer: string, errors: list<string>}
 */
function formGeneratorValidateQuestionContentSnapshot(array $snapshot): array
{
    $errors = [];
    $options = [
        'A' => trim((string) ($snapshot['option_a'] ?? '')),
        'B' => trim((string) ($snapshot['option_b'] ?? '')),
        'C' => trim((string) ($snapshot['option_c'] ?? '')),
        'D' => trim((string) ($snapshot['option_d'] ?? '')),
        'E' => trim((string) ($snapshot['option_e'] ?? '')),
    ];
    foreach ($options as $optLetter => $text) {
        if ($text === '' || formGeneratorIsPlaceholderAnswerText($text)) {
            $errors[] = 'Option ' . $optLetter . ' is missing or invalid.';
        }
    }

    $letter = strtoupper(trim((string) ($snapshot['correct_answer_letter'] ?? '')));
    if (!preg_match('/^[A-E]$/', $letter)) {
        $errors[] = 'Correct answer letter must be A–E.';
    }

    $correctAnswer = trim((string) ($snapshot['correct_answer'] ?? ''));
    if (formGeneratorIsPlaceholderAnswerText($correctAnswer)) {
        $correctAnswer = '';
    }
    if ($correctAnswer === '' && preg_match('/^[A-E]$/', $letter) && isset($options[$letter])) {
        $correctAnswer = $options[$letter];
    }
    if ($correctAnswer === '' || formGeneratorIsPlaceholderAnswerText($correctAnswer)) {
        $errors[] = 'Correct answer text is missing or invalid.';
    } elseif (isset($options[$letter]) && !formGeneratorAnswerTextsMatch($correctAnswer, $options[$letter])) {
        $errors[] = 'Correct answer does not match the selected option.';
    }

    return [
        'ok' => $errors === [],
        'letter' => $letter,
        'correct_answer' => $correctAnswer,
        'errors' => $errors,
    ];
}

/**
 * @return array<string, mixed>
 */
function formGeneratorQuestionContentSnapshot(array $question): array
{
    $letter = strtoupper(trim((string) ($question['correct_answer_letter'] ?? '')));
    $options = [
        'A' => trim((string) ($question['option_a'] ?? '')),
        'B' => trim((string) ($question['option_b'] ?? '')),
        'C' => trim((string) ($question['option_c'] ?? '')),
        'D' => trim((string) ($question['option_d'] ?? '')),
        'E' => trim((string) ($question['option_e'] ?? '')),
    ];
    $correctAnswer = trim((string) ($question['correct_answer'] ?? ''));
    if (formGeneratorIsPlaceholderAnswerText($correctAnswer)) {
        $correctAnswer = '';
    }
    if ($correctAnswer === '' && preg_match('/^[A-E]$/', $letter) && isset($options[$letter])) {
        $correctAnswer = $options[$letter];
    }

    return [
        'title' => trim((string) ($question['title'] ?? '')),
        'stem' => trim((string) ($question['stem'] ?? '')),
        'lead_in' => trim((string) ($question['lead_in'] ?? '')),
        'option_a' => $options['A'],
        'option_b' => $options['B'],
        'option_c' => $options['C'],
        'option_d' => $options['D'],
        'option_e' => $options['E'],
        'correct_answer_letter' => $letter,
        'correct_answer' => $correctAnswer,
        'justification' => trim((string) ($question['justification'] ?? '')),
    ];
}

/**
 * @param array<string, mixed> $dbRow
 * @param array<string, mixed> $override
 * @return array<string, mixed>
 */
function formGeneratorMergeQuestionRowWithContentOverride(array $dbRow, array $override): array
{
    $merged = $dbRow;
    $snapshot = formGeneratorQuestionContentSnapshot($override);
    foreach ($snapshot as $key => $value) {
        $merged[$key] = $value;
    }

    return $merged;
}

/**
 * @return array<string, mixed>
 */
function formGeneratorLockedQuestionContext(array $question): array
{
    return [
        'specialty' => trim((string) ($question['specialty'] ?? '')),
        'condition_name' => trim((string) ($question['condition_name'] ?? '')),
        'presentation' => trim((string) ($question['presentation'] ?? '')),
        'skill' => trim((string) ($question['skill'] ?? '')),
        'year_level' => trim((string) ($question['year_level'] ?? '')),
    ];
}

/**
 * @param array<string, mixed> $question
 * @return list<array<string, mixed>>
 */
function formGeneratorStyleWarningsForQuestionRow(array $question, ?PDO $pdo = null): array
{
    $validatorPath = dirname(__DIR__, 2) . '/validators/style_validator.php';
    if (!is_readable($validatorPath)) {
        return [];
    }
    require_once $validatorPath;
    if (!function_exists('validateSbaStyle')) {
        return [];
    }

    $question = formGeneratorApplyStyleRepairToQuestionRow($question);
    $qStyle = $question;
    $qStyle['_tutor_comments'] = (string) ($question['tutor_comments'] ?? '');
    $qStyle['_current_skill'] = (string) ($question['skill'] ?? '');
    $qStyle['_condition_name'] = (string) ($question['condition_name'] ?? '');
    $qStyle['_batch_prior_questions'] = [];

    if ($pdo instanceof PDO) {
        $batchJobId = generatorParseBatchJobIdFromTags((string) ($question['tags'] ?? ''));
        if ($batchJobId > 0 && ($question['workspace_id'] ?? '') !== '') {
            $priors = fetchFormGeneratorPriorQuestionsInBatch(
                $pdo,
                (string) $question['workspace_id'],
                $batchJobId
            );
            $qStyle['_batch_prior_questions'] = array_values(array_filter(
                $priors,
                static fn ($row) => is_array($row)
            ));
        }
    }

    if (function_exists('repairSbaStyle')) {
        $repaired = repairSbaStyle(formGeneratorQuestionContentSnapshot($question));
        if (is_array($repaired)) {
            foreach ($repaired as $key => $value) {
                if (is_string($key)) {
                    $qStyle[$key] = $value;
                }
            }
        }
    }

    try {
        $validated = validateSbaStyle($qStyle);

        return is_array($validated) ? $validated : [];
    } catch (Throwable $e) {
        return [];
    }
}

/**
 * @return array{ok: bool, data: ?array<string, mixed>, error: string}
 */
function formGeneratorRunClinicalSafetyReviewForQuestion(
    array $question,
    array $styleWarnings = [],
    array $reviewContext = []
): array {
    $promptPath = dirname(__DIR__, 2) . '/prompts/sba_clinical_safety_reviewer_prompt.php';
    if (!is_readable($promptPath)) {
        return ['ok' => false, 'data' => null, 'error' => 'Clinical safety reviewer prompt not found.'];
    }
    require_once $promptPath;

    $prompt = buildSbaClinicalSafetyReviewerPrompt($question, $styleWarnings, $reviewContext);
    $fetch = formGeneratorOpenAiChatJsonQuestion(
        $prompt,
        '',
        0,
        0.2,
        formGeneratorOpenAiSbaSafetyReviewModel()
    );

    if (!$fetch['ok'] || !is_array($fetch['data'])) {
        $raw = trim((string) ($fetch['raw'] ?? ''));
        if ($raw !== '') {
            $logDir = dirname(__DIR__, 2) . '/logs';
            if (!is_dir($logDir)) {
                @mkdir($logDir, 0755, true);
            }
            @file_put_contents(
                $logDir . '/clinical_safety_review_raw_output.log',
                json_encode([
                    'time' => gmdate('c'),
                    'question_id' => (string) ($question['id'] ?? ''),
                    'model' => formGeneratorOpenAiSbaSafetyReviewModel(),
                    'raw' => $raw,
                ], JSON_UNESCAPED_UNICODE) . "\n",
                FILE_APPEND | LOCK_EX
            );
        }
        return [
            'ok' => false,
            'data' => null,
            'error' => $fetch['error'] !== '' ? $fetch['error'] : 'Clinical safety review failed.',
        ];
    }

    $normalized = normalizeSbaClinicalSafetyReviewerResponse($fetch['data']);
    if (!$normalized['ok'] || !is_array($normalized['data'])) {
        return [
            'ok' => false,
            'data' => null,
            'error' => $normalized['error'] !== '' ? $normalized['error'] : 'Clinical safety review returned invalid JSON.',
        ];
    }

    return ['ok' => true, 'data' => $normalized['data'], 'error' => ''];
}
