<?php
declare(strict_types=1);

require __DIR__ . '/../partials/auth.php';
require_once __DIR__ . '/../../config/database.php';

header('Content-Type: application/json; charset=utf-8');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');

function assessmentJson(array $payload, int $status = 200): never
{
    http_response_code($status);
    echo json_encode(
        $payload,
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );
    exit;
}

function assessmentNumber(mixed $value): float
{
    return is_numeric($value) ? (float)$value : 0.0;
}

function finalIpaSound(string $phonetic): string
{
    $value = trim($phonetic);
    $value = trim($value, "/[] \t\n\r\0\x0B");
    $value = preg_replace('/[ˈˌ. ]+/u', '', $value) ?? $value;

    if ($value === '') {
        return '';
    }

    foreach (['tʃ', 'dʒ'] as $sound) {
        if (str_ends_with($value, $sound)) {
            return $sound;
        }
    }

    preg_match_all('/./us', $value, $characters);
    $parts = $characters[0] ?? [];

    if (!$parts) {
        return '';
    }

    $last = array_pop($parts);

    if ($last === 'ː' && $parts) {
        $last = array_pop($parts) . 'ː';
    }

    return (string)$last;
}

function buildStudentFeedback(
    float $score,
    float $finalScore,
    string $finalSound,
    float $lowestScore,
    string $lowestPhoneme
): array {
    if ($score >= 90 && $finalScore >= 75) {
        return [
            'praise' => 'Rất tốt!',
            'feedback' => 'Em phát âm rõ và tự tin.',
            'tip' => 'Tiếp tục giữ cách đọc này nhé.',
            'focus' => 'none',
        ];
    }

    if ($finalSound !== '' && $finalScore > 0 && $finalScore < 75) {
        return [
            'praise' => $score >= 70 ? 'Em đọc khá tốt.' : 'Em đã cố gắng.',
            'feedback' => "Âm cuối /{$finalSound}/, đọc cho rõ âm nhé.",
            'tip' => 'Nghe mẫu rồi ghi âm lại một lần nữa.',
            'focus' => 'final',
        ];
    }

    if ($lowestPhoneme !== '' && $lowestScore < 70) {
        return [
            'praise' => $score >= 70 ? 'Em đọc khá tốt.' : 'Em đã cố gắng.',
            'feedback' => "Âm /{$lowestPhoneme}/, nghe lại và đọc cho rõ nhé.",
            'tip' => 'Thử đọc chậm hơn một chút.',
            'focus' => 'phoneme',
        ];
    }

    return [
        'praise' => $score >= 75 ? 'Tốt lắm!' : 'Em đã hoàn thành.',
        'feedback' => 'Nghe lại và thử đọc rõ hơn một chút nhé.',
        'tip' => 'Ghi âm lại để cải thiện điểm phát âm.',
        'focus' => 'general',
    ];
}

if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
    assessmentJson([
        'success' => false,
        'message' => 'Method not allowed.',
    ], 405);
}

$setId = filter_input(
    INPUT_POST,
    'set_id',
    FILTER_VALIDATE_INT,
    ['options' => ['min_range' => 1]]
);
$wordId = filter_input(
    INPUT_POST,
    'word_id',
    FILTER_VALIDATE_INT,
    ['options' => ['min_range' => 1]]
);

if (!$setId || !$wordId || !isset($_FILES['recording'])) {
    assessmentJson([
        'success' => false,
        'message' => 'Thiếu dữ liệu chấm phát âm.',
    ], 422);
}

$upload = $_FILES['recording'];

if (
    !is_array($upload) ||
    (int)($upload['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK
) {
    assessmentJson([
        'success' => false,
        'message' => 'File ghi âm không hợp lệ.',
    ], 422);
}

$tmpPath = (string)($upload['tmp_name'] ?? '');
$fileSize = (int)($upload['size'] ?? 0);

if (
    $tmpPath === '' ||
    !is_uploaded_file($tmpPath) ||
    $fileSize <= 44 ||
    $fileSize > 5 * 1024 * 1024
) {
    assessmentJson([
        'success' => false,
        'message' => 'File ghi âm trống hoặc quá lớn.',
    ], 422);
}

$configPath = __DIR__ . '/../../config/azure_speech.php';

if (!is_file($configPath)) {
    assessmentJson([
        'success' => false,
        'message' => 'Chưa có cấu hình Azure Speech.',
    ], 500);
}

$config = require $configPath;
$key = trim((string)($config['key'] ?? ''));
$region = strtolower(trim((string)($config['region'] ?? '')));

if ($key === '' || $region === '') {
    assessmentJson([
        'success' => false,
        'message' => 'Azure Speech Key hoặc Region chưa đúng.',
    ], 500);
}

$pdo = getDbConnection();

$wordStmt = $pdo->prepare(
    'SELECT w.word, w.phonetic
     FROM flashcard_set_words fsw
     INNER JOIN words w ON w.id = fsw.word_id
     WHERE fsw.set_id = ?
       AND w.id = ?
       AND w.status = ?
     LIMIT 1'
);
$wordStmt->execute([$setId, $wordId, 'active']);
$wordRow = $wordStmt->fetch(PDO::FETCH_ASSOC);

if (!$wordRow) {
    assessmentJson([
        'success' => false,
        'message' => 'Từ không thuộc Flashcard Set này.',
    ], 404);
}

$referenceText = trim((string)$wordRow['word']);
$phonetic = trim((string)($wordRow['phonetic'] ?? ''));

$parameters = [
    'ReferenceText' => $referenceText,
    'GradingSystem' => 'HundredMark',
    'Granularity' => 'Phoneme',
    'Dimension' => 'Comprehensive',
    'EnableMiscue' => true,
];

$assessmentHeader = base64_encode(
    json_encode(
        $parameters,
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    )
);

$url = sprintf(
    'https://%s.stt.speech.microsoft.com/speech/recognition/conversation/cognitiveservices/v1?language=en-US&format=detailed',
    rawurlencode($region)
);

$audio = file_get_contents($tmpPath);

if ($audio === false) {
    assessmentJson([
        'success' => false,
        'message' => 'Không đọc được file ghi âm.',
    ], 500);
}

$curl = curl_init($url);

if ($curl === false) {
    assessmentJson([
        'success' => false,
        'message' => 'Máy chủ chưa hỗ trợ kết nối Azure.',
    ], 500);
}

curl_setopt_array($curl, [
    CURLOPT_POST => true,
    CURLOPT_POSTFIELDS => $audio,
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_CONNECTTIMEOUT => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTPHEADER => [
        'Accept: application/json',
        'Content-Type: audio/wav; codecs=audio/pcm; samplerate=16000',
        'Ocp-Apim-Subscription-Key: ' . $key,
        'Pronunciation-Assessment: ' . $assessmentHeader,
    ],
]);

$responseBody = curl_exec($curl);
$curlError = curl_error($curl);
$statusCode = (int)curl_getinfo($curl, CURLINFO_HTTP_CODE);
curl_close($curl);

if ($responseBody === false || $curlError !== '') {
    error_log('Azure Speech cURL error: ' . $curlError);

    assessmentJson([
        'success' => false,
        'message' => 'Không kết nối được Azure Speech.',
    ], 502);
}

$result = json_decode((string)$responseBody, true);

if (!is_array($result)) {
    error_log('Azure Speech invalid JSON: ' . (string)$responseBody);

    assessmentJson([
        'success' => false,
        'message' => 'Azure trả dữ liệu không hợp lệ.',
    ], 502);
}

if ($statusCode !== 200) {
    error_log(
        'Azure Speech HTTP ' . $statusCode . ': ' . (string)$responseBody
    );

    assessmentJson([
        'success' => false,
        'message' => $statusCode === 401
            ? 'Azure Speech Key hoặc Region không đúng.'
            : 'Azure Speech chưa chấm được bản ghi này.',
    ], 502);
}

if (($result['RecognitionStatus'] ?? '') !== 'Success') {
    assessmentJson([
        'success' => false,
        'message' => 'Chưa nghe rõ. Em hãy nói gần micro hơn nhé.',
    ], 422);
}

$best = $result['NBest'][0] ?? null;

if (!is_array($best)) {
    assessmentJson([
        'success' => false,
        'message' => 'Azure chưa trả kết quả phát âm.',
    ], 502);
}

$score = assessmentNumber(
    $best['PronScore'] ?? $best['AccuracyScore'] ?? 0
);
$accuracy = assessmentNumber($best['AccuracyScore'] ?? 0);
$fluency = assessmentNumber($best['FluencyScore'] ?? 0);
$completeness = assessmentNumber($best['CompletenessScore'] ?? 0);

$words = is_array($best['Words'] ?? null) ? $best['Words'] : [];
$firstWord = is_array($words[0] ?? null) ? $words[0] : [];
$phonemes = is_array($firstWord['Phonemes'] ?? null)
    ? $firstWord['Phonemes']
    : [];

$lowestScore = 101.0;
$lowestPhoneme = '';
$finalScore = 0.0;

foreach ($phonemes as $index => $phonemeRow) {
    if (!is_array($phonemeRow)) {
        continue;
    }

    $phonemeName = trim((string)($phonemeRow['Phoneme'] ?? ''));
    $phonemeScore = assessmentNumber(
        $phonemeRow['AccuracyScore'] ?? 0
    );

    if ($phonemeScore < $lowestScore) {
        $lowestScore = $phonemeScore;
        $lowestPhoneme = $phonemeName;
    }

    if ($index === array_key_last($phonemes)) {
        $finalScore = $phonemeScore;
    }
}

if ($lowestScore > 100) {
    $lowestScore = 0;
}

$finalSound = finalIpaSound($phonetic);
$feedback = buildStudentFeedback(
    $score,
    $finalScore,
    $finalSound,
    $lowestScore,
    $lowestPhoneme
);

assessmentJson([
    'success' => true,
    'assessment' => [
        'score' => round($score, 1),
        'accuracy' => round($accuracy, 1),
        'fluency' => round($fluency, 1),
        'completeness' => round($completeness, 1),
        'recognized_text' => (string)($result['DisplayText'] ?? ''),
        'praise' => $feedback['praise'],
        'feedback' => $feedback['feedback'],
        'tip' => $feedback['tip'],
        'focus' => $feedback['focus'],
        'final_sound' => $finalSound,
        'final_score' => round($finalScore, 1),
    ],
]);
