<?php
/**
 * Projet Final — Blog PHP MVC (version tout-en-un)
 * Démarrage : php -S localhost:8000 app.php
 * Compte admin : admin / admin123
 *
 * Architecture MVC dans un seul fichier pour la démo.
 * Structure réelle dans src/ Controllers/ Models/ Views/
 *
 * SQL MySQL équivalent :
 * CREATE TABLE users (id INT PK AUTO_INCREMENT, username VARCHAR(60) UNIQUE NOT NULL,
 *   password_hash VARCHAR(255) NOT NULL, role ENUM('user','admin') DEFAULT 'user');
 * CREATE TABLE posts (id INT PK AUTO_INCREMENT, user_id INT FK, titre VARCHAR(200),
 *   contenu TEXT, slug VARCHAR(220) UNIQUE, publie TINYINT DEFAULT 0,
 *   created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP);
 */
session_start();

// ─────────────────────────────────────────────
// CORE — Base de données
// ─────────────────────────────────────────────
class Database {
    private static ?PDO $instance = null;

    public static function getInstance(): PDO {
        if (self::$instance === null) {
            $path = sys_get_temp_dir() . '/php_blog_mvc.sqlite';
            self::$instance = new PDO("sqlite:$path", null, null, [
                PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
                PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            ]);
            self::migrate(self::$instance);
        }
        return self::$instance;
    }

    private static function migrate(PDO $db): void {
        $db->exec('CREATE TABLE IF NOT EXISTS users (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            username TEXT UNIQUE NOT NULL,
            password_hash TEXT NOT NULL,
            role TEXT DEFAULT "user"
        )');
        $db->exec('CREATE TABLE IF NOT EXISTS posts (
            id INTEGER PRIMARY KEY AUTOINCREMENT,
            user_id INTEGER NOT NULL DEFAULT 1,
            titre TEXT NOT NULL,
            contenu TEXT NOT NULL,
            slug TEXT UNIQUE NOT NULL,
            publie INTEGER DEFAULT 0,
            created_at TEXT DEFAULT (datetime("now","localtime"))
        )');
        // Données initiales
        if ($db->query('SELECT COUNT(*) FROM users')->fetchColumn() == 0) {
            $db->prepare('INSERT INTO users (username, password_hash, role) VALUES (?,?,?)')
               ->execute(['admin', password_hash('admin123', PASSWORD_DEFAULT), 'admin']);
            $db->prepare('INSERT INTO users (username, password_hash, role) VALUES (?,?,?)')
               ->execute(['alice', password_hash('alice123', PASSWORD_DEFAULT), 'user']);
        }
        if ($db->query('SELECT COUNT(*) FROM posts')->fetchColumn() == 0) {
            $seed = $db->prepare('INSERT INTO posts (user_id, titre, contenu, slug, publie) VALUES (?,?,?,?,?)');
            foreach ([
                [1,'Bienvenue sur le Blog PHP MVC','Ce blog est construit avec PHP 8 en architecture MVC, sans framework externe. Il utilise PDO avec SQLite pour la démonstration.','bienvenue-blog-php-mvc',1],
                [1,'Les requêtes préparées PDO','Les requêtes préparées protègent contre l\'injection SQL. Avec PDO, on utilise des placeholders (:name ou ?) qui sont substitués de manière sécurisée.','requetes-preparees-pdo',1],
                [1,'La programmation orientée objet en PHP 8','PHP 8 a introduit la promotion de propriétés dans les constructeurs, les union types, et l\'expression match().','poo-php-8',1],
                [1,'API REST avec PHP natif','Construire une API REST sans framework : routage par $_SERVER[\'REQUEST_METHOD\'], réponses JSON, et authentification Bearer Token.','api-rest-php-natif',0],
            ] as $r) $seed->execute($r);
        }
    }
}

// ─────────────────────────────────────────────
// MODELS
// ─────────────────────────────────────────────
class PostModel {
    private PDO $db;
    public function __construct() { $this->db = Database::getInstance(); }

    public function findAll(bool $publishedOnly = true, string $search = '', int $limit = 10, int $offset = 0): array {
        $where = $publishedOnly ? 'WHERE p.publie = 1' : '';
        if ($search) {
            $where .= ($where ? ' AND' : 'WHERE') . ' (p.titre LIKE :q OR p.contenu LIKE :q)';
        }
        $stmt = $this->db->prepare("SELECT p.*, u.username AS auteur FROM posts p JOIN users u ON u.id = p.user_id $where ORDER BY p.id DESC LIMIT :l OFFSET :o");
        if ($search) $stmt->bindValue(':q', '%' . addcslashes($search, '%_') . '%');
        $stmt->bindValue(':l', $limit, PDO::PARAM_INT);
        $stmt->bindValue(':o', $offset, PDO::PARAM_INT);
        $stmt->execute();
        return $stmt->fetchAll();
    }

    public function count(bool $publishedOnly = true): int {
        $where = $publishedOnly ? 'WHERE publie = 1' : '';
        return (int)$this->db->query("SELECT COUNT(*) FROM posts $where")->fetchColumn();
    }

    public function findBySlug(string $slug): ?array {
        $stmt = $this->db->prepare('SELECT p.*, u.username AS auteur FROM posts p JOIN users u ON u.id = p.user_id WHERE p.slug = :s');
        $stmt->execute([':s' => $slug]);
        return $stmt->fetch() ?: null;
    }

    public function findById(int $id): ?array {
        $stmt = $this->db->prepare('SELECT * FROM posts WHERE id = :id');
        $stmt->execute([':id' => $id]);
        return $stmt->fetch() ?: null;
    }

    public function create(array $d): int {
        $slug = $this->makeSlug($d['titre']);
        $stmt = $this->db->prepare('INSERT INTO posts (user_id, titre, contenu, slug, publie) VALUES (:u,:t,:c,:s,:p)');
        $stmt->execute([':u'=>$d['user_id'],':t'=>$d['titre'],':c'=>$d['contenu'],':s'=>$slug,':p'=>(int)($d['publie']??0)]);
        return (int)$this->db->lastInsertId();
    }

    public function update(int $id, array $d): bool {
        $stmt = $this->db->prepare('UPDATE posts SET titre=:t, contenu=:c, publie=:p WHERE id=:id');
        $stmt->execute([':t'=>$d['titre'],':c'=>$d['contenu'],':p'=>(int)($d['publie']??0),':id'=>$id]);
        return $stmt->rowCount() > 0;
    }

    public function delete(int $id): bool {
        $stmt = $this->db->prepare('DELETE FROM posts WHERE id = :id');
        $stmt->execute([':id' => $id]);
        return $stmt->rowCount() > 0;
    }

    private function makeSlug(string $titre): string {
        // Translittération basique : supprimer les accents, remplacer espaces par -
        $s = mb_strtolower($titre);
        $s = preg_replace('/[àáâãäå]/u', 'a', $s);
        $s = preg_replace('/[èéêë]/u', 'e', $s);
        $s = preg_replace('/[ìíîï]/u', 'i', $s);
        $s = preg_replace('/[òóôõö]/u', 'o', $s);
        $s = preg_replace('/[ùúûü]/u', 'u', $s);
        $s = preg_replace('/[^a-z0-9]+/', '-', $s);
        $s = trim($s, '-');
        // Éviter les doublons de slug
        $base = $s;
        $i = 1;
        $db = Database::getInstance();
        while ($db->prepare('SELECT 1 FROM posts WHERE slug = ?')->execute([$s]) && $db->query("SELECT 1 FROM posts WHERE slug = '$s'")->fetchColumn()) {
            $s = $base . '-' . $i++;
        }
        return $s;
    }
}

class UserModel {
    private PDO $db;
    public function __construct() { $this->db = Database::getInstance(); }

    public function findByUsername(string $u): ?array {
        $stmt = $this->db->prepare('SELECT * FROM users WHERE username = :u');
        $stmt->execute([':u' => $u]);
        return $stmt->fetch() ?: null;
    }
}

// ─────────────────────────────────────────────
// AUTH HELPERS
// ─────────────────────────────────────────────
function auth(): ?array { return $_SESSION['auth_user'] ?? null; }
function isAdmin(): bool { return (auth()['role'] ?? '') === 'admin'; }
function requireAdmin(): void { if (!isAdmin()) { redirect('/?error=forbidden'); } }
function redirect(string $url): never { header("Location: $url"); exit; }

if (empty($_SESSION['csrf'])) $_SESSION['csrf'] = bin2hex(random_bytes(32));
function csrf(): string { return htmlspecialchars($_SESSION['csrf'], ENT_QUOTES); }
function verifyCsrf(): void {
    if (!hash_equals($_SESSION['csrf'] ?? '', $_POST['_csrf'] ?? '')) {
        redirect('/?error=csrf');
    }
    $_SESSION['csrf'] = bin2hex(random_bytes(32)); // régénérer après chaque use
}

function h(mixed $s): string { return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); }

// ─────────────────────────────────────────────
// ROUTER
// ─────────────────────────────────────────────
$method  = $_SERVER['REQUEST_METHOD'];
$uri     = parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH);
$segments = array_values(array_filter(explode('/', trim($uri, '/'))));
$route   = $segments[0] ?? '';
$sub     = $segments[1] ?? '';
$id      = isset($segments[2]) ? (int)$segments[2] : 0;

// ── API JSON ───────────────────────────────────────────────
if ($route === 'api') {
    header('Content-Type: application/json; charset=utf-8');
    $posts = new PostModel();
    if ($sub === 'posts' && !$id && $method === 'GET') {
        $page = max(1, (int)($_GET['page'] ?? 1));
        $pp   = 5;
        $data = $posts->findAll(true, '', $pp, ($page - 1) * $pp);
        $total = $posts->count(true);
        echo json_encode(['data' => $data, 'meta' => ['total' => $total, 'page' => $page, 'last_page' => ceil($total / $pp)]], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
    } elseif ($sub === 'posts' && $id && $method === 'GET') {
        $p = $posts->findById($id);
        if (!$p || !$p['publie']) { http_response_code(404); echo json_encode(['error' => 'Not found']); }
        else echo json_encode(['data' => $p], JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
    } else {
        http_response_code(404);
        echo json_encode(['error' => 'Route not found']);
    }
    exit;
}

// ─────────────────────────────────────────────
// CONTROLLERS (inline)
// ─────────────────────────────────────────────
$posts = new PostModel();
$users = new UserModel();
$flash = $_SESSION['flash'] ?? null;
unset($_SESSION['flash']);
$error = $_GET['error'] ?? null;

// ── POST /login ────────────────────────────────────────────
if ($route === 'login' && $method === 'POST') {
    verifyCsrf();
    $username = trim($_POST['username'] ?? '');
    $password = $_POST['password'] ?? '';
    $user = $users->findByUsername($username);
    if ($user && password_verify($password, $user['password_hash'])) {
        session_regenerate_id(true); // anti session-fixation
        $_SESSION['auth_user'] = ['id' => $user['id'], 'username' => $user['username'], 'role' => $user['role']];
        redirect('/admin');
    }
    $_SESSION['flash'] = ['type' => 'error', 'msg' => 'Identifiants incorrects.'];
    redirect('/login');
}

// ── GET /logout ────────────────────────────────────────────
if ($route === 'logout') {
    $_SESSION = [];
    if (ini_get('session.use_cookies')) {
        $p = session_get_cookie_params();
        setcookie(session_name(), '', time()-42000, $p['path'], $p['domain'], $p['secure'], $p['httponly']);
    }
    session_destroy();
    redirect('/');
}

// ── POST /admin/posts/store ────────────────────────────────
if ($route === 'admin' && $sub === 'posts' && $id === 0 && $method === 'POST') {
    requireAdmin(); verifyCsrf();
    $titre   = trim($_POST['titre'] ?? '');
    $contenu = trim($_POST['contenu'] ?? '');
    $errors  = [];
    if (strlen($titre) < 3)    $errors[] = 'Titre trop court';
    if (strlen($contenu) < 20) $errors[] = 'Contenu trop court';
    if ($errors) { $_SESSION['flash'] = ['type'=>'error','msg'=>implode(', ', $errors)]; redirect('/admin/posts/new'); }
    $newId = $posts->create(['user_id' => auth()['id'], 'titre' => $titre, 'contenu' => $contenu, 'publie' => isset($_POST['publie']) ? 1 : 0]);
    $_SESSION['flash'] = ['type'=>'success','msg'=>"Article #$newId créé."];
    redirect('/admin');
}

// ── POST /admin/posts/{id}/update ─────────────────────────
if ($route === 'admin' && $sub === 'posts' && $id && $method === 'POST' && ($_GET['_method'] ?? '') === 'PUT') {
    requireAdmin(); verifyCsrf();
    $titre   = trim($_POST['titre'] ?? '');
    $contenu = trim($_POST['contenu'] ?? '');
    if (strlen($titre) < 3 || strlen($contenu) < 20) { $_SESSION['flash'] = ['type'=>'error','msg'=>'Données invalides']; redirect("/admin/posts/$id/edit"); }
    $posts->update($id, ['titre' => $titre, 'contenu' => $contenu, 'publie' => isset($_POST['publie']) ? 1 : 0]);
    $_SESSION['flash'] = ['type'=>'success','msg'=>"Article #$id modifié."];
    redirect('/admin');
}

// ── POST /admin/posts/{id}/delete ─────────────────────────
if ($route === 'admin' && $sub === 'posts' && $id && $method === 'POST' && ($_GET['_method'] ?? '') === 'DELETE') {
    requireAdmin(); verifyCsrf();
    $posts->delete($id);
    $_SESSION['flash'] = ['type'=>'success','msg'=>"Article #$id supprimé."];
    redirect('/admin');
}

// ─────────────────────────────────────────────
// VIEWS
// ─────────────────────────────────────────────
$search = trim($_GET['q'] ?? '');
$page   = max(1, (int)($_GET['page'] ?? 1));
$perPage = 5;

// Collecter les données selon la route
if ($route === '' || $route === 'home') {
    $articles = $posts->findAll(true, $search, $perPage, ($page - 1) * $perPage);
    $total    = $posts->count(true);
    $view     = 'home';
} elseif ($route === 'post' && $sub) {
    $article = $posts->findBySlug($sub);
    if (!$article || !$article['publie']) { http_response_code(404); $view = '404'; }
    else $view = 'post-show';
} elseif ($route === 'login') {
    $view = 'login';
} elseif ($route === 'admin') {
    requireAdmin();
    if (!$sub || $sub === 'posts' && !$id) {
        $articles = $posts->findAll(false);
        $view = 'admin-list';
    } elseif ($sub === 'posts' && ($id === 0 || $segments[2] === 'new')) {
        $view = 'admin-new';
    } elseif ($sub === 'posts' && $id && (($segments[3] ?? '') === 'edit')) {
        $article = $posts->findById($id);
        $view = $article ? 'admin-edit' : '404';
    } else {
        $view = 'admin-list';
    }
} else {
    http_response_code(404);
    $view = '404';
}

// ─────────────────────────────────────────────
// LAYOUT
// ─────────────────────────────────────────────
?>
<!DOCTYPE html>
<html lang="fr">
<head>
  <meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Blog PHP MVC<?= isset($article) ? ' — ' . h($article['titre'] ?? '') : '' ?></title>
  <style>
    *{margin:0;padding:0;box-sizing:border-box}
    body{background:#0a0e1a;color:#e6edf3;font-family:'Segoe UI',sans-serif;line-height:1.6}
    a{color:inherit;text-decoration:none}
    /* Navigation */
    .nav{background:rgba(13,17,23,.95);border-bottom:1px solid rgba(255,255,255,.08);padding:.8rem 1.5rem;display:flex;align-items:center;justify-content:space-between;position:sticky;top:0;z-index:100;backdrop-filter:blur(10px)}
    .nav-brand{font-weight:800;color:#8892be;font-size:1rem}
    .nav-links{display:flex;gap:1rem;align-items:center;font-size:.85rem}
    .nav-links a{color:#8b949e;transition:color .2s}.nav-links a:hover{color:#e6edf3}
    .nav-links .active{color:#8892be}
    /* Layout */
    .container{max-width:800px;margin:0 auto;padding:2rem 1rem}
    /* Cards */
    .post-card{background:rgba(22,27,34,.7);border:1px solid rgba(255,255,255,.07);border-radius:12px;padding:1.4rem;margin-bottom:1rem;transition:border-color .2s}
    .post-card:hover{border-color:rgba(136,146,190,.3)}
    .post-title{font-size:1.1rem;font-weight:700;margin-bottom:.4rem}
    .post-title a:hover{color:#8892be}
    .post-meta{font-size:.76rem;color:#6e7681;margin-bottom:.7rem}
    .post-excerpt{color:#8b949e;font-size:.87rem}
    .badge{display:inline-block;padding:.15rem .5rem;border-radius:4px;font-size:.68rem;font-weight:700}
    .badge-pub{background:rgba(63,185,80,.15);color:#3fb950}
    .badge-draft{background:rgba(139,148,158,.1);color:#8b949e}
    /* Forms */
    input,textarea,select{width:100%;background:rgba(255,255,255,.04);border:1px solid rgba(255,255,255,.1);border-radius:8px;color:#e6edf3;padding:.6rem .9rem;font-size:.88rem;font-family:inherit;outline:none;margin-bottom:.8rem}
    input:focus,textarea:focus{border-color:rgba(136,146,190,.5);box-shadow:0 0 0 3px rgba(136,146,190,.08)}
    label{display:block;font-size:.78rem;color:#8b949e;margin-bottom:.25rem;font-weight:600}
    .check-row{display:flex;align-items:center;gap:.5rem;margin-bottom:.8rem;font-size:.85rem;color:#8b949e}
    .btn{padding:.6rem 1.2rem;border:none;border-radius:8px;font-weight:700;font-size:.85rem;cursor:pointer;transition:opacity .2s;display:inline-block}
    .btn:hover{opacity:.82}
    .btn-php{background:linear-gradient(135deg,#8892be,#b0b9e8);color:#0d1117}
    .btn-danger{background:rgba(248,81,73,.15);color:#f85149;border:1px solid rgba(248,81,73,.3)}
    .btn-ghost{background:rgba(255,255,255,.05);color:#8b949e}
    /* Flash */
    .flash{border-radius:8px;padding:.7rem 1rem;margin-bottom:1.2rem;font-size:.85rem}
    .flash-success{background:rgba(63,185,80,.1);border:1px solid rgba(63,185,80,.3);color:#3fb950}
    .flash-error{background:rgba(248,81,73,.1);border:1px solid rgba(248,81,73,.3);color:#f85149}
    /* Article */
    .article-content{color:#c9d1d9;font-size:.93rem;line-height:1.8;margin-top:1rem}
    /* Search */
    .search-form{display:flex;gap:.5rem;margin-bottom:1.5rem}
    .search-form input{margin:0}
    /* Admin */
    .admin-actions{display:flex;gap:.5rem;margin-top:.6rem}
    .page-header{margin-bottom:2rem}
    .page-header h1{font-size:1.6rem;font-weight:800;color:#8892be}
    /* Pagination */
    .pagination{display:flex;gap:.4rem;margin-top:1.2rem;justify-content:center}
    .pagination a,.pagination span{padding:.4rem .7rem;border-radius:6px;font-size:.82rem;background:rgba(255,255,255,.05);color:#8b949e}
    .pagination a:hover{background:rgba(136,146,190,.15);color:#8892be}
    .pagination .current{background:rgba(136,146,190,.2);color:#8892be;font-weight:700}
    /* API badge */
    .api-badge{display:inline-block;background:rgba(63,185,80,.1);border:1px solid rgba(63,185,80,.2);border-radius:6px;padding:.3rem .7rem;font-size:.75rem;color:#3fb950;font-family:monospace}
  </style>
</head>
<body>

<nav class="nav">
  <a class="nav-brand" href="/">🐘 Blog PHP MVC</a>
  <div class="nav-links">
    <a href="/" class="<?= ($route===''||$route==='home')?'active':'' ?>">Accueil</a>
    <a href="/api/posts" target="_blank" class="api-badge">API JSON</a>
    <?php if (auth()): ?>
      <?php if (isAdmin()): ?><a href="/admin" class="<?= $route==='admin'?'active':'' ?>">Admin</a><?php endif; ?>
      <a href="/logout" class="btn btn-ghost" style="padding:.3rem .7rem;font-size:.8rem">Déconnexion (<?= h(auth()['username']) ?>)</a>
    <?php else: ?>
      <a href="/login" class="btn btn-php" style="padding:.35rem .8rem">Connexion</a>
    <?php endif; ?>
  </div>
</nav>

<div class="container">

<?php if ($flash): ?>
  <div class="flash flash-<?= h($flash['type']) ?>"><?= $flash['type']==='success'?'✅':'❌' ?> <?= h($flash['msg']) ?></div>
<?php endif; ?>

<?php if ($error === 'forbidden'): ?>
  <div class="flash flash-error">⛔ Accès refusé — authentification admin requise.</div>
<?php elseif ($error === 'csrf'): ?>
  <div class="flash flash-error">⛔ Requête invalide (CSRF).</div>
<?php endif; ?>

<?php // ── HOME ───────────────────────────────────────────
if ($view === 'home'): ?>
  <div class="page-header">
    <h1 style="font-size:1.6rem;font-weight:800;background:linear-gradient(135deg,#8892be,#b0b9e8);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text">Derniers articles</h1>
  </div>
  <form class="search-form" method="GET" action="/">
    <input type="text" name="q" placeholder="Rechercher..." value="<?= h($search) ?>">
    <button type="submit" class="btn btn-ghost">🔍</button>
    <?php if ($search): ?><a href="/" class="btn btn-ghost">✕</a><?php endif; ?>
  </form>
  <?php foreach ($articles as $p): ?>
    <div class="post-card">
      <div class="post-title"><a href="/post/<?= h($p['slug']) ?>"><?= h($p['titre']) ?></a></div>
      <div class="post-meta">Par <?= h($p['auteur']) ?> · <?= h($p['created_at']) ?></div>
      <div class="post-excerpt"><?= h(mb_substr(strip_tags($p['contenu']), 0, 150)) ?>…</div>
    </div>
  <?php endforeach; ?>
  <?php if (empty($articles)): ?><p style="color:#6e7681">Aucun article<?= $search?" pour \"".h($search).'"":'' ?>.</p><?php endif; ?>
  <?php
    $lastPage = (int)ceil($total / $perPage);
    if ($lastPage > 1):
  ?>
  <div class="pagination">
    <?php for ($i = 1; $i <= $lastPage; $i++): ?>
      <?php if ($i === $page): ?><span class="current"><?= $i ?></span>
      <?php else: ?><a href="?page=<?= $i ?><?= $search ? '&q='.urlencode($search) : '' ?>"><?= $i ?></a><?php endif; ?>
    <?php endfor; ?>
  </div>
  <?php endif; ?>

<?php // ── POST SHOW ──────────────────────────────────────
elseif ($view === 'post-show'): ?>
  <a href="/" style="color:#8b949e;font-size:.85rem;display:inline-block;margin-bottom:1rem">← Retour</a>
  <h1 style="font-size:1.7rem;font-weight:800;margin-bottom:.4rem"><?= h($article['titre']) ?></h1>
  <p style="color:#6e7681;font-size:.8rem;margin-bottom:1.5rem">Par <?= h($article['auteur']) ?> · <?= h($article['created_at']) ?></p>
  <div class="article-content"><?= nl2br(h($article['contenu'])) ?></div>
  <?php if (isAdmin()): ?>
    <div style="margin-top:1.5rem;padding-top:1rem;border-top:1px solid rgba(255,255,255,.07)">
      <a href="/admin/posts/<?= $article['id'] ?>/edit" class="btn btn-php">✏️ Modifier</a>
    </div>
  <?php endif; ?>

<?php // ── LOGIN ──────────────────────────────────────────
elseif ($view === 'login'): ?>
  <div style="max-width:380px;margin:3rem auto;background:rgba(22,27,34,.8);border:1px solid rgba(255,255,255,.08);border-radius:16px;padding:2rem;backdrop-filter:blur(20px)">
    <h1 style="font-size:1.3rem;font-weight:700;margin-bottom:.3rem;color:#8892be">🔐 Connexion</h1>
    <p style="color:#8b949e;font-size:.82rem;margin-bottom:1.5rem">admin / admin123 &nbsp;·&nbsp; alice / alice123</p>
    <form method="POST" action="/login">
      <input type="hidden" name="_csrf" value="<?= csrf() ?>">
      <label>Nom d'utilisateur</label>
      <input type="text" name="username" required autocomplete="username">
      <label>Mot de passe</label>
      <input type="password" name="password" required autocomplete="current-password">
      <button type="submit" class="btn btn-php" style="width:100%;margin-top:.3rem">Se connecter →</button>
    </form>
  </div>

<?php // ── ADMIN LIST ────────────────────────────────────
elseif ($view === 'admin-list'): ?>
  <div class="page-header" style="display:flex;justify-content:space-between;align-items:center">
    <h1 class="page-header" style="margin:0">Administration</h1>
    <a href="/admin/posts/new" class="btn btn-php">+ Nouvel article</a>
  </div>
  <?php foreach ($articles as $p): ?>
    <div class="post-card">
      <div class="post-title"><?= h($p['titre']) ?> <span class="badge <?= $p['publie']?'badge-pub':'badge-draft' ?>"><?= $p['publie']?'publié':'brouillon' ?></span></div>
      <div class="post-meta">ID #<?= $p['id'] ?> · <?= h($p['created_at']) ?></div>
      <div class="admin-actions">
        <a href="/post/<?= h($p['slug']) ?>" class="btn btn-ghost">👁 Voir</a>
        <a href="/admin/posts/<?= $p['id'] ?>/edit" class="btn btn-php">✏️ Modifier</a>
        <form method="POST" action="/admin/posts/<?= $p['id'] ?>?_method=DELETE" style="display:inline" onsubmit="return confirm('Supprimer cet article ?')">
          <input type="hidden" name="_csrf" value="<?= csrf() ?>">
          <button type="submit" class="btn btn-danger">🗑️</button>
        </form>
      </div>
    </div>
  <?php endforeach; ?>

<?php // ── ADMIN NEW ─────────────────────────────────────
elseif ($view === 'admin-new'): ?>
  <div class="page-header"><h1>Nouvel article</h1></div>
  <form method="POST" action="/admin/posts" style="background:rgba(22,27,34,.7);border:1px solid rgba(255,255,255,.07);border-radius:12px;padding:1.5rem">
    <input type="hidden" name="_csrf" value="<?= csrf() ?>">
    <label>Titre *</label>
    <input type="text" name="titre" required minlength="3" value="<?= h($_POST['titre'] ?? '') ?>">
    <label>Contenu *</label>
    <textarea name="contenu" rows="10" required minlength="20"><?= h($_POST['contenu'] ?? '') ?></textarea>
    <div class="check-row">
      <input type="checkbox" id="publie" name="publie" style="width:auto;margin:0">
      <label for="publie" style="margin:0;cursor:pointer">Publier immédiatement</label>
    </div>
    <button type="submit" class="btn btn-php">Créer l'article</button>
    <a href="/admin" class="btn btn-ghost" style="margin-left:.5rem">Annuler</a>
  </form>

<?php // ── ADMIN EDIT ────────────────────────────────────
elseif ($view === 'admin-edit'): ?>
  <div class="page-header"><h1>Modifier l'article #<?= $article['id'] ?></h1></div>
  <form method="POST" action="/admin/posts/<?= $article['id'] ?>?_method=PUT" style="background:rgba(22,27,34,.7);border:1px solid rgba(255,255,255,.07);border-radius:12px;padding:1.5rem">
    <input type="hidden" name="_csrf" value="<?= csrf() ?>">
    <label>Titre *</label>
    <input type="text" name="titre" required minlength="3" value="<?= h($article['titre']) ?>">
    <label>Contenu *</label>
    <textarea name="contenu" rows="10" required minlength="20"><?= h($article['contenu']) ?></textarea>
    <div class="check-row">
      <input type="checkbox" id="publie" name="publie" style="width:auto;margin:0" <?= $article['publie']?'checked':'' ?>>
      <label for="publie" style="margin:0;cursor:pointer">Publier</label>
    </div>
    <button type="submit" class="btn btn-php">Enregistrer</button>
    <a href="/admin" class="btn btn-ghost" style="margin-left:.5rem">Annuler</a>
  </form>

<?php // ── 404 ───────────────────────────────────────────
else: ?>
  <div style="text-align:center;padding:4rem 0">
    <div style="font-size:3rem;margin-bottom:1rem">404</div>
    <p style="color:#8b949e">Page introuvable.</p>
    <a href="/" class="btn btn-php" style="margin-top:1rem">Retour à l'accueil</a>
  </div>
<?php endif; ?>

</div>

<footer style="text-align:center;padding:2rem;color:#6e7681;font-size:.78rem;border-top:1px solid rgba(255,255,255,.05);margin-top:3rem">
  Blog PHP MVC · Projet Final · Formation PHP
</footer>
</body>
</html>
