<?php
declare(strict_types=1);

/**
 * EnglishSpeak — Speaking Challenge
 * Save one student/teacher speaking recording.
 *
 * POST multipart/form-data:
 * - set_id
 * - word_id
 * - word
 * - recording
 */

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 speakingJson(array $payload, int $status = 200): never
{
    http_response_code($status);

    echo json_encode(
        $payload,
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES
    );

    exit;
}

function speakingSafeFilename(string $value): string
{
    $value = trim($value);
    $value = preg_replace('/[^\pL\pN_-]+/u', '-', $value) ?? '';
    $value = trim($value, '-_');

    return $value !== '' ? mb_strtolower($value) : 'word';
}

if (($_SERVER['REQUEST_METHOD'] ?? 'GET') !== 'POST') {
    speakingJson([
        '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]]
);

$wordLabel = trim((string)($_POST['word'] ?? ''));

if (!$setId || !$wordId) {
    speakingJson([
        'success' => false,
        'message' => 'Invalid Flashcard Set or Word ID.',
    ], 422);
}

if (!isset($_FILES['recording'])) {
    speakingJson([
        'success' => false,
        'message' => 'No recording was uploaded.',
    ], 422);
}

$upload = $_FILES['recording'];

if (!is_array($upload)) {
    speakingJson([
        'success' => false,
        'message' => 'Invalid recording upload.',
    ], 422);
}

$uploadError = (int)($upload['error'] ?? UPLOAD_ERR_NO_FILE);

if ($uploadError !== UPLOAD_ERR_OK) {
    $messages = [
        UPLOAD_ERR_INI_SIZE => 'The recording is larger than the server upload limit.',
        UPLOAD_ERR_FORM_SIZE => 'The recording is too large.',
        UPLOAD_ERR_PARTIAL => 'The recording upload was interrupted.',
        UPLOAD_ERR_NO_FILE => 'No recording was uploaded.',
        UPLOAD_ERR_NO_TMP_DIR => 'The server temporary folder is missing.',
        UPLOAD_ERR_CANT_WRITE => 'The server could not write the recording.',
        UPLOAD_ERR_EXTENSION => 'The upload was stopped by a server extension.',
    ];

    speakingJson([
        'success' => false,
        'message' => $messages[$uploadError] ?? 'Recording upload failed.',
    ], 422);
}

$tmpPath = (string)($upload['tmp_name'] ?? '');
$originalName = (string)($upload['name'] ?? 'recording.webm');
$fileSize = (int)($upload['size'] ?? 0);

if (
    $tmpPath === '' ||
    !is_uploaded_file($tmpPath) ||
    $fileSize <= 0
) {
    speakingJson([
        'success' => false,
        'message' => 'The uploaded recording is empty or invalid.',
    ], 422);
}

/*
 * Keep recordings short and lightweight.
 * 12 MB is more than enough for a primary-school word recording.
 */
$maxBytes = 12 * 1024 * 1024;

if ($fileSize > $maxBytes) {
    speakingJson([
        'success' => false,
        'message' => 'The recording must not exceed 12 MB.',
    ], 422);
}

$pdo = getDbConnection();

try {
    $verifyStmt = $pdo->prepare(
        'SELECT
            fs.id AS set_id,
            w.id AS word_id,
            w.word
         FROM flashcard_sets fs
         INNER JOIN flashcard_set_words fsw
            ON fsw.set_id = fs.id
         INNER JOIN words w
            ON w.id = fsw.word_id
         WHERE fs.id = ?
           AND w.id = ?
           AND w.status = ?
         LIMIT 1'
    );

    $verifyStmt->execute([
        $setId,
        $wordId,
        'active',
    ]);

    $verified = $verifyStmt->fetch(PDO::FETCH_ASSOC);

    if (!$verified) {
        speakingJson([
            'success' => false,
            'message' => 'The selected word does not belong to this Flashcard Set.',
        ], 404);
    }

    $databaseWord = trim((string)$verified['word']);

    if ($wordLabel === '') {
        $wordLabel = $databaseWord;
    }

    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $mimeType = (string)$finfo->file($tmpPath);

    $allowedMimeTypes = [
        'audio/webm' => 'webm',
        'video/webm' => 'webm',
        'audio/ogg' => 'ogg',
        'application/ogg' => 'ogg',
        'audio/mp4' => 'm4a',
        'video/mp4' => 'm4a',
        'audio/mpeg' => 'mp3',
        'audio/wav' => 'wav',
        'audio/x-wav' => 'wav',
    ];

    $extension = $allowedMimeTypes[$mimeType] ?? '';

    /*
     * Some PHP installations identify MediaRecorder blobs as
     * application/octet-stream. In that case, use the original extension,
     * but only when it is one of the supported recording formats.
     */
    if ($extension === '' && $mimeType === 'application/octet-stream') {
        $candidateExtension = strtolower(
            pathinfo($originalName, PATHINFO_EXTENSION)
        );

        $supportedExtensions = [
            'webm',
            'ogg',
            'm4a',
            'mp4',
            'mp3',
            'wav',
        ];

        if (in_array($candidateExtension, $supportedExtensions, true)) {
            $extension = $candidateExtension === 'mp4'
                ? 'm4a'
                : $candidateExtension;
        }
    }

    if ($extension === '') {
        speakingJson([
            'success' => false,
            'message' => 'Unsupported recording format.',
            'mime_type' => $mimeType,
        ], 422);
    }

    $projectRoot = dirname(__DIR__, 2);
    $relativeDirectory = sprintf(
        'uploads/speaking/%s/%s',
        date('Y'),
        date('m')
    );

    $absoluteDirectory = $projectRoot
        . DIRECTORY_SEPARATOR
        . str_replace('/', DIRECTORY_SEPARATOR, $relativeDirectory);

    if (
        !is_dir($absoluteDirectory) &&
        !mkdir($absoluteDirectory, 0775, true) &&
        !is_dir($absoluteDirectory)
    ) {
        throw new RuntimeException(
            'Unable to create the speaking recordings directory.'
        );
    }

    $safeWord = speakingSafeFilename($databaseWord);

    try {
        $randomPart = bin2hex(random_bytes(6));
    } catch (Throwable) {
        $randomPart = str_replace('.', '', uniqid('', true));
    }

    $filename = sprintf(
        'set-%d-word-%d-%s-%s.%s',
        $setId,
        $wordId,
        $safeWord,
        $randomPart,
        $extension
    );

    $absolutePath = $absoluteDirectory
        . DIRECTORY_SEPARATOR
        . $filename;

    if (!move_uploaded_file($tmpPath, $absolutePath)) {
        throw new RuntimeException(
            'Unable to move the recording into the uploads folder.'
        );
    }

    $relativePath = $relativeDirectory . '/' . $filename;

    speakingJson([
        'success' => true,
        'message' => 'Recording saved successfully.',
        'recording' => [
            'set_id' => (int)$setId,
            'word_id' => (int)$wordId,
            'word' => $databaseWord,
            'path' => $relativePath,
            'url' => es_url($relativePath),
            'mime_type' => $mimeType,
            'size' => $fileSize,
        ],
    ]);
} catch (Throwable $error) {
    error_log(
        'EnglishSpeak speaking save_recording.php: '
        . $error->getMessage()
    );

    speakingJson([
        'success' => false,
        'message' => 'Unable to save the recording on the server.',
    ], 500);
}
