<?php
/**
 * Projet                    : MageStock
 * Logiciel                  : MageStock
 * Fichier                   : login.php
 * Rôle                      : Authentification locale/LDAP dans la passerelle publique MageStock.
 * Auteur                    : Régis KREMER
 * Version du fichier        : 2.6.123
 * Version générale          : 2.6.123
 * Lot                       : MAGESTOCK FOOTER MAGEKORP IDENTITY CONTACT1
 * Base requise              : MageStock 2.6.123
 * Création                  : 2026-02-21
 * Dernière modification     : 2026-07-30
 * Dépendances               : config.php, includes/ldap-helper.php, auth-gateway.css/js
 * Données touchées          : sessions PHP, rate_limits.json, audit LDAP
 * Permissions               : accès public ; redirection selon les autorisations après authentification
 * Sécurité                  : CSRF, rate limiting, session régénérée, erreurs génériques, no-store
 * Compatibilité             : PHP 8.1 à 8.4, PC, smartphone et tablette
 *
 * Résumé :
 * - Conserve la structure 65/35 tout en affirmant une identité visuelle propre à MageStock.
 * - Présente immédiatement les stocks, magasins, inventaires, mouvements et usages terrain.
 * - Authentifie les comptes locaux ou LDAP sans modifier les contrats de sécurité existants.
 *
 * Historique :
 * - 2.3.0 — Première refonte visuelle MageStock.
 * - 2.6.102 — Authentification locale/LDAP hybride.
 * - 2.6.105 — Priorité à la finalisation des mises à jour administrateur.
 * - 2.6.119 — Passerelle 65/35, CSRF de connexion, récupération et version de session.
 * - 2.6.121 — Identité MageStock renforcée et correction du bouton de visibilité du mot de passe.
 * - 2.6.123 — Footer MageKorp harmonisé avec le shell, logos officiels et contact public.
 */

declare(strict_types=1);

require_once __DIR__ . '/config.php';
require_once __DIR__ . '/includes/ldap-helper.php';

if(appInstallationRequired()){
    header('Location: ' . appUrl('install'));
    exit;
}

function normalizeLoginIdentifier(string $value): string
{
    $value = trim($value);
    if($value === '') return '';
    if(function_exists('normalizer_normalize') && class_exists('Normalizer')){
        $normalized = @normalizer_normalize($value, Normalizer::FORM_C);
        if(is_string($normalized) && $normalized !== '') $value = $normalized;
    }
    $value = function_exists('mb_strtolower') ? mb_strtolower($value, 'UTF-8') : strtolower($value);
    if(function_exists('iconv')){
        $ascii = @iconv('UTF-8', 'ASCII//TRANSLIT//IGNORE', $value);
        if(is_string($ascii) && $ascii !== '') $value = $ascii;
    }
    $value = preg_replace('/[^a-z0-9@._-]+/i', '', $value) ?? $value;
    return trim($value);
}

function loginIdentifierMatches(array $user, string $input): bool
{
    $input = trim($input);
    if($input === '') return false;
    foreach([(string)($user['username'] ?? ''), (string)($user['email'] ?? '')] as $candidate){
        $candidate = trim($candidate);
        if($candidate === '') continue;
        if(hash_equals($candidate, $input)) return true;
        if(normalizeLoginIdentifier($candidate) === normalizeLoginIdentifier($input)) return true;
    }
    return false;
}

function loadUsersForLogin(?string &$loginDataError = null, string $language = 'fr'): array
{
    $loginDataError = null;
    $file = dataPath('users');
    if(!is_file($file)){
        $loginDataError = appTranslate('login.error.users_missing', $language, [], 'Le fichier data/users.json est introuvable sur ce serveur.');
        return [];
    }
    if(!is_readable($file)){
        $loginDataError = appTranslate('login.error.users_unreadable', $language, [], 'Le fichier data/users.json existe mais PHP ne peut pas le lire.');
        return [];
    }
    $raw = file_get_contents($file);
    if($raw === false || trim($raw) === ''){
        $loginDataError = appTranslate('login.error.users_empty', $language, [], 'Le fichier data/users.json est vide ou illisible.');
        return [];
    }
    $decoded = json_decode($raw, true);
    if(!is_array($decoded)){
        $loginDataError = appTranslate('login.error.users_invalid_json', $language, ['error'=>json_last_error_msg()], 'Le fichier data/users.json n’est pas un JSON valide : {error}');
        return [];
    }
    return $decoded;
}

startSession();
$language = appResolveLanguage();
if(isLoggedIn()){
    header('Location: ' . appUrl('dashboard'));
    exit;
}

header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');
header('Pragma: no-cache');
header('X-Robots-Tag: noindex, nofollow, noarchive');

$error = '';
if($_SERVER['REQUEST_METHOD'] === 'POST'){
    $csrf = (string)($_POST['csrf_token'] ?? '');
    if(!hash_equals((string)($_SESSION['csrf_token'] ?? ''), $csrf)){
        $error = appTranslate('login.error.session_expired', $language, [], 'Session expirée. Rechargez la page puis réessayez.');
    } else {
        $username = trim((string)($_POST['username'] ?? ''));
        $password = (string)($_POST['password'] ?? '');
        $rateLimitKey = 'login_' . md5((string)($_SERVER['REMOTE_ADDR'] ?? 'unknown'));
        if(!checkRateLimit($rateLimitKey, 5, 300)){
            $error = appTranslate('login.error.rate_limit', $language, [], 'Trop de tentatives de connexion. Veuillez patienter 5 minutes.');
        } else {
            $loginDataError = null;
            $users = loadUsersForLogin($loginDataError, $language);
            if($loginDataError){
                $error = $loginDataError;
            } else {
                try {
                    $authResult = magestockLdapLogin($username, $password, $users);
                    $found = $authResult['user'] ?? null;
                    if(!$found || empty($found['id'])) throw new MageStockLdapException('Utilisateur authentifié invalide.', 'invalid_authenticated_user');
                    session_regenerate_id(true);
                    $_SESSION['user_id'] = $found['id'];
                    $_SESSION['user_role'] = $found['role'] ?? 'user';
                    $_SESSION['auth_source'] = $authResult['source'] ?? 'local';
                    $_SESSION['auth_version'] = magestockUserAuthVersion($found);
                    $_SESSION['authenticated_at'] = time();
                    if(magestockUpdateNeedsAttention()){
                        if(hasAdminAccess($found)){
                            header('Location: ' . appUrl('update'));
                            exit;
                        }
                        magestockClearAuthenticationSession();
                        throw new MageStockLdapException('Mise à jour à finaliser.', 'update_required', 'Une mise à jour MageStock doit être finalisée par un administrateur.');
                    }
                    header('Location: ' . appUrl('dashboard'));
                    exit;
                } catch(MageStockLdapException $authError) {
                    magestockLdapAudit('login_failed', [
                        'username'=>(function_exists('mb_substr') ? mb_substr($username, 0, 120, 'UTF-8') : substr($username, 0, 120)),
                        'code'=>$authError->reasonCode,
                    ], null);
                    $error = $authError->reasonCode === 'update_required'
                        ? $authError->publicMessage
                        : appTranslate('login.error.invalid_credentials', $language, [], $authError->publicMessage);
                } catch(Throwable $authError) {
                    error_log('[MageStock Auth] ' . $authError->getMessage());
                    magestockLdapAudit('login_failed', [
                        'username'=>(function_exists('mb_substr') ? mb_substr($username, 0, 120, 'UTF-8') : substr($username, 0, 120)),
                        'code'=>'unexpected_error',
                    ], null);
                    $error = appTranslate('login.error.invalid_credentials', $language, [], 'Identifiants incorrects ou service d’authentification indisponible.');
                }
            }
        }
    }
}

$settings = getSettings();
$company = trim((string)($settings['companyName'] ?? 'MageStock')) ?: 'MageStock';
$csrfToken = csrfToken();
$loginLangNames = [
    'fr'=>'Français','en'=>'English','de'=>'Deutsch','es'=>'Español','it'=>'Italiano','nl'=>'Nederlands',
    'pt'=>'Português','pl'=>'Polski','ru'=>'Русский','tr'=>'Türkçe','ar'=>'العربية','ja'=>'日本語',
    'ko'=>'한국어','zh'=>'中文','hi'=>'हिन्दी','id'=>'Bahasa Indonesia','uk'=>'Українська',
];
$loginThemeLight = appTranslate('login.theme.light', $language, [], 'Passer en thème clair');
$loginThemeDark = appTranslate('login.theme.dark', $language, [], 'Passer en thème sombre');
?>
<!doctype html>
<html lang="<?= htmlspecialchars($language) ?>" dir="<?= $language === 'ar' ? 'rtl' : 'ltr' ?>" data-theme="dark">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0, viewport-fit=cover">
<meta name="theme-color" content="#0b1320">
<meta name="application-name" content="MageStock">
<meta name="description" content="<?= htmlspecialchars(appTranslate('login.meta_description', $language, [], 'Connexion sécurisée à MageStock.')) ?>">
<meta name="robots" content="noindex,nofollow,noarchive">
<link rel="manifest" href="<?= htmlspecialchars(appUrl('app.webmanifest?v=2.6.121')) ?>">
<link rel="icon" href="<?= htmlspecialchars(appUrl('favicon.ico?v=2.6.121')) ?>" sizes="any">
<link rel="icon" type="image/svg+xml" href="<?= htmlspecialchars(assetUrl('img/favicon.svg?v=2.6.121')) ?>">
<link rel="apple-touch-icon" sizes="180x180" href="<?= htmlspecialchars(assetUrl('img/apple-touch-icon.png?v=2.6.121')) ?>">
<title><?= htmlspecialchars(appTranslate('login.page_title', $language, [], 'Connexion')) ?> — <?= htmlspecialchars($company) ?></title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=DM+Sans:opsz,wght@9..40,400;9..40,500;9..40,600;9..40,700;9..40,800&display=swap" rel="stylesheet">
<link rel="stylesheet" href="<?= htmlspecialchars(assetUrl('css/app.css?v=' . filemtime(__DIR__.'/assets/css/app.css'))) ?>">
<link rel="stylesheet" href="<?= htmlspecialchars(assetUrl('css/auth-gateway.css?v=' . filemtime(__DIR__.'/assets/css/auth-gateway.css'))) ?>">
</head>
<body class="auth-page" data-theme-light-label="<?= htmlspecialchars($loginThemeLight) ?>" data-theme-dark-label="<?= htmlspecialchars($loginThemeDark) ?>">
<div class="auth-gateway">
    <aside class="auth-brand-panel auth-brand-panel--login" aria-labelledby="authBrandName">
        <div class="auth-brand-shell">
            <div class="auth-brand-copy">
                <div class="auth-wordmark">
                    <img class="auth-wordmark__logo" src="<?= htmlspecialchars(assetUrl('img/sm.png')) ?>" alt="">
                    <div>
                        <strong class="auth-wordmark__name" id="authBrandName">MageStock</strong>
                        <span class="auth-wordmark__strapline"><?= htmlspecialchars(appTranslate('auth.brand.strapline', $language, [], 'Stocks · inventaires · terrain')) ?></span>
                    </div>
                </div>
                <p class="auth-brand-lead"><?= htmlspecialchars(appTranslate('login.brand.lead', $language, [], 'Pilotez vos stocks, vos magasins et vos opérations terrain depuis un espace unique.')) ?></p>
                <ul class="auth-brand-list">
                    <li><?= htmlspecialchars(appTranslate('login.brand.point.access', $language, [], 'Visualisez le stock disponible par magasin et emplacement')) ?></li>
                    <li><?= htmlspecialchars(appTranslate('login.brand.point.scope', $language, [], 'Réalisez les inventaires et les mouvements avec des parcours guidés')) ?></li>
                    <li><?= htmlspecialchars(appTranslate('login.brand.point.security', $language, [], 'Retrouvez le scanner et les outils terrain dans une expérience unifiée')) ?></li>
                </ul>
                <p class="auth-brand-foot"><?= htmlspecialchars(appTranslate('auth.brand.version', $language, ['version'=>APP_VER], 'MageStock {version}')) ?> · <?= htmlspecialchars(appTranslate('auth.brand.hosted', $language, [], 'hébergé par votre organisation')) ?></p>
            </div>

            <section class="auth-product-stage auth-product-stage--stock" aria-label="<?= htmlspecialchars(appTranslate('login.stage.aria', $language, [], 'Aperçu de démonstration des opérations de stock MageStock')) ?>">
                <div class="auth-product-stage__grid" aria-hidden="true"></div>
                <header class="auth-stage-head">
                    <div>
                        <span class="auth-stage-eyebrow"><?= htmlspecialchars(appTranslate('login.stage.eyebrow', $language, [], 'Aperçu opérationnel')) ?></span>
                        <strong><?= htmlspecialchars(appTranslate('login.stage.warehouse', $language, [], 'Magasin principal')) ?></strong>
                    </div>
                    <span class="auth-stage-status"><i></i><?= htmlspecialchars(appTranslate('login.stage.status', $language, [], 'Démonstration')) ?></span>
                </header>
                <div class="auth-stage-metrics">
                    <article class="auth-stage-metric auth-stage-metric--primary">
                        <span class="auth-stage-metric__icon"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M4 7h16v13H4z"></path><path d="M7 7V4h10v3"></path><path d="M8 12h8M8 16h5"></path></svg></span>
                        <span><?= htmlspecialchars(appTranslate('login.stage.products', $language, [], 'Références disponibles')) ?></span>
                        <strong>1 248</strong>
                    </article>
                    <article class="auth-stage-metric">
                        <span><?= htmlspecialchars(appTranslate('login.stage.inventory', $language, [], 'Inventaire en cours')) ?></span>
                        <strong><?= htmlspecialchars(appTranslate('login.stage.inventory_status', $language, [], '82 % contrôlé')) ?></strong>
                        <span class="auth-stage-progress"><i style="width:82%"></i></span>
                    </article>
                    <article class="auth-stage-metric auth-stage-metric--alert">
                        <span><?= htmlspecialchars(appTranslate('login.stage.alerts', $language, [], 'Stocks faibles')) ?></span>
                        <strong><?= htmlspecialchars(appTranslate('login.stage.alerts_value', $language, [], '12 alertes')) ?></strong>
                    </article>
                </div>
                <div class="auth-stage-warehouse" aria-hidden="true">
                    <div class="auth-stage-rack"><i></i><i></i><i></i><i></i><i></i><i></i></div>
                    <div class="auth-stage-rack auth-stage-rack--middle"><i></i><i></i><i></i><i></i><i></i><i></i></div>
                    <div class="auth-stage-rack"><i></i><i></i><i></i><i></i><i></i><i></i></div>
                    <span class="auth-stage-scanline"></span>
                </div>
                <footer class="auth-stage-movement">
                    <span class="auth-stage-movement__icon"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M5 12h14"></path><path d="m14 7 5 5-5 5"></path><path d="M5 7v10"></path></svg></span>
                    <span><small><?= htmlspecialchars(appTranslate('login.stage.movement', $language, [], 'Dernier mouvement')) ?></small><strong><?= htmlspecialchars(appTranslate('login.stage.movement_value', $language, [], 'Transfert validé')) ?></strong></span>
                    <em><?= htmlspecialchars(appTranslate('login.stage.destination', $language, [], 'Dépôt Nord')) ?></em>
                </footer>
            </section>
        </div>
    </aside>

    <main class="auth-form-panel">
        <div class="auth-toolbar">
            <label class="sr-only" for="authLanguageSelect"><?= htmlspecialchars(appTranslate('login.language', $language, [], 'Langue')) ?></label>
            <select id="authLanguageSelect" aria-label="<?= htmlspecialchars(appTranslate('login.language', $language, [], 'Langue')) ?>">
                <?php foreach(appSupportedLanguages() as $languageCode): ?>
                    <option value="<?= htmlspecialchars($languageCode) ?>" <?= $languageCode === $language ? 'selected' : '' ?>><?= htmlspecialchars($loginLangNames[$languageCode] ?? strtoupper($languageCode)) ?></option>
                <?php endforeach; ?>
            </select>
            <button type="button" id="authThemeToggle" aria-label="<?= htmlspecialchars($loginThemeLight) ?>" title="<?= htmlspecialchars($loginThemeLight) ?>">
                <svg id="authThemeIcon" viewBox="0 0 24 24" aria-hidden="true"></svg>
            </button>
        </div>

        <div class="auth-form-panel__inner">
            <p class="auth-kicker"><?= htmlspecialchars(appTranslate('login.form.kicker', $language, [], 'Bienvenue')) ?></p>
            <h1 class="auth-title"><?= htmlspecialchars(appTranslate('login.form.title', $language, [], 'Accédez à votre espace')) ?></h1>
            <p class="auth-intro"><?= htmlspecialchars(appTranslate('login.form.intro', $language, ['company'=>$company], 'Renseignez les identifiants fournis par votre organisation. MageStock vous dirigera vers les services disponibles pour votre compte.')) ?></p>

            <?php if($error !== ''): ?>
            <div class="auth-alert" role="alert">
                <svg viewBox="0 0 24 24" aria-hidden="true"><circle cx="12" cy="12" r="9"></circle><path d="M12 7v6"></path><path d="M12 17h.01"></path></svg>
                <span><?= htmlspecialchars($error) ?></span>
            </div>
            <?php endif; ?>

            <form method="post" action="<?= htmlspecialchars(appUrl('login.php')) ?>" autocomplete="off">
                <input type="hidden" name="csrf_token" value="<?= htmlspecialchars($csrfToken) ?>">
                <label class="auth-field" for="loginUsername">
                    <span class="auth-field__label"><?= htmlspecialchars(appTranslate('login.username', $language, [], 'Identifiant ou adresse e-mail')) ?></span>
                    <input class="auth-input" id="loginUsername" name="username" type="text" maxlength="190" required autocomplete="username" value="<?= htmlspecialchars((string)($_POST['username'] ?? '')) ?>">
                </label>
                <label class="auth-field" for="loginPassword">
                    <span class="auth-field__label"><?= htmlspecialchars(appTranslate('login.password', $language, [], 'Mot de passe')) ?></span>
                    <span class="auth-input-wrap">
                        <input class="auth-input auth-input--with-action" id="loginPassword" name="password" type="password" maxlength="255" required autocomplete="current-password">
                        <button class="auth-input-action" type="button" data-password-toggle="loginPassword" aria-label="<?= htmlspecialchars(appTranslate('auth.password.toggle', $language, [], 'Afficher ou masquer le mot de passe')) ?>" aria-pressed="false"><svg viewBox="0 0 24 24" aria-hidden="true"><path d="M2 12s3.5-6 10-6 10 6 10 6-3.5 6-10 6S2 12 2 12Z"></path><circle cx="12" cy="12" r="2.5"></circle></svg></button>
                    </span>
                </label>
                <button class="auth-submit" type="submit">
                    <svg viewBox="0 0 24 24" aria-hidden="true"><rect x="5" y="10" width="14" height="10" rx="1"></rect><path d="M8 10V7a4 4 0 0 1 8 0v3"></path></svg>
                    <?= htmlspecialchars(appTranslate('login.submit', $language, [], 'Accéder à mon espace')) ?>
                </button>
            </form>

            <div class="auth-links">
                <a href="<?= htmlspecialchars(appUrl('password-recovery')) ?>"><?= htmlspecialchars(appTranslate('login.forgot_password', $language, [], 'Mot de passe oublié ?')) ?></a>
            </div>
            <div class="auth-security-note">
                <svg viewBox="0 0 24 24" aria-hidden="true"><path d="M12 3l8 4v5c0 5-3.4 8.2-8 9-4.6-.8-8-4-8-9V7l8-4Z"></path><path d="M9 12l2 2 4-4"></path></svg>
                <span><?= htmlspecialchars(appTranslate('login.security_note', $language, [], 'Plusieurs tentatives incorrectes peuvent suspendre temporairement la connexion. En cas de difficulté, contactez votre administrateur.')) ?></span>
            </div>
            <footer class="auth-page-footer" aria-label="MageStock">
                <a class="auth-page-footer__brand"
                   href="https://magekorp.com/contact"
                   target="_blank"
                   rel="noopener noreferrer external"
                   aria-label="<?= htmlspecialchars(appTranslate('shell.footer.contact', $language, [], 'Contacter MageKorp')) ?>">
                    <picture>
                        <source media="(max-width: 640px)" srcset="<?= htmlspecialchars(assetUrl('img/magekorp-mark.png')) ?>">
                        <img src="<?= htmlspecialchars(assetUrl('img/magekorp-logo-horizontal.png')) ?>" alt="MageKorp">
                    </picture>
                </a>
                <div class="auth-page-footer__meta">
                    <span><?= htmlspecialchars(appTranslate('shell.footer.developed_by', $language, [], 'Développé par')) ?> <strong>Régis KREMER</strong></span>
                    <span aria-hidden="true">•</span>
                    <strong>MageStock</strong>
                    <span class="auth-page-footer__version">v<?= htmlspecialchars(APP_VER) ?></span>
                    <span aria-hidden="true">•</span>
                    <a href="https://magekorp.com/contact"
                       target="_blank"
                       rel="noopener noreferrer external"><?= htmlspecialchars(appTranslate('shell.footer.magekorp_app', $language, [], 'une application web MageKorp')) ?></a>
                    <span aria-hidden="true">•</span>
                    <span>&copy; <?= date('Y') ?></span>
                    <span aria-hidden="true">•</span>
                    <span><?= htmlspecialchars(appTranslate('shell.footer.license', $language, [], 'Licence d’utilisation')) ?></span>
                    <span aria-hidden="true">•</span>
                    <a href="https://magekorp.com/contact"
                       target="_blank"
                       rel="noopener noreferrer external"><?= htmlspecialchars(appTranslate('shell.footer.contact', $language, [], 'Contacter MageKorp')) ?></a>
                </div>
            </footer>
        </div>
    </main>
</div>
<script>
window.MageStockRuntime = <?= json_encode([
    'basePath'=>appBasePath(), 'cookiePath'=>appCookiePath(), 'apiBase'=>apiUrl(), 'assetBase'=>assetUrl(), 'version'=>APP_VER,
], JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE) ?>;
window._SM_LANG_PHP = <?= json_encode($language) ?>;
window.SM_LANG = window._SM_LANG_PHP;
</script>
<script src="<?= htmlspecialchars(assetUrl('js/i18n.js?v=' . filemtime(__DIR__ . '/assets/js/i18n.js'))) ?>"></script>
<script src="<?= htmlspecialchars(assetUrl('js/auth-gateway.js?v=2.6.121')) ?>"></script>
</body>
</html>
