<?php
/**
 * Fonctions utilitaires globales
 */

function formatDate($date) {
    if (!$date || $date === '0000-00-00') return '-';
    $d = new DateTime($date);
    $months = ['','Janvier','Février','Mars','Avril','Mai','Juin','Juillet','Août','Septembre','Octobre','Novembre','Décembre'];
    return $d->format('j') . ' ' . $months[(int)$d->format('n')] . ' ' . $d->format('Y');
}

function formatAmount($amount) {
    if ($amount === null) return '-';
    return number_format((float)$amount, 0, ',', ' ') . ' FCFA';
}

function getRoleLabel($role) {
    $labels = [
        'super_admin' => 'Super Admin',
        'school_admin' => 'Admin École',
        'teacher' => 'Enseignant',
        'accountant' => 'Comptable',
        'parent' => 'Parent',
        'director' => 'Directeur',
    ];
    return $labels[$role] ?? ucfirst($role);
}

function getRoleBadgeClass($role) {
    $classes = [
        'super_admin' => 'badge-danger',
        'school_admin' => 'badge-primary',
        'teacher' => 'badge-success',
        'accountant' => 'badge-warning',
        'parent' => 'badge-info',
        'director' => 'badge-secondary',
    ];
    return $classes[$role] ?? 'badge-light';
}

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

function showFlash() {
    if (empty($_SESSION['flash'])) return '';
    $f = $_SESSION['flash'];
    unset($_SESSION['flash']);
    $icons = ['success' => 'check-circle', 'error' => 'times-circle', 'warning' => 'exclamation-triangle', 'info' => 'info-circle'];
    $icon = $icons[$f['type']] ?? 'info-circle';
    return '<div class="toast toast-' . $f['type'] . ' show" role="alert"><i class="fas fa-' . $icon . '"></i> ' . htmlspecialchars($f['message'], ENT_QUOTES, 'UTF-8') . '<button type="button" onclick="this.parentElement.remove()">&times;</button></div>';
}

function csrfToken(): string {
    if (empty($_SESSION['csrf_token'])) {
        $_SESSION['csrf_token'] = bin2hex(random_bytes(32));
    }
    return $_SESSION['csrf_token'];
}

function csrfField(): string {
    return '<input type="hidden" name="csrf_token" value="' . csrfToken() . '">';
}

function verifyCsrf(): void {
    $submitted = $_POST['csrf_token'] ?? ($_GET['csrf_token'] ?? '');
    if (empty($submitted) || !hash_equals($_SESSION['csrf_token'] ?? '', $submitted)) {
        http_response_code(403);
        exit('Action non autorisée.');
    }
}

function checkLoginAttempts(): bool {
    $key = 'login_attempts_' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
    $attempts = $_SESSION[$key]['count'] ?? 0;
    $last = $_SESSION[$key]['time'] ?? 0;
    if ($attempts >= 5) {
        if (time() - $last < 900) return false;
        resetLoginAttempts();
    }
    return true;
}

function recordFailedLogin(): void {
    $key = 'login_attempts_' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
    $_SESSION[$key]['count'] = ($_SESSION[$key]['count'] ?? 0) + 1;
    $_SESSION[$key]['time'] = time();
}

function resetLoginAttempts(): void {
    $key = 'login_attempts_' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
    unset($_SESSION[$key]);
}

function remainingLockTime(): int {
    $key = 'login_attempts_' . ($_SERVER['REMOTE_ADDR'] ?? 'unknown');
    $last = $_SESSION[$key]['time'] ?? 0;
    return max(0, 900 - (time() - $last));
}

function validateEmail(string $email): bool {
    return filter_var($email, FILTER_VALIDATE_EMAIL) !== false;
}

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

function resolveSort(array $allowed, string $default = 'created_at', string $defDir = 'desc'): array {
    $col = $_GET['sort'] ?? $default;
    if (!in_array($col, $allowed)) $col = $default;
    $dir = strtoupper($_GET['dir'] ?? $defDir);
    if (!in_array($dir, ['ASC','DESC'])) $dir = $defDir;
    return ['col' => $col, 'dir' => $dir];
}

function sortUrl(string $col, array $current): string {
    $qs = $_GET;
    $qs['sort'] = $col;
    $qs['dir'] = ($current['col'] === $col && $current['dir'] === 'ASC') ? 'DESC' : 'ASC';
    return '?' . http_build_query($qs);
}

function sortIcon(string $col, array $current): string {
    if ($current['col'] !== $col) return '<i class="fas fa-sort" style="opacity:.3"></i>';
    return $current['dir'] === 'ASC' ? '<i class="fas fa-sort-up"></i>' : '<i class="fas fa-sort-down"></i>';
}

function sortTh(string $col, string $label, array $current): string {
    return '<a href="' . sortUrl($col, $current) . '" class="sort-th">' . $label . ' ' . sortIcon($col, $current) . '</a>';
}

function logoUrl(?string $filename): string {
    if (empty($filename)) return '';
    return BASE_URL . 'uploads/logos/' . rawurlencode($filename);
}

function hasPermission(string $page, string $action = 'view'): bool {
    $role = $_SESSION['user_role'] ?? '';
    if ($role === 'super_admin' || $role === 'school_admin') return true;
    static $permCache = null;
    if ($permCache === null) {
        $permCache = $_SESSION['_permissions'] ?? [];
        if (empty($permCache) && $role) {
            global $pdo;
            $st = $pdo->prepare("SELECT id FROM roles WHERE name=?");
            $st->execute([$role]);
            $rid = $st->fetchColumn();
            if ($rid) {
                $st2 = $pdo->prepare("SELECT page, actions FROM role_permissions WHERE role_id=?");
                $st2->execute([$rid]);
                while ($r = $st2->fetch()) $permCache[$r['page']] = json_decode($r['actions'] ?? '[]', true);
            }
            $_SESSION['_permissions'] = $permCache;
        }
    }
    $actions = $permCache[$page] ?? [];
    return in_array($action, $actions);
}

function clearPermissionCache(?string $role = null): void {
    if ($role === null) unset($_SESSION['_permissions']);
    else $_SESSION['_permissions'] = [];
}

function isAdminRole(string $role): bool {
    return in_array($role, ['super_admin','school_admin','director','accountant']);
}

function isPortalRole(string $role): bool {
    return in_array($role, ['teacher','parent','director']);
}

function getAllRoleLabels(): array {
    return [
        'super_admin' => 'Super Admin',
        'school_admin' => 'Admin École',
        'teacher' => 'Enseignant',
        'accountant' => 'Comptable',
        'parent' => 'Parent',
        'director' => 'Directeur',
    ];
}

function getAllRoleBadgeClasses(): array {
    return [
        'super_admin' => 'badge-danger',
        'school_admin' => 'badge-primary',
        'teacher' => 'badge-success',
        'accountant' => 'badge-warning',
        'parent' => 'badge-info',
        'director' => 'badge-secondary',
    ];
}

function getSetting(string $key, string $default = ''): string {
    global $pdo;
    $st = $pdo->prepare("SELECT value FROM settings WHERE `key`=?");
    $st->execute([$key]);
    return $st->fetchColumn() ?: $default;
}

function setSetting(string $key, string $value): void {
    global $pdo;
    $st = $pdo->prepare("INSERT INTO settings (`key`,value) VALUES (?,?) ON DUPLICATE KEY UPDATE value=?");
    $st->execute([$key, $value, $value]);
}

function checkDeleteAllowed(): void {
    if (($_SESSION['user_role'] ?? '') !== 'super_admin') {
        flashMessage('error', 'Seul le Super Admin peut supprimer.');
        header('Location: dashboard.php');
        exit;
    }
}

function userPhotoUrl(?string $photo): string {
    if ($photo && file_exists(__DIR__ . '/../' . $photo)) return $photo;
    return '';
}

function userPhotoImg(?string $photo, string $name, string $class = ''): string {
    $url = userPhotoUrl($photo);
    if ($url) return '<img src="' . e($url) . '" alt="Photo" class="' . $class . '">';
    $initials = '';
    $parts = explode(' ', $name);
    foreach ($parts as $p) if ($p !== '') $initials .= strtoupper($p[0]);
    return '<span class="photo-placeholder ' . $class . '">' . e($initials ?: '?') . '</span>';
}

function calcMoyMatiere(?float $ni, ?float $nd, ?float $nc): ?float {
    if ($ni === null && $nd === null && $nc === null) return null;
    $notes = array_filter([$ni, $nd, $nc], fn($v) => $v !== null);
    return empty($notes) ? null : round(array_sum($notes) / count($notes), 2);
}

function autoAppreciation(?float $moy): string {
    if ($moy === null) return '';
    if ($moy >= 16) return 'Excellent';
    if ($moy >= 14) return 'Très bien';
    if ($moy >= 12) return 'Bien';
    if ($moy >= 10) return 'Passable';
    return 'Insuffisant';
}

function checkGradingPeriod(PDO $pdo, int $schoolId, string $academicYear, int $trimestre): array {
    $st = $pdo->prepare("SELECT id, label, start_date, end_date, is_open, period_type FROM grading_periods WHERE school_id=? AND academic_year=? AND period_type='trimestre' AND period_number=? LIMIT 1");
    $st->execute([$schoolId, $academicYear, $trimestre]);
    $period = $st->fetch();
    if (!$period) return ['allowed' => true, 'message' => 'Aucune période définie — saisie libre.', 'period' => null];
    $today = date('Y-m-d');
    $isOpen = (bool)$period['is_open'];
    $inRange = $today >= $period['start_date'] && $today <= $period['end_date'];
    if (!$isOpen) return ['allowed' => false, 'message' => 'La période de saisie est fermée.', 'period' => $period];
    if (!$inRange) return ['allowed' => false, 'message' => 'Hors période de saisie (du ' . date('d/m/Y', strtotime($period['start_date'])) . ' au ' . date('d/m/Y', strtotime($period['end_date'])) . ').', 'period' => $period];
    return ['allowed' => true, 'message' => '', 'period' => $period];
}

function getDefaultAcademicYear(PDO $pdo, ?int $schoolId = null): string {
    $st = $pdo->prepare("SHOW COLUMNS FROM classes LIKE 'academic_year'");
    $st->execute();
    if (!$st->fetch()) return date('Y') . '-' . (date('y') + 1);
    if ($schoolId) {
        $st = $pdo->prepare("SELECT DISTINCT academic_year FROM classes WHERE academic_year IS NOT NULL AND academic_year != '' AND id IN (SELECT DISTINCT class_id FROM students WHERE school_id=?) ORDER BY academic_year DESC LIMIT 1");
        $st->execute([$schoolId]);
    } else {
        $st = $pdo->query("SELECT DISTINCT academic_year FROM classes WHERE academic_year IS NOT NULL AND academic_year != '' ORDER BY academic_year DESC LIMIT 1");
    }
    return $st->fetchColumn() ?: (date('Y') . '-' . (date('y') + 1));
}

function getActivePeriod(): int {
    return (int)($_SESSION['active_period'] ?? 1);
}

function setActivePeriod(int $period): void {
    if ($period >= 1 && $period <= 3) $_SESSION['active_period'] = $period;
}

function formatFileSize(int $bytes, int $decimals = 1): string {
    $units = ['o','Ko','Mo','Go'];
    $i = 0;
    while ($bytes >= 1024 && $i < count($units) - 1) { $bytes /= 1024; $i++; }
    return round($bytes, $decimals) . ' ' . $units[$i];
}

function nombreEnLettres(float $n): string {
    if ($n < 0) return 'moins ' . nombreEnLettres(abs($n));
    $units = ['','un','deux','trois','quatre','cinq','six','sept','huit','neuf','dix','onze','douze','treize','quatorze','quinze','seize','dix-sept','dix-huit','dix-neuf'];
    $tens = ['','','vingt','trente','quarante','cinquante','soixante','soixante-dix','quatre-vingt','quatre-vingt-dix'];
    $res = '';

    if ($n >= 1000000) {
        $m = floor($n / 1000000);
        $res .= ($m > 1 ? _nombreEnLettres99($m, $units, $tens) . ' ' : '') . 'million ';
        $n = fmod($n, 1000000);
    }
    if ($n >= 1000) {
        $m = floor($n / 1000);
        $res .= ($m > 1 ? _nombreEnLettres99($m, $units, $tens) . ' ' : '') . 'mille ';
        $n = fmod($n, 1000);
    }
    $res .= _nombreEnLettres99((int)$n, $units, $tens);
    return trim($res);
}

function _nombreEnLettres99(int $n, array $u, array $d): string {
    if ($n === 0) return 'zéro';
    $r = '';
    if ($n >= 100) {
        $c = floor($n / 100);
        $r .= ($c > 1 ? $u[$c] . ' ' : '') . 'cent ';
        $n = $n % 100;
        if ($n === 0 && $c > 1) $r .= 's';
    }
    if ($n >= 20) {
        $t = floor($n / 10);
        $r .= $d[$t];
        $n = $n % 10;
        if ($n === 1 && $t < 8) { $r .= ' et '; $n = 0; }
        if ($t === 7 || $t === 9) { $n += 10; }
        if ($n > 0) $r .= '-' . $u[$n];
        if ($t === 8 && $n === 0) $r .= 's';
    } elseif ($n > 0) {
        $r .= $u[$n];
    }
    return $r;
}

function sendWhatsApp(string $phone, string $message): bool {
    $phone = preg_replace('/[^0-9]/', '', $phone);
    if (strlen($phone) < 8) return false;
    $api = getSetting('whatsapp_api_url', '');
    $token = getSetting('whatsapp_api_token', '');
    if (!$api || !$token) return false;
    $ch = curl_init($api);
    curl_setopt_array($ch, [
        CURLOPT_POST => true,
        CURLOPT_HTTPHEADER => ['Content-Type: application/json', 'Authorization: Bearer ' . $token],
        CURLOPT_POSTFIELDS => json_encode(['phone' => $phone, 'message' => $message]),
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT => 10,
    ]);
    $res = curl_exec($ch);
    $http = curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);
    return $http === 200;
}

function getUserPhone(PDO $pdo, int $userId, string $role): ?string {
    switch ($role) {
        case 'teacher': $t = $pdo->prepare("SELECT phone FROM teachers WHERE id=?"); break;
        case 'parent': $t = $pdo->prepare("SELECT phone FROM parents WHERE id=?"); break;
        default: return null;
    }
    $t->execute([$userId]);
    $phone = $t->fetchColumn();
    return $phone ?: null;
}

function getCycleClassIds(PDO $pdo, int $classId): array {
    $st = $pdo->prepare("SELECT level FROM classes WHERE id=?");
    $st->execute([$classId]);
    $level = $st->fetchColumn();
    if (!$level) return [$classId];
    if (preg_match('/^(6|5|4|3)/', $level)) {
        $st = $pdo->prepare("SELECT id FROM classes WHERE level REGEXP '^(6|5|4|3)'");
    } elseif (preg_match('/^2nde/', $level)) {
        $st = $pdo->prepare("SELECT id FROM classes WHERE level REGEXP '^2nde'");
    } elseif (preg_match('/^1/', $level)) {
        $st = $pdo->prepare("SELECT id FROM classes WHERE level REGEXP '^1'");
    } elseif (preg_match('/^T/', $level)) {
        $st = $pdo->prepare("SELECT id FROM classes WHERE level REGEXP '^T'");
    } else {
        return [$classId];
    }
    $st->execute();
    return $st->fetchAll(PDO::FETCH_COLUMN);
}

function renderBackToTop() {
    if (!empty($_SERVER['HTTP_X_REQUESTED_WITH']) && strtolower($_SERVER['HTTP_X_REQUESTED_WITH']) === 'xmlhttprequest') return;
    ?><style>#backToTop{position:fixed;bottom:2rem;right:2rem;width:44px;height:44px;border-radius:50%;background:#1F4E79;color:#fff;border:none;font-size:1.4rem;cursor:pointer;box-shadow:0 2px 8px rgba(0,0,0,.25);opacity:0;visibility:hidden;transition:opacity .3s,visibility .3s;z-index:9999;display:flex;align-items:center;justify-content:center}#backToTop.visible{opacity:1;visibility:visible}#backToTop:hover{background:#163a5c}</style>
<button id="backToTop" onclick="window.scrollTo({top:0,behavior:'smooth'})" title="Retour en haut">&uarr;</button>
<script>
(function(){var b=document.getElementById('backToTop');if(!b)return;window.addEventListener('scroll',function(){b.classList.toggle('visible',window.scrollY>300)});if(window.scrollY>300)b.classList.add('visible')})();
</script><?php
}

function getClassSortSQL(string $alias = 'c'): string {
    $levels = "'6ème','5ème','4ème','3ème','2nde A4','2nde CD','2nde G1','2nde G2','2nde G3','2nde T1','2nde T2','2nde TI','2nde ET','2nde','1ère A4','1ère C4','1ère D','1ère G2','1ère G3','1ère T1','1ère T2','1ère TI','1ère ET','1ère','Tle A4','Tle C4','Tle D','Tle G2','Tle G3','Tle T1','Tle T2','Tle TI','Tle ET','Tle'";
    return "FIELD(COALESCE($alias.level, SUBSTRING_INDEX($alias.name, ' ', 1), 'ZZ'), $levels), $alias.name";
}

// ── Cache simple fichier ────────────────────────────────────────────────
function cachePath(string $key): string {
    $dir = __DIR__ . '/../cache';
    if (!is_dir($dir)) @mkdir($dir, 0775, true);
    return $dir . '/' . md5($key) . '.cache';
}

function cacheGet(string $key, int $ttl = 300): mixed {
    $file = cachePath($key);
    if (!file_exists($file)) return null;
    if (time() - filemtime($file) > $ttl) { @unlink($file); return null; }
    $data = @file_get_contents($file);
    if ($data === false) return null;
    $decoded = json_decode($data, true);
    return is_array($decoded) ? $decoded : null;
}

function cacheSet(string $key, mixed $value, int $ttl = 300): void {
    $file = cachePath($key);
    @file_put_contents($file, json_encode($value), LOCK_EX);
}

function cacheClear(?string $prefix = null): void {
    $dir = __DIR__ . '/../cache';
    if (!is_dir($dir)) return;
    foreach (glob($dir . '/*.cache') as $f) {
        if ($prefix === null || str_starts_with(basename($f), md5($prefix))) @unlink($f);
    }
}

// Fermeture session en écriture (pages read-only)
function sessionClose(): void {
    if (session_status() === PHP_SESSION_ACTIVE) session_write_close();
}
