<?php
if (!defined('STATUTS_LABELS')) {
    // Référentiel des statuts de candidature (Tâche 10+) : mêmes 4 valeurs que
    // l'ENUM statut de schema.sql, dans l'ordre du cycle de traitement.
    define('STATUTS_LABELS', [
        'nouveau' => 'Nouveau',
        'traite' => 'Traité',
        'accepte' => 'Accepté',
        'rejete' => 'Rejeté',
    ]);
}
if (!function_exists('valider_candidature')) {
    function valider_candidature(array $d): array {
        foreach ($d as $k => $v) if (is_string($v)) $d[$k] = trim($v);
        $e = [];
        $obligatoires = ['nom','prenoms','date_naissance','lieu_naissance','sexe','nationalite',
            'telephone','ville','tuteur_nom','tuteur_telephone','bac_serie','bac_annee',
            'etablissement_origine','filiere1'];
        foreach ($obligatoires as $c) {
            $v = $d[$c] ?? '';
            if (!is_string($v)) { $e[$c] = 'Valeur invalide.'; continue; }
            if ($v === '') $e[$c] = 'Ce champ est obligatoire.';
        }
        if (!isset($e['telephone']) && !preg_match('/^\+?[0-9]{8,15}$/', str_replace(' ', '', $d['telephone'])))
            $e['telephone'] = 'Numéro de téléphone invalide.';
        if (!isset($e['tuteur_telephone']) && !preg_match('/^\+?[0-9]{8,15}$/', str_replace(' ', '', $d['tuteur_telephone'])))
            $e['tuteur_telephone'] = 'Numéro de téléphone invalide.';
        if (($d['email'] ?? '') !== '' && (!is_string($d['email']) || !filter_var($d['email'], FILTER_VALIDATE_EMAIL)))
            $e['email'] = 'Adresse e-mail invalide.';
        if (!isset($e['sexe']) && !in_array($d['sexe'], ['M','F'], true)) $e['sexe'] = 'Sexe invalide (M ou F attendu).';
        if (!isset($e['date_naissance'])) {
            $dt = DateTime::createFromFormat('Y-m-d', $d['date_naissance']);
            $err = DateTime::getLastErrors(); // PHP >= 8.2 : false quand aucune erreur ni alerte
            $ok = $dt instanceof DateTime
                && ($err === false || ($err['warning_count'] === 0 && $err['error_count'] === 0));
            if ($ok) $ok = $dt->format('Y-m-d') <= date('Y-m-d') && (int)$dt->format('Y') >= 1940;
            if (!$ok) $e['date_naissance'] = 'Date de naissance invalide.';
        }
        if (!isset($e['bac_annee']) && !preg_match('/^(19|20)\d{2}$/', $d['bac_annee']))
            $e['bac_annee'] = 'Année du BAC invalide (ex. 2026).';
        $codes = array_keys(filieres());
        if (!isset($e['filiere1']) && !in_array($d['filiere1'], $codes, true)) $e['filiere1'] = 'Filière inconnue.';
        if (($d['filiere2'] ?? '') !== '' && (!is_string($d['filiere2']) || !in_array($d['filiere2'], $codes, true)))
            $e['filiere2'] = 'Filière inconnue.';
        // Longueurs maximales alignées sur les VARCHAR de schema.sql
        $limites = ['nom' => 100, 'prenoms' => 150, 'lieu_naissance' => 100, 'nationalite' => 60,
            'telephone' => 20, 'email' => 150, 'ville' => 100, 'tuteur_nom' => 150,
            'tuteur_telephone' => 20, 'bac_serie' => 30, 'bac_annee' => 9, 'etablissement_origine' => 150];
        foreach ($limites as $c => $max)
            if (!isset($e[$c]) && is_string($d[$c] ?? null) && mb_strlen($d[$c]) > $max)
                $e[$c] = "Texte trop long (max $max caractères).";
        return $e;
    }
}
if (!function_exists('generer_numero_dossier')) {
    function generer_numero_dossier(PDO $pdo, int $annee): string {
        // MAX (et non COUNT) : après une suppression, le prochain numéro reste unique.
        // Le suffixe est à largeur fixe (0001..9999), donc le MAX lexicographique = MAX numérique.
        $stmt = $pdo->prepare('SELECT MAX(numero) FROM candidatures WHERE numero LIKE :motif');
        $stmt->bindValue(':motif', 'ISIT-' . $annee . '-%', PDO::PARAM_STR);
        $stmt->execute();
        $max = $stmt->fetchColumn();
        $suite = ($max === null || $max === false) ? 1 : ((int)substr((string)$max, -4)) + 1;
        return sprintf('ISIT-%d-%04d', $annee, $suite);
    }
}
if (!function_exists('filtrer_candidatures')) {
    /**
     * Liste admin : candidatures filtrées par filière, statut et recherche libre.
     *
     * Contrat : $filtres = ['filiere' => string, 'statut' => string, 'q' => string],
     * valeurs déjà validées par l'appelant ('' = pas de filtre ; filiere et statut
     * hors référentiel écartés en amont). La recherche q porte sur nom, prenoms,
     * numero et telephone, en sous-chaîne ; ses métacaractères LIKE (%, _, \) sont
     * échappés pour être cherchés littéralement — « % » ne doit pas devenir un joker.
     *
     * Clause ESCAPE par pilote (même branchement que connexion_bloquee()) : le texte
     * SQL doit être ESCAPE '\' pour SQLite mais ESCAPE '\\' pour MySQL, dont les
     * littéraux de chaîne interprètent l'antislash comme caractère d'échappement.
     *
     * Tri du plus récent (created_at puis id), plafonné à 501 lignes : l'appelant
     * coupe à 500 et signale le dépassement (la 501e ligne ne sert qu'à le détecter).
     */
    function filtrer_candidatures(PDO $pdo, array $filtres): array {
        $conditions = [];
        $params = [];
        if (($filtres['filiere'] ?? '') !== '') { $conditions[] = 'filiere1 = ?'; $params[] = $filtres['filiere']; }
        if (($filtres['statut'] ?? '') !== '') { $conditions[] = 'statut = ?'; $params[] = $filtres['statut']; }
        $q = $filtres['q'] ?? '';
        if ($q !== '') {
            $esc = $pdo->getAttribute(PDO::ATTR_DRIVER_NAME) === 'sqlite' ? "ESCAPE '\\'" : "ESCAPE '\\\\'";
            $motif = '%' . str_replace(['\\', '%', '_'], ['\\\\', '\\%', '\\_'], $q) . '%';
            $like = [];
            foreach (['nom', 'prenoms', 'numero', 'telephone'] as $col) {
                $like[] = "$col LIKE ? $esc";
                $params[] = $motif;
            }
            $conditions[] = '(' . implode(' OR ', $like) . ')';
        }
        $where = $conditions ? ('WHERE ' . implode(' AND ', $conditions)) : '';
        $stmt = $pdo->prepare("SELECT numero, nom, prenoms, filiere1, telephone, statut, dossier_complet, created_at
            FROM candidatures $where ORDER BY created_at DESC, id DESC LIMIT 501");
        $stmt->execute($params);
        return $stmt->fetchAll();
    }
}
if (!function_exists('enregistrer_candidature')) {
    /**
     * Insère une candidature et retourne ['id' => int, 'numero' => string].
     *
     * Précondition : $d doit avoir été validé au préalable — valider_candidature($d)
     * doit retourner un tableau vide. Aucune revalidation n'est faite ici.
     *
     * En cas de collision de numéro (deux soumissions simultanées), le numéro est
     * régénéré et l'insertion retentée (3 tentatives au plus), puis l'exception est relancée.
     */
    function enregistrer_candidature(PDO $pdo, array $d, int $annee): array {
        $cols = ['numero','nom','prenoms','date_naissance','lieu_naissance','sexe','nationalite','telephone',
            'email','ville','tuteur_nom','tuteur_telephone','bac_serie','bac_annee','etablissement_origine',
            'filiere1','filiere2'];
        $sql = 'INSERT INTO candidatures (' . implode(',', $cols) . ') VALUES (' . rtrim(str_repeat('?,', count($cols)), ',') . ')';
        for ($tentative = 1; ; $tentative++) {
            $numero = generer_numero_dossier($pdo, $annee);
            $vals = [$numero];
            foreach (array_slice($cols, 1) as $c) {
                $v = $d[$c] ?? '';
                if (is_string($v)) $v = trim($v);
                $vals[] = $v === '' ? null : $v;
            }
            try {
                $pdo->prepare($sql)->execute($vals);
                return ['id' => (int)$pdo->lastInsertId(), 'numero' => $numero];
            } catch (PDOException $ex) {
                $etat = (string)($ex->errorInfo[0] ?? $ex->getCode());
                $conflitNumero = $etat === '23000' && str_contains($ex->getMessage(), 'numero');
                if (!$conflitNumero || $tentative >= 3) throw $ex;
            }
        }
    }
}
