<?php
function config_value(string $key, mixed $default = null): mixed
{
    $config = $GLOBALS['config'] ?? [];
    foreach (explode('.', $key) as $part) {
        if (!is_array($config) || !array_key_exists($part, $config)) {
            return $default;
        }
        $config = $config[$part];
    }
    return $config;
}

function e(?string $value): string
{
    return htmlspecialchars((string)$value, ENT_QUOTES, 'UTF-8');
}

function app_base_path(): string
{
    $script = str_replace('\\', '/', (string)($_SERVER['SCRIPT_NAME'] ?? '/index.php'));
    $dir = rtrim(str_replace('\\', '/', dirname($script)), '/');
    return $dir === '/' ? '' : $dir;
}

function url(string $route = 'home', array $params = []): string
{
    $query = array_merge(['r' => $route], $params);
    return app_base_path() . '/index.php?' . http_build_query($query);
}

function asset(string $path): string
{
    return app_base_path() . '/' . ltrim($path, '/');
}

function redirect(string $route, array $params = []): never
{
    header('Location: ' . url($route, $params));
    exit;
}

function flash(string $type, string $message): void
{
    $_SESSION['flash'][] = ['type' => $type, 'message' => $message];
}

function flashes(): array
{
    $items = $_SESSION['flash'] ?? [];
    unset($_SESSION['flash']);
    return $items;
}

function only_digits(string $value): string
{
    return preg_replace('/\D+/', '', $value) ?? '';
}


function has_forbidden_numeric_sequence(?string $value, int $minimumRun = 6): bool
{
    $digits = only_digits((string)$value);
    if ($digits === '') {
        return false;
    }

    // Bloqueia números formados por um único dígito repetido.
    if (preg_match('/^(\d)\1+$/', $digits)) {
        return true;
    }

    $length = strlen($digits);
    $minimumRun = max(4, $minimumRun);
    if ($length < $minimumRun) {
        return false;
    }

    // Bloqueia sequências crescentes/decrescentes longas, como 123456 ou 987654.
    for ($start = 0; $start <= $length - $minimumRun; $start++) {
        $ascending = true;
        $descending = true;
        for ($offset = 1; $offset < $minimumRun; $offset++) {
            $previous = (int)$digits[$start + $offset - 1];
            $current = (int)$digits[$start + $offset];
            if ($current !== (($previous + 1) % 10)) {
                $ascending = false;
            }
            if ($current !== (($previous + 9) % 10)) {
                $descending = false;
            }
        }
        if ($ascending || $descending) {
            return true;
        }
    }

    // Bloqueia blocos extensos do mesmo dígito dentro do número.
    return (bool)preg_match('/(\d)\1{' . ($minimumRun - 1) . ',}/', $digits);
}

function is_valid_cpf_basic(?string $cpf): bool
{
    $digits = only_digits((string)$cpf);
    if (strlen($digits) !== 11) {
        return false;
    }
    if (preg_match('/^(\d)\1{10}$/', $digits)) {
        return false;
    }
    return true;
}

function is_valid_cpf_strict(?string $cpf): bool
{
    $digits = only_digits((string)$cpf);
    if (strlen($digits) !== 11 || has_forbidden_numeric_sequence($digits, 6)) {
        return false;
    }

    // Validação oficial dos dois dígitos verificadores do CPF.
    for ($position = 9; $position <= 10; $position++) {
        $sum = 0;
        for ($i = 0; $i < $position; $i++) {
            $sum += ((int)$digits[$i]) * (($position + 1) - $i);
        }
        $digit = (10 * $sum) % 11;
        if ($digit === 10) {
            $digit = 0;
        }
        if ($digit !== (int)$digits[$position]) {
            return false;
        }
    }

    return true;
}

function is_valid_whatsapp_br(?string $phone): bool
{
    $digits = only_digits((string)$phone);
    if (strlen($digits) === 13 && str_starts_with($digits, '55')) {
        $digits = substr($digits, 2);
    }
    if (strlen($digits) !== 11) {
        return false;
    }
    if (preg_match('/^(\d)\1{10}$/', $digits)) {
        return false;
    }
    return true;
}

function is_valid_whatsapp_br_strict(?string $phone): bool
{
    $digits = only_digits((string)$phone);
    if (strlen($digits) === 13 && str_starts_with($digits, '55')) {
        $digits = substr($digits, 2);
    }
    if (strlen($digits) !== 11 || has_forbidden_numeric_sequence($digits, 6)) {
        return false;
    }

    $ddd = (int)substr($digits, 0, 2);
    if ($ddd < 11 || $ddd > 99) {
        return false;
    }

    // Celular brasileiro com DDD: o terceiro dígito deve ser 9.
    return $digits[2] === '9';
}

function normalize_whatsapp_br(?string $phone): string
{
    $digits = only_digits((string)$phone);
    if (strlen($digits) === 13 && str_starts_with($digits, '55')) {
        $digits = substr($digits, 2);
    }
    return $digits;
}

function format_cpf(?string $cpf): string
{
    $digits = only_digits((string)$cpf);
    if (strlen($digits) !== 11) {
        return (string)$cpf;
    }
    return substr($digits, 0, 3) . '.' . substr($digits, 3, 3) . '.' . substr($digits, 6, 3) . '-' . substr($digits, 9, 2);
}

function format_whatsapp(?string $phone): string
{
    $digits = only_digits((string)$phone);
    if (strlen($digits) === 13 && str_starts_with($digits, '55')) {
        $digits = substr($digits, 2);
    }
    if (strlen($digits) === 11) {
        return '(' . substr($digits, 0, 2) . ') ' . substr($digits, 2, 5) . '-' . substr($digits, 7, 4);
    }
    if (strlen($digits) === 10) {
        return '(' . substr($digits, 0, 2) . ') ' . substr($digits, 2, 4) . '-' . substr($digits, 6, 4);
    }
    return (string)$phone;
}

function whatsapp_url(?string $phone, string $message = ''): string
{
    $digits = only_digits((string)$phone);
    if ($digits === '') {
        return '';
    }
    if (!str_starts_with($digits, '55')) {
        $digits = '55' . $digits;
    }
    return 'https://wa.me/' . $digits . ($message !== '' ? '?text=' . rawurlencode($message) : '');
}

function weekday_pt(string $date): string
{
    $names = [
        1 => 'Seg',
        2 => 'Ter',
        3 => 'Qua',
        4 => 'Qui',
        5 => 'Sex',
        6 => 'Sab',
        7 => 'Dom',
    ];
    $n = (int)date('N', strtotime($date));
    return $names[$n] ?? '';
}

function weekday_pt_full(string $date): string
{
    $names = [
        1 => 'Segunda-feira',
        2 => 'Terca-feira',
        3 => 'Quarta-feira',
        4 => 'Quinta-feira',
        5 => 'Sexta-feira',
        6 => 'Sabado',
        7 => 'Domingo',
    ];
    $n = (int)date('N', strtotime($date));
    return $names[$n] ?? '';
}


function normalize_lesson_color(?string $color): string
{
    $color = strtolower(trim((string)$color));
    return in_array($color, ['blue', 'red', 'green'], true) ? $color : 'blue';
}

function lesson_color_label(?string $color): string
{
    return match (normalize_lesson_color($color)) {
        'red' => 'Conclusão',
        'green' => 'Sem Monitorar',
        default => 'Monitorada',
    };
}

function lesson_color_badge_class(?string $color): string
{
    return match (normalize_lesson_color($color)) {
        'red' => 'text-bg-danger',
        'green' => 'text-bg-success',
        default => 'text-bg-primary',
    };
}

function lesson_color_row_class(?string $color): string
{
    return match (normalize_lesson_color($color)) {
        'red' => 'lesson-row-red',
        'green' => 'lesson-row-green',
        default => 'lesson-row-blue',
    };
}


function student_learning_category_options(): array
{
    return [
        'geral' => 'Avaliação geral',
        'direcao' => 'Direção e controle do veículo',
        'baliza' => 'Baliza e estacionamento',
        'transito' => 'Trânsito e sinalização',
        'seguranca' => 'Segurança e atenção',
        'exame' => 'Preparação para o exame',
    ];
}

function student_learning_category_label(?string $category): string
{
    $options = student_learning_category_options();
    $key = trim((string)$category);
    return $options[$key] ?? ($key !== '' ? $key : 'Categoria');
}

function student_level_options(): array
{
    return [
        '' => 'Sem nível definido',
        'iniciante' => 'Iniciante',
        'intermediario' => 'Intermediário',
        'avancado' => 'Avançado',
        'pronto_exame' => 'Pronto para exame',
        'atencao' => 'Atenção especial',
    ];
}

function student_level_label(?string $level): string
{
    $options = student_level_options();
    $key = trim((string)$level);
    return $options[$key] ?? ($key !== '' ? $key : 'Sem nível definido');
}

function app_config(): array
{
    return $GLOBALS['config'] ?? [];
}


function format_money_br(int|float|string|null $value): string
{
    return 'R$ ' . number_format((float)$value, 2, ',', '.');
}

function parse_money_br(int|float|string|null $value): float
{
    if (is_int($value) || is_float($value)) {
        return max(0, (float)$value);
    }
    $raw = trim((string)$value);
    if ($raw === '') {
        return 0.0;
    }
    $raw = preg_replace('/[^0-9,.-]/', '', $raw) ?? '';
    if (str_contains($raw, ',')) {
        $raw = str_replace('.', '', $raw);
        $raw = str_replace(',', '.', $raw);
    }
    return max(0, (float)$raw);
}

function is_valid_http_url(?string $value): bool
{
    $url = trim((string)$value);
    if ($url === '' || filter_var($url, FILTER_VALIDATE_URL) === false) {
        return false;
    }
    return in_array(strtolower((string)parse_url($url, PHP_URL_SCHEME)), ['http', 'https'], true);
}

function normalize_http_url(?string $value): string
{
    $url = trim((string)$value);
    if ($url === '') {
        return '';
    }
    if (!preg_match('~^https?://~i', $url)) {
        $url = 'https://' . ltrim($url, '/');
    }
    return is_valid_http_url($url) ? $url : '';
}

function license_process_options(): array
{
    return [
        'first_license' => 'Primeira habilitação',
        'addition' => 'Adição de categoria',
        'classification' => 'Classificação / mudança de categoria',
    ];
}

function license_process_label(?string $process): string
{
    $options = license_process_options();
    $key = trim((string)$process);
    return $options[$key] ?? 'Não definido';
}

function driving_category_options(): array
{
    return [
        'A' => 'A - Motocicleta',
        'B' => 'B - Automóvel',
        'C' => 'C - Caminhão',
        'D' => 'D - Ônibus / Micro-ônibus',
        'E' => 'E - Carreta / Articulado',
    ];
}

function normalize_driving_category(?string $category): string
{
    $category = strtoupper(trim((string)$category));
    return array_key_exists($category, driving_category_options()) ? $category : '';
}

function training_content_type_label(?string $type): string
{
    return match (trim((string)$type)) {
        'video' => 'Videoaula',
        'manual' => 'Livro / Manual',
        default => 'E-book',
    };
}

function training_order_status_label(?string $status): string
{
    return match (trim((string)$status)) {
        'awaiting_payment' => 'Aguardando pagamento',
        'proof_sent' => 'Comprovante enviado',
        'approved' => 'Pagamento aprovado / liberado',
        'granted' => 'Liberado pelo administrador',
        'rejected' => 'Comprovante recusado',
        default => 'Solicitação pendente',
    };
}

function training_order_status_badge(?string $status): string
{
    return match (trim((string)$status)) {
        'approved', 'granted' => 'text-bg-success',
        'proof_sent' => 'text-bg-info',
        'rejected' => 'text-bg-danger',
        'awaiting_payment' => 'text-bg-warning text-dark',
        default => 'text-bg-secondary',
    };
}


function save_cover_image_upload(array $file, string $folder): array
{
    $error = (int)($file['error'] ?? UPLOAD_ERR_NO_FILE);
    if ($error === UPLOAD_ERR_NO_FILE || empty($file['tmp_name'])) {
        return ['ok' => true, 'path' => null, 'mime' => null, 'message' => ''];
    }
    if ($error !== UPLOAD_ERR_OK) {
        return ['ok' => false, 'path' => null, 'mime' => null, 'message' => 'Não foi possível receber a imagem da capa.'];
    }
    $size = (int)($file['size'] ?? 0);
    if ($size <= 0 || $size > 5 * 1024 * 1024) {
        return ['ok' => false, 'path' => null, 'mime' => null, 'message' => 'A imagem da capa deve ter no máximo 5 MB.'];
    }
    $finfo = new finfo(FILEINFO_MIME_TYPE);
    $mime = (string)($finfo->file((string)$file['tmp_name']) ?: '');
    $allowed = [
        'image/jpeg' => 'jpg',
        'image/png' => 'png',
        'image/webp' => 'webp',
    ];
    if (!isset($allowed[$mime])) {
        return ['ok' => false, 'path' => null, 'mime' => null, 'message' => 'A capa deve estar em JPG, PNG ou WEBP.'];
    }
    $safeFolder = trim(preg_replace('/[^a-zA-Z0-9_-]+/', '', $folder) ?? '', '/');
    if ($safeFolder === '') {
        $safeFolder = 'geral';
    }
    $relativeDir = 'covers/' . $safeFolder;
    $absoluteDir = STORAGE_PATH . '/' . $relativeDir;
    if (!is_dir($absoluteDir) && !mkdir($absoluteDir, 0775, true) && !is_dir($absoluteDir)) {
        return ['ok' => false, 'path' => null, 'mime' => null, 'message' => 'Não foi possível criar a pasta das imagens.'];
    }
    $filename = 'capa_' . date('Ymd_His') . '_' . bin2hex(random_bytes(8)) . '.' . $allowed[$mime];
    $destination = $absoluteDir . '/' . $filename;
    if (!move_uploaded_file((string)$file['tmp_name'], $destination)) {
        return ['ok' => false, 'path' => null, 'mime' => null, 'message' => 'Não foi possível salvar a imagem da capa.'];
    }
    return ['ok' => true, 'path' => $relativeDir . '/' . $filename, 'mime' => $mime, 'message' => ''];
}

function delete_storage_file(?string $relativePath): void
{
    $relativePath = trim((string)$relativePath);
    if ($relativePath === '') {
        return;
    }
    if (str_starts_with($relativePath, 'storage/')) {
        $path = ROOT_PATH . '/' . ltrim($relativePath, '/');
    } else {
        $path = STORAGE_PATH . '/' . ltrim($relativePath, '/');
    }
    if (is_file($path)) {
        @unlink($path);
    }
}

function stream_storage_image(?string $relativePath, ?string $mimeType = null): never
{
    $relativePath = trim((string)$relativePath);
    $path = STORAGE_PATH . '/' . ltrim($relativePath, '/');
    if ($relativePath === '' || !is_file($path)) {
        http_response_code(404);
        exit('Imagem não encontrada.');
    }
    $mime = trim((string)$mimeType);
    if ($mime === '') {
        $finfo = new finfo(FILEINFO_MIME_TYPE);
        $mime = (string)($finfo->file($path) ?: 'application/octet-stream');
    }
    header('Content-Type: ' . $mime);
    header('Content-Length: ' . filesize($path));
    header('Cache-Control: public, max-age=86400');
    header('X-Content-Type-Options: nosniff');
    readfile($path);
    exit;
}

function contact_folder_options(): array
{
    return [
        'alunos' => 'Alunos',
        'trabalho' => 'Trabalho',
        'outros' => 'Outros',
    ];
}

function normalize_contact_folder(?string $folder): string
{
    $folder = strtolower(trim((string)$folder));
    return array_key_exists($folder, contact_folder_options()) ? $folder : 'outros';
}

function contact_folder_label(?string $folder): string
{
    $folder = normalize_contact_folder($folder);
    return contact_folder_options()[$folder];
}

function contact_folder_icon(?string $folder): string
{
    return match (normalize_contact_folder($folder)) {
        'alunos' => 'bi-people-fill',
        'trabalho' => 'bi-briefcase-fill',
        default => 'bi-folder-fill',
    };
}

function short_person_name(?string $name): string
{
    $parts = preg_split('/\s+/', trim((string)$name)) ?: [];
    $parts = array_values(array_filter($parts, static fn($part) => $part !== ''));
    if (!$parts) {
        return 'Usuário';
    }
    if (count($parts) === 1) {
        return $parts[0];
    }
    return $parts[0] . ' ' . $parts[count($parts) - 1];
}

function internal_actor_label(?string $type): string
{
    return match (trim((string)$type)) {
        'admin' => 'Administração',
        'professional' => 'Instrutor',
        'beginner' => 'Formação',
        'group' => 'Grupo teórico',
        default => 'Aluno',
    };
}

function normalize_internal_actor_type(?string $type): string
{
    $type = trim((string)$type);
    return in_array($type, ['admin', 'student', 'professional', 'beginner'], true) ? $type : 'student';
}

function video_embed_url(?string $url): string
{
    $url = trim((string)$url);
    if ($url === '') {
        return '';
    }
    if (preg_match('~(?:youtube\.com/watch\?v=|youtu\.be/|youtube\.com/shorts/)([A-Za-z0-9_-]{6,})~i', $url, $m)) {
        return 'https://www.youtube.com/embed/' . $m[1];
    }
    if (preg_match('~vimeo\.com/(?:video/)?(\d+)~i', $url, $m)) {
        return 'https://player.vimeo.com/video/' . $m[1];
    }
    return '';
}

function is_direct_video_url(?string $url): bool
{
    $path = strtolower((string)parse_url(trim((string)$url), PHP_URL_PATH));
    return (bool)preg_match('/\.(mp4|webm|ogg|m4v)$/', $path);
}
