<?php

declare(strict_types=1);

/**
 * Shared secure clinical imaging upload support.
 * Files remain outside the public web root and are always linked to a patient.
 */

function clinical_imaging_categories(): array
{
    return [
        'Periapical X-ray',
        'Bitewing X-ray',
        'Panoramic X-ray',
        'Occlusal X-ray',
        'Cephalometric X-ray',
        'CBCT',
        'Intraoral Photograph',
        'Extraoral Photograph',
    ];
}

function clinical_is_imaging_category(string $category): bool
{
    return in_array($category, clinical_imaging_categories(), true);
}

function clinical_is_xray_category(string $category): bool
{
    return in_array($category, [
        'Periapical X-ray',
        'Bitewing X-ray',
        'Panoramic X-ray',
        'Occlusal X-ray',
        'Cephalometric X-ray',
        'CBCT',
    ], true);
}

function clinical_validate_tooth_or_region(string $value): string
{
    $value = trim($value);
    if ($value === '') return '';
    if (mb_strlen($value) > 80) {
        throw new RuntimeException('The tooth or region label is too long.');
    }
    return $value;
}

function clinical_store_document_upload(
    PDO $pdo,
    array $file,
    int $patientId,
    ?int $encounterId,
    string $category,
    string $toothRegion = '',
    string $description = '',
    string $accessLevel = 'clinical',
    int $maxBytes = 10485760
): int {
    require_patient($patientId);
    $category = trim($category);
    if ($category === '' || mb_strlen($category) > 100) {
        throw new RuntimeException('Choose a valid imaging or document category.');
    }
    if (!in_array($accessLevel, ['clinical','staff','patient'], true)) {
        $accessLevel = 'clinical';
    }
    if (($file['error'] ?? UPLOAD_ERR_NO_FILE) !== UPLOAD_ERR_OK) {
        $code = (int)($file['error'] ?? UPLOAD_ERR_NO_FILE);
        $message = match ($code) {
            UPLOAD_ERR_INI_SIZE, UPLOAD_ERR_FORM_SIZE => 'The selected file exceeds the server upload limit.',
            UPLOAD_ERR_PARTIAL => 'The file upload was interrupted. Try again.',
            UPLOAD_ERR_NO_FILE => 'Choose an X-ray, image, or PDF to upload.',
            default => 'The clinical file could not be uploaded.',
        };
        throw new RuntimeException($message);
    }
    $size = (int)($file['size'] ?? 0);
    if ($size < 1 || $size > $maxBytes) {
        throw new RuntimeException('Clinical files must be 10 MB or smaller.');
    }
    $tmp = (string)($file['tmp_name'] ?? '');
    if ($tmp === '' || !is_file($tmp)) {
        throw new RuntimeException('The temporary upload could not be read.');
    }
    $allowed = [
        'image/jpeg' => 'jpg',
        'image/png' => 'png',
        'image/webp' => 'webp',
        'application/pdf' => 'pdf',
    ];
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $mime = (string)$finfo->file($tmp);
    if (!isset($allowed[$mime])) {
        throw new RuntimeException('Only JPG, PNG, WebP, and PDF clinical files are allowed.');
    }

    $branchId = null;
    if ($encounterId) {
        $stmt = $pdo->prepare('SELECT patient_id,branch_id FROM `' . table('clinical_encounters') . '` WHERE id=? LIMIT 1');
        $stmt->execute([$encounterId]);
        $encounter = $stmt->fetch();
        if (!$encounter || (int)$encounter['patient_id'] !== $patientId) {
            throw new RuntimeException('The selected encounter does not belong to this patient.');
        }
        $branchId = !empty($encounter['branch_id']) ? (int)$encounter['branch_id'] : null;
    }

    $toothRegion = clinical_validate_tooth_or_region($toothRegion);
    $description = trim($description);
    if (mb_strlen($description) > 6000) {
        throw new RuntimeException('The imaging description is too long.');
    }

    $stored = bin2hex(random_bytes(20)) . '.' . $allowed[$mime];
    $target = private_document_directory() . '/' . $stored;
    if (!move_uploaded_file($tmp, $target)) {
        throw new RuntimeException('The clinical file could not be stored securely.');
    }
    @chmod($target, 0640);

    try {
        $stmt = $pdo->prepare(
            'INSERT INTO `' . table('documents') . '` '
            . '(patient_id,branch_id,encounter_id,category,file_name,stored_name,mime_type,file_size,tooth_region,description,access_level,uploaded_by,created_at) '
            . 'VALUES (?,?,?,?,?,?,?,?,?,?,?,?,NOW())'
        );
        $stmt->execute([
            $patientId,
            $branchId,
            $encounterId ?: null,
            $category,
            basename((string)($file['name'] ?? ('clinical-file.' . $allowed[$mime]))),
            $stored,
            $mime,
            $size,
            $toothRegion !== '' ? $toothRegion : null,
            $description !== '' ? $description : null,
            $accessLevel,
            (int)(current_user()['id'] ?? 0) ?: null,
        ]);
        $documentId = (int)$pdo->lastInsertId();
        if (schema_column_exists(table('documents'), 'clinical_significance')) {
            $clinicalSignificance = clinical_is_xray_category($category) ? 'diagnostic' : (str_contains(strtolower($category), 'consent') ? 'consent_evidence' : 'supporting');
            $pdo->prepare('UPDATE `' . table('documents') . '` SET clinical_significance=?,captured_at=COALESCE(captured_at,created_at),review_status=IF(review_status="archived",review_status,"uploaded") WHERE id=?')
                ->execute([$clinicalSignificance,$documentId]);
        }
    } catch (Throwable $exception) {
        @unlink($target);
        throw $exception;
    }

    return isset($documentId) ? (int)$documentId : (int)$pdo->lastInsertId();
}

function clinical_recent_imaging(PDO $pdo, int $patientId, ?int $encounterId = null, int $limit = 12): array
{
    $categories = clinical_imaging_categories();
    $placeholders = implode(',', array_fill(0, count($categories), '?'));
    $sql = 'SELECT d.*,u.name uploader FROM `' . table('documents') . '` d '
        . 'LEFT JOIN `' . table('users') . '` u ON u.id=d.uploaded_by '
        . 'WHERE d.patient_id=? AND d.category IN (' . $placeholders . ')';
    $args = array_merge([$patientId], $categories);
    if ($encounterId) {
        $sql .= ' AND (d.encounter_id=? OR d.encounter_id IS NULL)';
        $args[] = $encounterId;
    }
    $sql .= ' ORDER BY d.created_at DESC,d.id DESC LIMIT ' . max(1, min(50, $limit));
    $stmt = $pdo->prepare($sql);
    $stmt->execute($args);
    return $stmt->fetchAll();
}
