<?php

declare(strict_types=1);

/**
 * Safe tutor-facing messages and server-side logging for combined AI tutor review failures.
 * Does not change review logic — classifies failures for the API response only.
 */

/** @var array<string, string> */
const COMBINED_TUTOR_REVIEW_TUTOR_MESSAGES = [
    'timeout' => 'AI review timed out. Original draft unchanged.',
    'incomplete_response' => 'AI review returned an incomplete response. Original draft unchanged.',
    'schema_validation' => 'AI review returned an incomplete response. Original draft unchanged.',
    'suggestions_inconsistent' => 'AI suggestions were blocked because they were inconsistent. Original draft unchanged.',
    'generic' => 'AI review failed. Original draft unchanged.',
];

/**
 * @param array<string, mixed> $context
 */
function combinedTutorReviewLogFailure(string $questionId, string $errorCode, string $detail, array $context = []): void
{
    $payload = array_merge(
        [
            'time' => gmdate('c'),
            'question_id' => $questionId,
            'error_code' => $errorCode,
            'detail' => $detail,
        ],
        $context
    );

    $line = '[combined_tutor_review] ' . json_encode($payload, JSON_UNESCAPED_UNICODE);
    error_log($line);

    $logDir = dirname(__DIR__, 2) . '/logs';
    if (!is_dir($logDir)) {
        @mkdir($logDir, 0755, true);
    }
    if (is_dir($logDir) && is_writable($logDir)) {
        @file_put_contents(
            $logDir . '/combined_tutor_review_errors.log',
            $line . PHP_EOL,
            FILE_APPEND | LOCK_EX
        );
    }
}

function combinedTutorReviewTutorMessageForCode(string $errorCode): string
{
    return COMBINED_TUTOR_REVIEW_TUTOR_MESSAGES[$errorCode]
        ?? COMBINED_TUTOR_REVIEW_TUTOR_MESSAGES['generic'];
}

/**
 * @param array<string, mixed> $fetch formGeneratorOpenAiChatJsonQuestion result
 * @return array{error_code: string, tutor_message: string, detail: string}
 */
function combinedTutorReviewFailureFromOpenAiFetch(array $fetch, string $questionId): array
{
    $detail = trim((string) ($fetch['error'] ?? ''));
    if ($detail === '') {
        $detail = 'The review service could not complete.';
    }

    $timedOut = !empty($fetch['timed_out']);
    $httpStatus = (int) ($fetch['http_status'] ?? 0);
    $responseLen = (int) ($fetch['response_len'] ?? 0);
    $rawLen = is_string($fetch['raw'] ?? null) ? strlen((string) $fetch['raw']) : 0;
    $lower = mb_strtolower($detail);

    $errorCode = 'generic';
    if ($timedOut || str_contains($lower, 'timed out') || str_contains($lower, 'timeout')) {
        $errorCode = 'timeout';
    } elseif (
        str_contains($lower, 'did not return valid json')
        || str_contains($lower, 'invalid json')
        || ($rawLen === 0 && $responseLen === 0 && $httpStatus >= 200 && $httpStatus < 300)
        || ($rawLen > 0 && $rawLen < 40)
    ) {
        $errorCode = 'incomplete_response';
    } elseif ($httpStatus === 429 || str_contains($lower, 'rate limit')) {
        $errorCode = 'timeout';
    }

    combinedTutorReviewLogFailure($questionId, $errorCode, $detail, [
        'phase' => 'openai',
        'timed_out' => $timedOut,
        'http_status' => $httpStatus,
        'response_len' => $responseLen,
        'raw_len' => $rawLen,
    ]);

    return [
        'error_code' => $errorCode,
        'tutor_message' => combinedTutorReviewTutorMessageForCode($errorCode),
        'detail' => $detail,
    ];
}

/**
 * @return array{error_code: string, tutor_message: string, detail: string}
 */
function combinedTutorReviewFailureFromNormalize(string $normalizeError, string $questionId, int $rawLen = 0): array
{
    $detail = trim($normalizeError);
    if ($detail === '') {
        $detail = 'Review returned JSON that could not be validated.';
    }

    $lower = mb_strtolower($detail);
    $errorCode = 'schema_validation';

    if (
        str_contains($lower, 'correct answer')
        || str_contains($lower, 'placeholder')
        || str_contains($lower, 'inconsistent')
        || str_contains($lower, 'duplicate')
        || str_contains($lower, 'overlap')
        || str_contains($lower, 'suggested edits')
    ) {
        $errorCode = 'suggestions_inconsistent';
    } elseif (str_contains($lower, 'missing summary') || str_contains($lower, 'incomplete')) {
        $errorCode = 'incomplete_response';
    }

    combinedTutorReviewLogFailure($questionId, $errorCode, $detail, [
        'phase' => 'normalize',
        'raw_len' => $rawLen,
    ]);

    return [
        'error_code' => $errorCode,
        'tutor_message' => combinedTutorReviewTutorMessageForCode($errorCode),
        'detail' => $detail,
    ];
}

/**
 * @return array{error_code: string, tutor_message: string, detail: string}
 */
function combinedTutorReviewFailureFromException(Throwable $e, string $questionId): array
{
    $detail = trim($e->getMessage());
    if ($detail === '') {
        $detail = 'An unexpected error occurred.';
    }

    combinedTutorReviewLogFailure($questionId, 'generic', $detail, [
        'phase' => 'exception',
        'exception' => $e::class,
    ]);

    return [
        'error_code' => 'generic',
        'tutor_message' => combinedTutorReviewTutorMessageForCode('generic'),
        'detail' => $detail,
    ];
}

/**
 * @return array{error_code: string, tutor_message: string, detail: string}
 */
function combinedTutorReviewFailureFromClientDetail(string $detail, string $questionId): array
{
    $detail = trim($detail);
    if ($detail === '') {
        $detail = 'Request could not be processed.';
    }

    combinedTutorReviewLogFailure($questionId, 'generic', $detail, ['phase' => 'request']);

    return [
        'error_code' => 'generic',
        'tutor_message' => combinedTutorReviewTutorMessageForCode('generic'),
        'detail' => $detail,
    ];
}

/**
 * @param array<string, mixed> $failure
 * @return array<string, mixed>
 */
function combinedTutorReviewErrorJsonPayload(array $failure, int $httpCode = 502): array
{
    return [
        'ok' => false,
        'error' => 'AI review failed',
        'error_code' => (string) ($failure['error_code'] ?? 'generic'),
        'tutor_message' => (string) ($failure['tutor_message'] ?? combinedTutorReviewTutorMessageForCode('generic')),
        'detail' => (string) ($failure['detail'] ?? ''),
        'review' => null,
        'status' => 'error',
    ];
}
