<?php
/**
 * API REST — Boutique en Ligne (PHP + PDO)
 * Formation MySQL — Projet Final
 *
 * Endpoints :
 *   GET    /api/produits          Liste paginée (?page=1&limit=10&cat=ID)
 *   GET    /api/produits/:id      Détail
 *   POST   /api/produits          Créer
 *   PUT    /api/produits/:id      Mettre à jour
 *   DELETE /api/produits/:id      Soft delete (actif=0)
 *   GET    /api/categories        Liste
 *   POST   /api/categories        Créer
 *   GET    /api/stats             KPIs globaux
 *
 * Lancer : php -S localhost:8080
 * Tester : curl http://localhost:8080/app.php/api/produits
 */

// ---------------------------------------------------------------------------
// Configuration
// ---------------------------------------------------------------------------
define('DB_HOST', '127.0.0.1');
define('DB_NAME', 'formation_mysql');
define('DB_USER', 'root');
define('DB_PASS', '');
define('DB_PORT', 3306);

// ---------------------------------------------------------------------------
// Headers CORS + JSON
// ---------------------------------------------------------------------------
header('Content-Type: application/json; charset=utf-8');
header('Access-Control-Allow-Origin: *');
header('Access-Control-Allow-Methods: GET, POST, PUT, DELETE, OPTIONS');
header('Access-Control-Allow-Headers: Content-Type');

if ($_SERVER['REQUEST_METHOD'] === 'OPTIONS') {
    http_response_code(204);
    exit;
}

// ---------------------------------------------------------------------------
// Connexion PDO
// ---------------------------------------------------------------------------
function getPDO(): PDO {
    static $pdo = null;
    if ($pdo !== null) return $pdo;

    $dsn = sprintf('mysql:host=%s;port=%d;dbname=%s;charset=utf8mb4',
        DB_HOST, DB_PORT, DB_NAME);
    try {
        $pdo = new PDO($dsn, DB_USER, DB_PASS, [
            PDO::ATTR_ERRMODE            => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_EMULATE_PREPARES   => false,
        ]);
    } catch (PDOException $e) {
        jsonError(500, 'Connexion base de données impossible : ' . $e->getMessage());
    }
    return $pdo;
}

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function jsonResponse(int $code, mixed $data): never {
    http_response_code($code);
    echo json_encode($data, JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT);
    exit;
}

function jsonError(int $code, string $message): never {
    jsonResponse($code, ['error' => $message]);
}

function bodyJson(): array {
    $raw = file_get_contents('php://input');
    $data = json_decode($raw, true);
    if ($data === null && $raw !== '') {
        jsonError(400, 'Corps JSON invalide');
    }
    return $data ?? [];
}

function requireFields(array $data, array $fields): void {
    foreach ($fields as $f) {
        if (!isset($data[$f]) || $data[$f] === '') {
            jsonError(400, "Champ requis manquant : $f");
        }
    }
}

// ---------------------------------------------------------------------------
// Routage
// ---------------------------------------------------------------------------
$method = $_SERVER['REQUEST_METHOD'];
// Support php -S : PATH_INFO ou REQUEST_URI nettoyé
$pathInfo = $_SERVER['PATH_INFO']
    ?? (parse_url($_SERVER['REQUEST_URI'], PHP_URL_PATH) ?? '/');
// Retirer le nom du script si présent
$pathInfo = preg_replace('#^/app\.php#', '', $pathInfo);
$segments = array_filter(explode('/', trim($pathInfo, '/')));
$segments = array_values($segments);  // réindexer

// $segments[0] = 'api', $segments[1] = ressource, $segments[2] = id
$resource = $segments[1] ?? '';
$id       = isset($segments[2]) ? (int)$segments[2] : null;

match (true) {
    // Produits
    $resource === 'produits' && $method === 'GET'    && $id === null => handleGetProduits(),
    $resource === 'produits' && $method === 'GET'    && $id !== null => handleGetProduit($id),
    $resource === 'produits' && $method === 'POST'                   => handlePostProduit(),
    $resource === 'produits' && $method === 'PUT'    && $id !== null => handlePutProduit($id),
    $resource === 'produits' && $method === 'DELETE' && $id !== null => handleDeleteProduit($id),
    // Catégories
    $resource === 'categories' && $method === 'GET'  => handleGetCategories(),
    $resource === 'categories' && $method === 'POST' => handlePostCategorie(),
    // Stats
    $resource === 'stats' && $method === 'GET'       => handleGetStats(),
    // Défaut
    default => jsonError(404, 'Route introuvable')
};

// ===========================================================================
// HANDLERS — Produits
// ===========================================================================

function handleGetProduits(): never {
    $pdo   = getPDO();
    $page  = max(1, (int)($_GET['page']  ?? 1));
    $limit = min(50, max(1, (int)($_GET['limit'] ?? 10)));
    $cat   = isset($_GET['cat']) ? (int)$_GET['cat'] : null;
    $search = trim($_GET['q'] ?? '');
    $offset = ($page - 1) * $limit;

    $where = ['p.actif = 1'];
    $params = [];

    if ($cat) {
        $where[] = 'p.categorie_id = :cat';
        $params[':cat'] = $cat;
    }
    if ($search !== '') {
        $where[] = '(p.nom LIKE :search OR p.description LIKE :search)';
        $params[':search'] = "%$search%";
    }

    $whereSQL = implode(' AND ', $where);

    // Total
    $stmtCount = $pdo->prepare(
        "SELECT COUNT(*) FROM produits p WHERE $whereSQL"
    );
    $stmtCount->execute($params);
    $total = (int)$stmtCount->fetchColumn();

    // Données
    $paramsData = array_merge($params, [':limit' => $limit, ':offset' => $offset]);
    $stmt = $pdo->prepare("
        SELECT p.id, p.nom, p.description, p.prix,
               ROUND(p.prix * 1.20, 2) AS prix_ttc,
               p.stock, p.actif,
               c.id AS categorie_id, c.nom AS categorie
        FROM produits p
        JOIN categories c ON p.categorie_id = c.id
        WHERE $whereSQL
        ORDER BY p.nom
        LIMIT :limit OFFSET :offset
    ");
    // PDO bindValue pour LIMIT/OFFSET (typage entier obligatoire)
    foreach ($params as $key => $val) {
        $stmt->bindValue($key, $val);
    }
    $stmt->bindValue(':limit',  $limit,  PDO::PARAM_INT);
    $stmt->bindValue(':offset', $offset, PDO::PARAM_INT);
    $stmt->execute();
    $data = $stmt->fetchAll();

    jsonResponse(200, [
        'data'  => $data,
        'total' => $total,
        'page'  => $page,
        'pages' => (int)ceil($total / $limit),
    ]);
}

function handleGetProduit(int $id): never {
    $pdo = getPDO();
    $stmt = $pdo->prepare("
        SELECT p.id, p.nom, p.description, p.prix,
               ROUND(p.prix * 1.20, 2) AS prix_ttc,
               p.stock, p.actif, p.created_at, p.updated_at,
               c.id AS categorie_id, c.nom AS categorie
        FROM produits p
        JOIN categories c ON p.categorie_id = c.id
        WHERE p.id = :id
    ");
    $stmt->execute([':id' => $id]);
    $produit = $stmt->fetch();

    if (!$produit) jsonError(404, "Produit #$id introuvable");
    jsonResponse(200, $produit);
}

function handlePostProduit(): never {
    $body = bodyJson();
    requireFields($body, ['nom', 'prix', 'categorie_id']);

    $pdo = getPDO();

    // Vérifier que la catégorie existe
    $stmtCat = $pdo->prepare('SELECT id FROM categories WHERE id = :id');
    $stmtCat->execute([':id' => (int)$body['categorie_id']]);
    if (!$stmtCat->fetch()) jsonError(400, 'categorie_id inexistant');

    $stmt = $pdo->prepare("
        INSERT INTO produits (nom, description, prix, stock, categorie_id)
        VALUES (:nom, :description, :prix, :stock, :categorie_id)
    ");
    $stmt->execute([
        ':nom'          => trim($body['nom']),
        ':description'  => trim($body['description'] ?? ''),
        ':prix'         => (float)$body['prix'],
        ':stock'        => (int)($body['stock'] ?? 0),
        ':categorie_id' => (int)$body['categorie_id'],
    ]);

    $newId = (int)$pdo->lastInsertId();
    handleGetProduit($newId); // retourne le produit créé avec 201
    // Note: handleGetProduit fait exit, mais le code 201 est défini ici
}

function handlePutProduit(int $id): never {
    $pdo = getPDO();

    // Vérifier existence
    $check = $pdo->prepare('SELECT id FROM produits WHERE id = :id');
    $check->execute([':id' => $id]);
    if (!$check->fetch()) jsonError(404, "Produit #$id introuvable");

    $body = bodyJson();
    $fields = [];
    $params = [':id' => $id];

    if (isset($body['nom'])) {
        $fields[] = 'nom = :nom';
        $params[':nom'] = trim($body['nom']);
    }
    if (isset($body['description'])) {
        $fields[] = 'description = :description';
        $params[':description'] = trim($body['description']);
    }
    if (isset($body['prix'])) {
        $fields[] = 'prix = :prix';
        $params[':prix'] = (float)$body['prix'];
    }
    if (isset($body['stock'])) {
        $fields[] = 'stock = :stock';
        $params[':stock'] = (int)$body['stock'];
    }
    if (isset($body['categorie_id'])) {
        $fields[] = 'categorie_id = :categorie_id';
        $params[':categorie_id'] = (int)$body['categorie_id'];
    }
    if (isset($body['actif'])) {
        $fields[] = 'actif = :actif';
        $params[':actif'] = (int)(bool)$body['actif'];
    }

    if (empty($fields)) jsonError(400, 'Aucun champ à mettre à jour');

    $pdo->prepare('UPDATE produits SET ' . implode(', ', $fields) . ' WHERE id = :id')
        ->execute($params);

    handleGetProduit($id);
}

function handleDeleteProduit(int $id): never {
    $pdo = getPDO();
    $check = $pdo->prepare('SELECT id FROM produits WHERE id = :id');
    $check->execute([':id' => $id]);
    if (!$check->fetch()) jsonError(404, "Produit #$id introuvable");

    // Soft delete : on désactive le produit sans le supprimer
    $pdo->prepare('UPDATE produits SET actif = 0 WHERE id = :id')
        ->execute([':id' => $id]);

    jsonResponse(200, ['message' => "Produit #$id désactivé", 'id' => $id]);
}

// ===========================================================================
// HANDLERS — Catégories
// ===========================================================================

function handleGetCategories(): never {
    $pdo = getPDO();
    $stmt = $pdo->query("
        SELECT c.id, c.nom, c.description,
               COUNT(p.id) AS nb_produits
        FROM categories c
        LEFT JOIN produits p ON p.categorie_id = c.id AND p.actif = 1
        GROUP BY c.id, c.nom, c.description
        ORDER BY c.nom
    ");
    jsonResponse(200, $stmt->fetchAll());
}

function handlePostCategorie(): never {
    $body = bodyJson();
    requireFields($body, ['nom']);

    $pdo = getPDO();

    // Unicité
    $check = $pdo->prepare('SELECT id FROM categories WHERE nom = :nom');
    $check->execute([':nom' => trim($body['nom'])]);
    if ($check->fetch()) jsonError(409, 'Une catégorie avec ce nom existe déjà');

    $stmt = $pdo->prepare('INSERT INTO categories (nom, description) VALUES (:nom, :desc)');
    $stmt->execute([
        ':nom'  => trim($body['nom']),
        ':desc' => trim($body['description'] ?? ''),
    ]);

    $newId = (int)$pdo->lastInsertId();
    $cat = $pdo->prepare('SELECT * FROM categories WHERE id = :id');
    $cat->execute([':id' => $newId]);
    jsonResponse(201, $cat->fetch());
}

// ===========================================================================
// HANDLER — Stats
// ===========================================================================

function handleGetStats(): never {
    $pdo = getPDO();

    $stats = [];

    // KPIs globaux
    $row = $pdo->query("
        SELECT
            COUNT(DISTINCT cl.id)                          AS nb_clients,
            COUNT(DISTINCT co.id)                          AS nb_commandes,
            ROUND(COALESCE(SUM(co.total), 0), 2)           AS ca_total,
            ROUND(COALESCE(AVG(co.total), 0), 2)           AS panier_moyen,
            COUNT(DISTINCT p.id)                           AS nb_produits_actifs
        FROM clients cl
        LEFT JOIN commandes co ON cl.id = co.client_id AND co.statut != 'annulee'
        CROSS JOIN (SELECT COUNT(*) AS c FROM produits WHERE actif = 1) p
    ")->fetch();
    $stats['kpi'] = $row;

    // Répartition par statut
    $stats['par_statut'] = $pdo->query("
        SELECT statut, COUNT(*) AS nb, ROUND(SUM(total), 2) AS total
        FROM commandes
        GROUP BY statut
        ORDER BY nb DESC
    ")->fetchAll();

    // Top 5 catégories par CA
    $stats['top_categories'] = $pdo->query("
        SELECT c.nom AS categorie,
               SUM(cp.quantite * cp.prix_unitaire) AS ca,
               SUM(cp.quantite) AS unites_vendues
        FROM commande_produits cp
        JOIN produits p ON cp.produit_id = p.id
        JOIN categories c ON p.categorie_id = c.id
        JOIN commandes co ON cp.commande_id = co.id
        WHERE co.statut != 'annulee'
        GROUP BY c.id, c.nom
        ORDER BY ca DESC
        LIMIT 5
    ")->fetchAll();

    // Stock critique
    $stats['stock_critique'] = $pdo->query("
        SELECT nom, stock, categorie FROM v_catalogue
        WHERE disponibilite IN ('Rupture', 'Stock faible')
        ORDER BY stock
    ")->fetchAll();

    jsonResponse(200, $stats);
}
