<?php

declare(strict_types=1);

/**
 * Shared anatomical tooth renderer used by the odontogram and live treatment workspace.
 * The model is intentionally diagrammatic rather than radiographic: it provides a
 * recognizable crown/root silhouette and independently colourable tooth surfaces.
 */

function dental_tooth_type(string $toothNumber): string
{
    $position = (int) substr($toothNumber, -1);
    if ($position <= 2) return 'incisor';
    if ($position === 3) return 'canine';
    if ($position <= 5 && (int) $toothNumber < 50) return 'premolar';
    return 'molar';
}

function dental_is_valid_fdi_tooth(string $toothNumber): bool
{
    if (!preg_match('/^[1-8][1-8]$/', $toothNumber)) return false;
    $quadrant = (int) $toothNumber[0];
    $position = (int) $toothNumber[1];
    return $quadrant <= 4 ? $position >= 1 && $position <= 8 : $position >= 1 && $position <= 5;
}

function dental_tooth_label(string $toothNumber): string
{
    if (!dental_is_valid_fdi_tooth($toothNumber)) return 'Tooth / region ' . $toothNumber;
    $quadrant = (int) $toothNumber[0];
    $position = (int) $toothNumber[1];
    $primary = $quadrant >= 5;
    $regions = [
        1 => 'Upper right', 2 => 'Upper left', 3 => 'Lower left', 4 => 'Lower right',
        5 => 'Upper right', 6 => 'Upper left', 7 => 'Lower left', 8 => 'Lower right',
    ];
    $permanentNames = [
        1 => 'central incisor', 2 => 'lateral incisor', 3 => 'canine', 4 => 'first premolar',
        5 => 'second premolar', 6 => 'first molar', 7 => 'second molar', 8 => 'third molar',
    ];
    $primaryNames = [
        1 => 'central incisor', 2 => 'lateral incisor', 3 => 'canine', 4 => 'first molar', 5 => 'second molar',
    ];
    $name = $primary ? ($primaryNames[$position] ?? 'primary tooth') : ($permanentNames[$position] ?? 'permanent tooth');
    return ($regions[$quadrant] ?? '') . ' ' . $name . ($primary ? ' (primary)' : ' (permanent)');
}

function dental_tooth_display_label(string $toothNumber): string
{
    if (!dental_is_valid_fdi_tooth($toothNumber)) return dental_tooth_label($toothNumber);
    return 'FDI ' . $toothNumber . ' — ' . dental_tooth_label($toothNumber);
}

function dental_tooth_is_anterior(string $toothNumber): bool
{
    if (!dental_is_valid_fdi_tooth($toothNumber)) return false;
    return (int) substr($toothNumber, -1) <= 3;
}

function dental_tooth_dentition(string $toothNumber): ?string
{
    if (!dental_is_valid_fdi_tooth($toothNumber)) return null;
    return (int) $toothNumber[0] >= 5 ? 'primary' : 'permanent';
}

function dental_extract_fdi_tooth(string $region): ?string
{
    if (preg_match('/(?<!\d)([1-8][1-8])(?!\d)/', $region, $match) && dental_is_valid_fdi_tooth((string) $match[1])) {
        return (string) $match[1];
    }
    return null;
}

function dental_surface_codes_from_text(string $surfaceText, string $toothNumber = ''): array
{
    $allowed = ['M','D','F','B','L','P','O','I','Cervical','Root','Whole'];
    $values = [];
    foreach (preg_split('/[\s,;|\/-]+/', trim($surfaceText)) ?: [] as $value) {
        if ($value === '') continue;
        $normalized = match (strtolower($value)) {
            'mesial' => 'M',
            'distal' => 'D',
            'facial', 'labial' => 'F',
            'buccal' => 'B',
            'lingual' => 'L',
            'palatal' => 'P',
            'occlusal' => 'O',
            'incisal' => 'I',
            'cervical' => 'Cervical',
            'root' => 'Root',
            'whole', 'wholetooth' => 'Whole',
            default => strlen($value) === 1 ? strtoupper($value) : $value,
        };
        if ($normalized === 'B' && dental_tooth_is_anterior($toothNumber)) $normalized = 'F';
        if ($normalized === 'F' && $toothNumber !== '' && !dental_tooth_is_anterior($toothNumber)) $normalized = 'B';
        if (in_array($normalized, $allowed, true) && !in_array($normalized, $values, true)) $values[] = $normalized;
    }
    return $values;
}

/**
 * Caries charting uses a deliberately small, tooth-specific surface set.
 * Anterior: Mesial, Distal, Facial, Lingual.
 * Posterior: Mesial, Distal, Lingual, Buccal.
 */
function dental_caries_surface_options(string $toothNumber): array
{
    if (dental_tooth_is_anterior($toothNumber)) {
        return ['M' => 'Mesial', 'D' => 'Distal', 'F' => 'Facial', 'L' => 'Lingual'];
    }
    return ['M' => 'Mesial', 'D' => 'Distal', 'L' => 'Lingual', 'B' => 'Buccal'];
}

function dental_surface_label(string $surfaceCode, string $toothNumber = ''): string
{
    $surfaceCode = trim($surfaceCode);
    if ($surfaceCode === '') return '';
    if ($surfaceCode === 'B' && dental_tooth_is_anterior($toothNumber)) return 'Facial';
    return match ($surfaceCode) {
        'M' => 'Mesial',
        'D' => 'Distal',
        'F' => 'Facial',
        'B' => 'Buccal',
        'L' => 'Lingual',
        'P' => 'Palatal',
        'O' => 'Occlusal',
        'I' => 'Incisal',
        'Cervical' => 'Cervical',
        'Root' => 'Root',
        'Whole' => 'Whole tooth',
        default => $surfaceCode,
    };
}

function dental_surface_display(string $surfaceText, string $toothNumber = ''): string
{
    $codes = array_values(array_filter(array_map('trim', explode(',', $surfaceText)), static fn(string $value): bool => $value !== ''));
    if (!$codes) return 'Whole tooth';
    $labels = [];
    foreach ($codes as $code) {
        $label = dental_surface_label($code, $toothNumber);
        if ($label !== '' && !in_array($label, $labels, true)) $labels[] = $label;
    }
    return $labels ? implode(', ', $labels) : 'Whole tooth';
}

function dental_tooth_chart_class(?array $entry): string
{
    if (!$entry) return '';
    $category = preg_replace('/[^a-z0-9_-]/', '-', strtolower((string) ($entry['category'] ?? $entry['finding_type'] ?? 'condition'))) ?: 'condition';
    $layer = preg_replace('/[^a-z0-9_-]/', '-', strtolower((string) ($entry['chart_layer'] ?? 'existing'))) ?: 'existing';
    $severity = preg_replace('/[^a-z0-9_-]/', '-', strtolower((string) ($entry['severity'] ?? 'none'))) ?: 'none';
    return 'category-' . $category . ' layer-' . $layer . ' severity-' . $severity;
}

function dental_tooth_surface_map(array $entries): array
{
    $map = [];
    foreach ($entries as $entry) {
        $surfaces = array_filter(array_map('trim', explode(',', (string) ($entry['surface'] ?? ''))));
        if (!$surfaces) $surfaces = ['Whole'];
        foreach ($surfaces as $surface) $map[$surface] = $entry;
    }
    return $map;
}

function dental_tooth_svg_geometry(string $type): array
{
    return match ($type) {
        'incisor' => [
            'root' => 'M27 8 C25 24 23 41 25 57 C27 63 37 63 39 57 C41 41 39 24 37 8 C34 3 30 3 27 8 Z',
            'crown' => 'M19 52 C23 47 41 47 45 52 L47 82 C43 91 21 91 17 82 Z',
            'center' => 'M25 60 C28 57 36 57 39 60 L40 77 C37 82 27 82 24 77 Z',
        ],
        'canine' => [
            'root' => 'M28 5 C24 28 22 44 25 59 C27 66 37 66 40 59 C42 43 39 27 36 5 C34 1 30 1 28 5 Z',
            'crown' => 'M18 55 C23 49 41 49 46 55 L44 78 L32 93 L20 78 Z',
            'center' => 'M25 61 C28 57 36 57 39 61 L38 75 L32 84 L26 75 Z',
        ],
        'premolar' => [
            'root' => 'M23 9 C22 29 21 45 24 59 C27 65 31 64 32 58 C33 64 38 65 41 59 C44 44 42 28 40 9 C35 4 28 4 23 9 Z',
            'crown' => 'M14 56 C18 47 46 47 50 56 L48 83 C43 93 21 93 16 83 Z',
            'center' => 'M23 61 C27 56 37 56 41 61 L41 78 C37 84 27 84 23 78 Z',
        ],
        default => [
            'root' => 'M16 12 C15 31 15 48 19 61 C22 66 27 64 29 57 C30 67 35 68 38 58 C40 66 46 67 49 61 C53 47 51 30 48 12 C40 5 24 5 16 12 Z',
            'crown' => 'M9 57 C12 46 52 46 55 57 L53 84 C48 96 16 96 11 84 Z',
            'center' => 'M20 61 C25 55 39 55 44 61 L44 79 C39 87 25 87 20 79 Z',
        ],
    };
}

function dental_render_tooth_model(
    string $tooth,
    array $entries,
    string $url,
    bool $selected = false,
    string $arch = 'upper',
    bool $compact = false,
    array $options = []
): string {
    $surfaceMap = dental_tooth_surface_map($entries);
    $whole = $surfaceMap['Whole'] ?? null;
    $type = dental_tooth_type($tooth);
    $geometry = dental_tooth_svg_geometry($type);
    $wholeClass = dental_tooth_chart_class($whole);
    $missing = $whole && (string) ($whole['condition_code'] ?? '') === 'missing';
    $titles = [];
    foreach ($entries as $entry) {
        $name = (string) ($entry['condition_name'] ?? ucwords(str_replace('_', ' ', (string) ($entry['condition_code'] ?? 'Finding'))));
        $surface = trim((string) ($entry['surface'] ?? ''));
        $titles[] = $name . ($surface !== '' ? ' [' . dental_surface_display($surface, $tooth) . ']' : '');
    }
    $anatomicalLabel = dental_tooth_display_label($tooth);
    $title = $anatomicalLabel . ' · ' . ($titles ? implode('; ', $titles) : 'No active finding');
    $surfaceClass = static function (array $map, array $keys): string {
        foreach ($keys as $key) if (isset($map[$key])) return dental_tooth_chart_class($map[$key]);
        return '';
    };
    $classes = ['odontogram-tooth', 'tooth-type-' . $type, 'arch-' . $arch];
    if ($selected) $classes[] = 'selected';
    if ($compact) $classes[] = 'compact';
    if ($missing) $classes[] = 'tooth-missing';
    if ($wholeClass !== '') $classes[] = $wholeClass;
    $transform = $arch === 'lower' ? 'translate(0 102) scale(1 -1)' : '';
    $interactive = !empty($options['interactive']);
    $editable = !empty($options['editable']);
    $dialogId = trim((string) ($options['dialog_id'] ?? 'finding-form'));
    $readOnlyMessage = trim((string) ($options['read_only_message'] ?? 'This tooth chart is read-only. Sign in as the assigned dentist and start treatment before recording a finding.'));
    $attributes = [];
    if ($interactive) {
        $anchorId = 'tooth-' . preg_replace('/[^A-Za-z0-9_-]/', '', $tooth);
        $attributes[] = 'id="' . e($anchorId) . '"';
        $attributes[] = 'data-chart-anchor="' . e($anchorId) . '"';
        $attributes[] = 'data-chart-tooth="' . e($tooth) . '"';
        $attributes[] = 'data-tooth-label="' . e(dental_tooth_display_label($tooth)) . '"';
        $attributes[] = 'data-tooth-editable="' . ($editable ? '1' : '0') . '"';
        if ($dialogId !== '') $attributes[] = 'data-finding-dialog="' . e($dialogId) . '"';
        if (!$editable && $readOnlyMessage !== '') $attributes[] = 'data-read-only-message="' . e($readOnlyMessage) . '"';
    }

    ob_start();
    ?>
    <a class="<?= e(implode(' ', $classes)) ?>" href="<?= e($url) ?>" title="<?= e($title) ?>" aria-label="<?= e($title) ?>" <?= implode(' ', $attributes) ?>>
        <svg viewBox="0 0 64 102" role="img" aria-hidden="true">
            <g<?= $transform !== '' ? ' transform="' . e($transform) . '"' : '' ?>>
                <path class="tooth-root tooth-whole <?= e($wholeClass) ?>" d="<?= e($geometry['root']) ?>" />
                <path class="tooth-crown tooth-whole <?= e($wholeClass) ?>" d="<?= e($geometry['crown']) ?>" />
                <path class="tooth-zone surface-b <?= e($surfaceClass($surfaceMap, ['B', 'F'])) ?>" d="M16 57 C21 50 43 50 48 57 L43 64 C38 60 26 60 21 64 Z" />
                <path class="tooth-zone surface-m <?= e($surfaceClass($surfaceMap, ['M'])) ?>" d="M16 58 L23 64 L23 79 L16 83 C13 75 13 66 16 58 Z" />
                <path class="tooth-zone surface-o <?= e($surfaceClass($surfaceMap, ['O', 'I'])) ?>" d="<?= e($geometry['center']) ?>" />
                <path class="tooth-zone surface-d <?= e($surfaceClass($surfaceMap, ['D'])) ?>" d="M48 58 L41 64 L41 79 L48 83 C51 75 51 66 48 58 Z" />
                <path class="tooth-zone surface-l <?= e($surfaceClass($surfaceMap, ['L', 'P'])) ?>" d="M21 79 C27 84 37 84 43 79 L48 83 C43 92 21 92 16 83 Z" />
                <?php if ($missing): ?><path class="tooth-missing-mark" d="M15 31 L49 84 M49 31 L15 84" /><?php endif; ?>
            </g>
        </svg>
        <span class="tooth-number"><?= e($tooth) ?></span>
        <?php if (count($entries) > 1): ?><span class="tooth-finding-count"><?= count($entries) ?></span><?php endif; ?>
    </a>
    <?php
    return (string) ob_get_clean();
}

function dental_group_active_entries_by_tooth(array $entries): array
{
    $grouped = [];
    foreach ($entries as $entry) {
        if (($entry['status'] ?? 'active') !== 'active') continue;
        $grouped[(string) ($entry['tooth_number'] ?? '')][] = $entry;
    }
    return $grouped;
}
