<?php

declare(strict_types=1);

function auth_users_table_name(PDO $pdo): ?string
{
    $configured = trim((string) env('AUTH_TABLE', ''));
    if ($configured !== '' && table_exists($pdo, $configured)) {
        return $configured;
    }

    $hasUtilisateurs = table_exists($pdo, 'utilisateurs');
    $hasUsers = table_exists($pdo, 'users');

    if (!$hasUtilisateurs && !$hasUsers) {
        return null;
    }

    $catalogTable = strtolower(trim((string) env('CATALOG_TABLE', '')));
    if ($catalogTable === 'products' && $hasUsers) {
        return 'users';
    }
    if ($catalogTable === 'articles' && $hasUtilisateurs) {
        return 'utilisateurs';
    }

    if ($hasUtilisateurs && $hasUsers) {
        $usersCount = auth_table_count($pdo, 'users');
        $utilisateursCount = auth_table_count($pdo, 'utilisateurs');

        if ($usersCount >= $utilisateursCount) {
            return 'users';
        }

        return 'utilisateurs';
    }

    if ($hasUsers) {
        return 'users';
    }

    return 'utilisateurs';
}

function auth_table_count(PDO $pdo, string $table): int
{
    if (!table_exists($pdo, $table)) {
        return 0;
    }

    $stmt = $pdo->query('SELECT COUNT(*) FROM ' . qi($table));

    return (int) $stmt->fetchColumn();
}

function auth_is_admin_email(string $email): bool
{
    $email = strtolower(trim($email));
    if ($email === '') {
        return false;
    }

    $adminEmail = strtolower(trim((string) env('ORDER_ADMIN_EMAIL', '')));
    if ($adminEmail !== '' && $email === $adminEmail) {
        return true;
    }

    return false;
}

function auth_enum_values(array $columns, string $column): array
{
    $meta = $columns[$column] ?? null;
    if (!is_array($meta)) {
        return [];
    }

    $type = strtolower((string) ($meta['Type'] ?? ''));
    if (!str_starts_with($type, 'enum(')) {
        return [];
    }

    if (preg_match_all("/'([^']*)'/", $type, $matches) < 1) {
        return [];
    }

    return array_map(static fn (string $value): string => trim($value), $matches[1]);
}

function auth_default_role_value(PDO $pdo, string $table, string $column): string
{
    $columns = table_columns($pdo, $table);
    $allowed = auth_enum_values($columns, $column);
    if ($allowed === []) {
        return 'client';
    }

    foreach (['client', 'customer', 'user'] as $preferred) {
        if (in_array($preferred, $allowed, true)) {
            return $preferred;
        }
    }

    return $allowed[0] ?? 'client';
}

function auth_pick_client_role_value(PDO $pdo, string $table, string $column): string
{
    $columns = table_columns($pdo, $table);
    $allowed = auth_enum_values($columns, $column);
    if ($allowed === []) {
        return 'client';
    }

    foreach (['client', 'customer', 'user', 'utilisateur'] as $preferred) {
        if (in_array($preferred, $allowed, true)) {
            return $preferred;
        }
    }

    foreach ($allowed as $value) {
        $normalized = strtolower(trim($value));
        if (!in_array($normalized, ['admin', 'administrator'], true)) {
            return $value;
        }
    }

    return 'client';
}

function auth_pick_role_value(PDO $pdo, string $table, string $column, string $email): string
{
    $columns = table_columns($pdo, $table);
    $allowed = auth_enum_values($columns, $column);

    if (auth_is_admin_email($email) && in_array('admin', $allowed, true)) {
        return 'admin';
    }

    return auth_pick_client_role_value($pdo, $table, $column);
}

function auth_pick_approval_value(PDO $pdo, string $table, string $column, string $target): string
{
    $columns = table_columns($pdo, $table);
    $allowed = auth_enum_values($columns, $column);
    $target = strtolower(trim($target));

    if ($allowed === []) {
        return match ($target) {
            'approved' => 'approved',
            'rejected' => 'rejected',
            default => 'pending',
        };
    }

    $candidates = match ($target) {
        'approved' => ['approved', 'valide', 'active', 'actif', 'ok'],
        'rejected' => ['rejected', 'refused', 'refuse', 'inactive', 'inactif', 'annule'],
        default => ['pending', 'en_attente', 'attente', 'a_valider', 'inactive', 'inactif', 'rejected'],
    };

    foreach ($candidates as $candidate) {
        if (in_array($candidate, $allowed, true)) {
            return $candidate;
        }
    }

    foreach ($allowed as $value) {
        $normalized = strtolower(trim($value));
        if ($target === 'approved' && (str_contains($normalized, 'appro') || str_contains($normalized, 'valid'))) {
            return $value;
        }
        if ($target === 'rejected' && (str_contains($normalized, 'reject') || str_contains($normalized, 'refus'))) {
            return $value;
        }
        if ($target === 'pending' && (str_contains($normalized, 'pending') || str_contains($normalized, 'attente'))) {
            return $value;
        }
    }

    if ($target === 'pending') {
        foreach ($allowed as $value) {
            $normalized = strtolower(trim($value));
            if (
                str_contains($normalized, 'inact')
                || str_contains($normalized, 'reject')
                || str_contains($normalized, 'refus')
            ) {
                return $value;
            }
        }
    }

    return $allowed[0] ?? 'pending';
}

function auth_role_from_row(array $row, array $schema): string
{
    $email = strtolower(trim((string) ($row[$schema['email']] ?? '')));
    if (auth_is_admin_email($email)) {
        return 'admin';
    }

    if ($schema['role'] === null) {
        return 'client';
    }

    $rawRole = strtolower(trim((string) ($row[$schema['role']] ?? 'client')));
    if (in_array($rawRole, ['admin', 'administrator'], true)) {
        return 'admin';
    }

    return 'client';
}

function auth_normalize_approval_status(array $row, array $schema): string
{
    $role = auth_role_from_row($row, $schema);
    if ($role === 'admin') {
        return 'approved';
    }

    if ($schema['approval_status'] !== null) {
        $raw = strtolower(trim((string) ($row[$schema['approval_status']] ?? '')));
        if ($raw !== '') {
            if (
                str_contains($raw, 'reject')
                || str_contains($raw, 'refus')
                || str_contains($raw, 'inact')
                || str_contains($raw, 'annul')
                || str_contains($raw, 'block')
            ) {
                return 'rejected';
            }

            if (
                str_contains($raw, 'pending')
                || str_contains($raw, 'attent')
                || str_contains($raw, 'valider')
                || str_contains($raw, 'review')
                || str_contains($raw, 'verif')
            ) {
                return 'pending';
            }

            if (
                str_contains($raw, 'appro')
                || str_contains($raw, 'valid')
                || str_contains($raw, 'actif')
                || str_contains($raw, 'active')
                || $raw === 'ok'
            ) {
                return 'approved';
            }
        }
    }

    if ($schema['actif'] !== null) {
        return ((int) ($row[$schema['actif']] ?? 0) === 1) ? 'approved' : 'pending';
    }

    return 'approved';
}

function auth_account_is_active(array $row, array $schema): bool
{
    if ($schema['actif'] === null) {
        return true;
    }

    return (int) ($row[$schema['actif']] ?? 0) === 1;
}

function auth_assert_can_login(array $row, array $schema): void
{
    $role = auth_role_from_row($row, $schema);
    if ($role !== 'admin') {
        $approval = auth_normalize_approval_status($row, $schema);
        if ($approval === 'pending') {
            throw new RuntimeException('Compte en attente de validation administrateur.');
        }
        if ($approval === 'rejected') {
            throw new RuntimeException('Ouverture de compte refusee. Contactez le support.');
        }
    }

    if (!auth_account_is_active($row, $schema)) {
        throw new RuntimeException('Compte inactif.');
    }
}

function auth_schema(): array
{
    static $schema = null;

    if (is_array($schema)) {
        return $schema;
    }

    $pdo = db();
    $table = auth_users_table_name($pdo);
    if ($table === null) {
        throw new RuntimeException('Table utilisateurs/users introuvable.');
    }

    $schema = [
        'table' => $table,
        'id' => pick_column($pdo, $table, ['id']),
        'nom' => pick_column($pdo, $table, ['nom', 'name', 'full_name', 'username'], false),
        'prenom' => pick_column($pdo, $table, ['prenom', 'first_name'], false),
        'email' => pick_column($pdo, $table, ['email', 'mail']),
        'password' => pick_column($pdo, $table, ['mot_de_passe', 'password', 'password_hash']),
        'role' => pick_column($pdo, $table, ['role', 'user_role'], false),
        'actif' => pick_column($pdo, $table, ['actif', 'is_active', 'active'], false),
        'approval_status' => pick_column($pdo, $table, ['validation_statut', 'approval_status', 'account_status', 'status_compte'], false),
        'created_at' => pick_column($pdo, $table, ['created_at', 'date_creation'], false),
        'updated_at' => pick_column($pdo, $table, ['updated_at'], false),
        'numero' => pick_column($pdo, $table, ['numero', 'phone', 'telephone'], false),
        'adresse' => pick_column($pdo, $table, ['adresse', 'address'], false),
        'siret' => pick_column($pdo, $table, ['siret'], false),
    ];

    return $schema;
}

function auth_user_from_row(array $row, array $schema): array
{
    $email = trim((string) ($row[$schema['email']] ?? ''));
    $nom = '';
    $prenom = '';
    if ($schema['nom'] !== null) {
        $nom = trim((string) ($row[$schema['nom']] ?? ''));
    }
    if ($schema['prenom'] !== null) {
        $prenom = trim((string) ($row[$schema['prenom']] ?? ''));
    }

    $fullName = trim($nom . ' ' . $prenom);
    $role = auth_role_from_row($row, $schema);
    $approval = auth_normalize_approval_status($row, $schema);

    return [
        'id' => is_numeric((string) ($row[$schema['id']] ?? '')) ? (int) $row[$schema['id']] : (string) ($row[$schema['id']] ?? ''),
        'mail' => $email,
        'nom' => $fullName !== '' ? $fullName : ($nom !== '' ? $nom : $email),
        'prenom' => $prenom,
        'numero' => $schema['numero'] !== null ? trim((string) ($row[$schema['numero']] ?? '')) : '',
        'adresse' => $schema['adresse'] !== null ? trim((string) ($row[$schema['adresse']] ?? '')) : '',
        'siret' => $schema['siret'] !== null ? trim((string) ($row[$schema['siret']] ?? '')) : '',
        'role' => $role,
        'approval_status' => $approval,
        'is_active' => auth_account_is_active($row, $schema),
        'created_at' => $schema['created_at'] !== null ? (string) ($row[$schema['created_at']] ?? '') : '',
    ];
}

function auth_admin_account_from_row(array $row, array $schema): array
{
    $mapped = auth_user_from_row($row, $schema);

    return [
        'id' => $mapped['id'],
        'mail' => $mapped['mail'],
        'nom' => $schema['nom'] !== null ? trim((string) ($row[$schema['nom']] ?? '')) : '',
        'prenom' => $schema['prenom'] !== null ? trim((string) ($row[$schema['prenom']] ?? '')) : '',
        'display_name' => $mapped['nom'],
        'numero' => $mapped['numero'],
        'adresse' => $mapped['adresse'],
        'siret' => $mapped['siret'],
        'role' => $mapped['role'],
        'approval_status' => $mapped['approval_status'],
        'is_active' => $mapped['is_active'],
        'created_at' => $mapped['created_at'],
    ];
}

function auth_fill_required_columns(array $columns, array $data): array
{
    foreach ($columns as $column => $meta) {
        if (array_key_exists($column, $data)) {
            continue;
        }

        $extra = strtolower((string) ($meta['Extra'] ?? ''));
        if (str_contains($extra, 'auto_increment')) {
            continue;
        }

        $nullable = strtoupper((string) ($meta['Null'] ?? 'YES')) === 'YES';
        $hasDefault = array_key_exists('Default', $meta) && $meta['Default'] !== null;
        if ($nullable || $hasDefault) {
            continue;
        }

        $type = strtolower((string) ($meta['Type'] ?? ''));
        if (str_contains($type, 'int') || str_contains($type, 'decimal') || str_contains($type, 'float')) {
            $data[$column] = 0;
            continue;
        }

        if (str_contains($type, 'date') || str_contains($type, 'time')) {
            $data[$column] = date('Y-m-d H:i:s');
            continue;
        }

        $data[$column] = '';
    }

    return $data;
}

function auth_dynamic_insert(PDO $pdo, string $table, array $data): string
{
    if ($data === []) {
        throw new RuntimeException('Aucune donnee a inserer dans ' . $table);
    }

    $columns = array_keys($data);
    $sql = 'INSERT INTO ' . qi($table)
        . ' (' . implode(', ', array_map('qi', $columns)) . ') '
        . 'VALUES (' . implode(', ', array_map(static fn (string $col): string => ':' . $col, $columns)) . ')';

    $stmt = $pdo->prepare($sql);

    foreach ($data as $column => $value) {
        if (is_int($value)) {
            $stmt->bindValue(':' . $column, $value, PDO::PARAM_INT);
        } elseif ($value === null) {
            $stmt->bindValue(':' . $column, null, PDO::PARAM_NULL);
        } else {
            $stmt->bindValue(':' . $column, (string) $value, PDO::PARAM_STR);
        }
    }

    $stmt->execute();

    return (string) $pdo->lastInsertId();
}

function auth_find_raw_user_by_email(string $email): ?array
{
    $email = strtolower(trim($email));
    if ($email === '') {
        return null;
    }

    $pdo = db();
    $schema = auth_schema();
    $sql = 'SELECT * FROM ' . qi($schema['table'])
        . ' WHERE LOWER(' . qi($schema['email']) . ') = :email LIMIT 1';

    $stmt = $pdo->prepare($sql);
    $stmt->bindValue(':email', $email, PDO::PARAM_STR);
    $stmt->execute();

    $row = $stmt->fetch();
    if (!is_array($row)) {
        return null;
    }

    return $row;
}

function auth_find_raw_user_by_id(int|string $id): ?array
{
    if ((is_int($id) && $id <= 0) || (is_string($id) && trim($id) === '')) {
        return null;
    }

    $pdo = db();
    $schema = auth_schema();
    $sql = 'SELECT * FROM ' . qi($schema['table'])
        . ' WHERE ' . qi($schema['id']) . ' = :id LIMIT 1';

    $stmt = $pdo->prepare($sql);
    if (is_int($id) || (is_string($id) && ctype_digit($id))) {
        $stmt->bindValue(':id', (int) $id, PDO::PARAM_INT);
    } else {
        $stmt->bindValue(':id', (string) $id, PDO::PARAM_STR);
    }
    $stmt->execute();

    $row = $stmt->fetch();
    if (!is_array($row)) {
        return null;
    }

    return $row;
}

function auth_regenerate_session_id(): void
{
    if (session_status() === PHP_SESSION_ACTIVE) {
        session_regenerate_id(true);
    }
}

function auth_set_session_user(array $user): void
{
    $_SESSION['auth_user'] = $user;
}

function auth_current_user(): ?array
{
    $user = $_SESSION['auth_user'] ?? null;
    if (!is_array($user)) {
        return null;
    }

    $schema = auth_schema();
    $email = strtolower(trim((string) ($user['mail'] ?? '')));
    $id = $user['id'] ?? '';

    $rawUser = $email !== ''
        ? auth_find_raw_user_by_email($email)
        : auth_find_raw_user_by_id(is_scalar($id) ? (string) $id : '');

    if (!is_array($rawUser)) {
        auth_logout();
        return null;
    }

    try {
        auth_assert_can_login($rawUser, $schema);
    } catch (RuntimeException) {
        auth_logout();
        return null;
    }

    $freshUser = auth_user_from_row($rawUser, $schema);
    auth_set_session_user($freshUser);

    return $freshUser;
}

function auth_logout(): void
{
    unset($_SESSION['auth_user']);
    auth_regenerate_session_id();
}

function auth_register(array $payload): array
{
    $email = strtolower(trim((string) ($payload['email'] ?? $payload['mail'] ?? '')));
    $nom = trim((string) ($payload['nom'] ?? ''));
    $prenom = trim((string) ($payload['prenom'] ?? ''));
    $password = (string) ($payload['password'] ?? '');
    $passwordConfirm = (string) ($payload['password_confirm'] ?? $payload['passwordConfirm'] ?? '');
    $adresse = trim((string) ($payload['adresse'] ?? ''));
    $numero = trim((string) ($payload['numero'] ?? ''));
    $siret = trim((string) ($payload['siret'] ?? ''));

    if ($nom === '') {
        throw new InvalidArgumentException('Le nom est requis.');
    }
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        throw new InvalidArgumentException('Adresse email invalide.');
    }
    if (strlen($password) < 6) {
        throw new InvalidArgumentException('Le mot de passe doit contenir au moins 6 caracteres.');
    }
    if ($password !== $passwordConfirm) {
        throw new InvalidArgumentException('La confirmation du mot de passe ne correspond pas.');
    }
    if (auth_find_raw_user_by_email($email) !== null) {
        throw new RuntimeException('Cet email est deja utilise.');
    }

    $pdo = db();
    $schema = auth_schema();
    $now = date('Y-m-d H:i:s');

    $isAdminSignup = auth_is_admin_email($email);

    $data = [
        $schema['email'] => $email,
        $schema['password'] => password_hash($password, PASSWORD_DEFAULT),
    ];

    if ($schema['nom'] !== null) {
        $data[$schema['nom']] = $nom;
    }
    if ($schema['prenom'] !== null) {
        $data[$schema['prenom']] = $prenom;
    }
    if ($schema['role'] !== null) {
        $data[$schema['role']] = auth_pick_role_value($pdo, $schema['table'], $schema['role'], $email);
    }
    if ($schema['actif'] !== null) {
        $data[$schema['actif']] = $isAdminSignup ? 1 : 0;
    }
    if ($schema['approval_status'] !== null) {
        $data[$schema['approval_status']] = auth_pick_approval_value(
            $pdo,
            $schema['table'],
            $schema['approval_status'],
            $isAdminSignup ? 'approved' : 'pending'
        );
    }
    if ($schema['created_at'] !== null) {
        $data[$schema['created_at']] = $now;
    }
    if ($schema['updated_at'] !== null) {
        $data[$schema['updated_at']] = $now;
    }
    if ($schema['adresse'] !== null && $adresse !== '') {
        $data[$schema['adresse']] = $adresse;
    }
    if ($schema['numero'] !== null && $numero !== '') {
        $data[$schema['numero']] = $numero;
    }
    if ($schema['siret'] !== null && $siret !== '') {
        $data[$schema['siret']] = $siret;
    }

    $data = auth_fill_required_columns(table_columns($pdo, $schema['table']), $data);
    $insertedId = auth_dynamic_insert($pdo, $schema['table'], $data);

    if ($isAdminSignup) {
        $created = auth_find_raw_user_by_id(ctype_digit($insertedId) ? (int) $insertedId : $insertedId);
        $user = is_array($created)
            ? auth_user_from_row($created, $schema)
            : [
                'id' => ctype_digit($insertedId) ? (int) $insertedId : $insertedId,
                'mail' => $email,
                'nom' => trim($nom . ' ' . $prenom) !== '' ? trim($nom . ' ' . $prenom) : $email,
                'role' => 'admin',
                'approval_status' => 'approved',
                'is_active' => true,
            ];

        $notifications = function_exists('send_account_registration_notification_emails')
            ? send_account_registration_notification_emails([
                'mail' => $email,
                'nom' => $nom,
                'prenom' => $prenom,
                'numero' => $numero,
                'adresse' => $adresse,
                'siret' => $siret,
            ], true)
            : [];

        auth_regenerate_session_id();
        auth_set_session_user($user);

        return [
            'user' => $user,
            'message' => 'Compte admin cree et connecte.',
            'notifications' => $notifications,
        ];
    }

    $notifications = function_exists('send_account_registration_notification_emails')
        ? send_account_registration_notification_emails([
            'mail' => $email,
            'nom' => $nom,
            'prenom' => $prenom,
            'numero' => $numero,
            'adresse' => $adresse,
            'siret' => $siret,
        ], false)
        : [];

    return [
        'user' => null,
        'message' => 'Compte cree. En attente de validation par un administrateur.',
        'notifications' => $notifications,
    ];
}

function auth_login(array $payload): array
{
    $email = strtolower(trim((string) ($payload['email'] ?? $payload['mail'] ?? '')));
    $password = (string) ($payload['password'] ?? '');

    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        throw new InvalidArgumentException('Adresse email invalide.');
    }
    if ($password === '') {
        throw new InvalidArgumentException('Mot de passe requis.');
    }

    $schema = auth_schema();
    $record = auth_find_raw_user_by_email($email);
    if ($record === null) {
        throw new RuntimeException('Identifiants invalides.');
    }

    $hash = (string) ($record[$schema['password']] ?? '');
    if ($hash === '' || !password_verify($password, $hash)) {
        throw new RuntimeException('Identifiants invalides.');
    }

    auth_assert_can_login($record, $schema);

    if (password_needs_rehash($hash, PASSWORD_DEFAULT)) {
        $pdo = db();
        $sql = 'UPDATE ' . qi($schema['table'])
            . ' SET ' . qi($schema['password']) . ' = :password '
            . 'WHERE ' . qi($schema['id']) . ' = :id LIMIT 1';
        $stmt = $pdo->prepare($sql);
        $stmt->bindValue(':password', password_hash($password, PASSWORD_DEFAULT), PDO::PARAM_STR);
        $rowId = $record[$schema['id']] ?? 0;
        if (is_numeric((string) $rowId)) {
            $stmt->bindValue(':id', (int) $rowId, PDO::PARAM_INT);
        } else {
            $stmt->bindValue(':id', (string) $rowId, PDO::PARAM_STR);
        }
        $stmt->execute();
    }

    $user = auth_user_from_row($record, $schema);
    auth_regenerate_session_id();
    auth_set_session_user($user);

    return $user;
}

function auth_list_accounts(): array
{
    $pdo = db();
    $schema = auth_schema();

    $orderBy = $schema['created_at'] !== null
        ? qi($schema['created_at']) . ' DESC, ' . qi($schema['id']) . ' DESC'
        : qi($schema['id']) . ' DESC';

    $sql = 'SELECT * FROM ' . qi($schema['table']) . ' ORDER BY ' . $orderBy . ' LIMIT 500';
    $stmt = $pdo->query($sql);
    $rows = $stmt->fetchAll();

    $accounts = [];
    foreach ($rows as $row) {
        if (!is_array($row)) {
            continue;
        }
        $accounts[] = auth_admin_account_from_row($row, $schema);
    }

    usort($accounts, static function (array $a, array $b): int {
        $priorityMap = ['pending' => 0, 'approved' => 1, 'rejected' => 2];
        $priorityA = $priorityMap[(string) ($a['approval_status'] ?? 'approved')] ?? 3;
        $priorityB = $priorityMap[(string) ($b['approval_status'] ?? 'approved')] ?? 3;
        if ($priorityA !== $priorityB) {
            return $priorityA <=> $priorityB;
        }

        $createdA = strtotime((string) ($a['created_at'] ?? '')) ?: 0;
        $createdB = strtotime((string) ($b['created_at'] ?? '')) ?: 0;

        return $createdB <=> $createdA;
    });

    return $accounts;
}

function auth_update_account_approval(int|string $userId, string $decision): array
{
    $target = strtolower(trim($decision));
    if (in_array($target, ['approve', 'approved', 'accept', 'accepted', 'valider', 'validee'], true)) {
        $target = 'approved';
    } elseif (in_array($target, ['reject', 'rejected', 'refuse', 'refused', 'bloquer', 'blocked'], true)) {
        $target = 'rejected';
    } else {
        throw new InvalidArgumentException('Decision de validation invalide.');
    }

    $schema = auth_schema();
    $pdo = db();
    $record = auth_find_raw_user_by_id($userId);
    if (!is_array($record)) {
        throw new RuntimeException('Compte utilisateur introuvable.');
    }

    if (auth_role_from_row($record, $schema) === 'admin') {
        throw new RuntimeException('Impossible de modifier un compte administrateur.');
    }

    $updates = [];
    if ($schema['approval_status'] !== null) {
        $updates[$schema['approval_status']] = auth_pick_approval_value(
            $pdo,
            $schema['table'],
            $schema['approval_status'],
            $target
        );
    }
    if ($schema['actif'] !== null) {
        $updates[$schema['actif']] = $target === 'approved' ? 1 : 0;
    }
    if ($schema['updated_at'] !== null) {
        $updates[$schema['updated_at']] = date('Y-m-d H:i:s');
    }

    if ($updates === []) {
        throw new RuntimeException('Aucune colonne de validation disponible sur cette table utilisateurs.');
    }

    $setParts = [];
    foreach ($updates as $column => $unused) {
        $setParts[] = qi($column) . ' = :set_' . $column;
    }

    $sql = 'UPDATE ' . qi($schema['table'])
        . ' SET ' . implode(', ', $setParts)
        . ' WHERE ' . qi($schema['id']) . ' = :id LIMIT 1';

    $stmt = $pdo->prepare($sql);
    foreach ($updates as $column => $value) {
        $param = ':set_' . $column;
        if (is_int($value)) {
            $stmt->bindValue($param, $value, PDO::PARAM_INT);
        } else {
            $stmt->bindValue($param, (string) $value, PDO::PARAM_STR);
        }
    }

    if (is_int($userId) || (is_string($userId) && ctype_digit($userId))) {
        $stmt->bindValue(':id', (int) $userId, PDO::PARAM_INT);
    } else {
        $stmt->bindValue(':id', (string) $userId, PDO::PARAM_STR);
    }
    $stmt->execute();

    $updated = auth_find_raw_user_by_id($userId);
    if (!is_array($updated)) {
        throw new RuntimeException('Compte mis a jour mais introuvable ensuite.');
    }

    return auth_admin_account_from_row($updated, $schema);
}

function auth_forgot_password(array $payload): array
{
    $email = strtolower(trim((string) ($payload['email'] ?? $payload['mail'] ?? '')));
    if (!filter_var($email, FILTER_VALIDATE_EMAIL)) {
        throw new InvalidArgumentException('Adresse email invalide.');
    }

    return [
        'message' => 'Fonction reinitialisation mot de passe a venir.',
    ];
}
