<?php
/**
 * Fichier  : api/delivery_note.php
 * Auteur   : KREMER Régis
 * Version  : 2.6.87
 * Date     : 2026-07-20
 * Description : Génération bon de livraison HTML professionnel (individuel, chantier, sélection groupée simple)
 * Changelog :
 *   v2.6.87 (2026-07-20) — RECETTE11 : formatage des unités compatible avec PHP sans mbstring.
 *   v2.6.23 (2026-07-08) — BL : marges A4 réelles, en-tête resserré, logo MageStock print.
 *   v3.4.0 (2026-06-02) — BL simplifié : document plus lisible et colonnes réduites.
 *   v2.6.8  (2026-07-08) — En-tête et feuille A4 alignés sur le modèle MageStock.
 *   v2.6.15 (2026-07-08) — Trame print premium commune : logo entreprise prioritaire,
 *                       logo MageStock secondaire et boutons portrait/paysage.
 *   v3.3.0 (2026-06-02) — BL : magasins multiples, destination/réceptionnaire corrigés, objet prédéfini, colonnes simplifiées.
 *   v3.1.0 (2026-06-02) — Ajout du mode ?ids= pour BL groupé multi-sorties sélectionnées.
 *   v3.0.0 (2026-03-09) — Génération BL individuel et BL groupé chantier.
 *
 * Modes :
 *   ?movementId=XXX                    -> BL individuel
 *   ?site=XXX&date=YYYY-MM-DD          -> BL groupé chantier
 *   ?ids=ID1,ID2,ID3                   -> BL groupé par sélection de mouvements
 *   Options : recipient, destination, subject, merge=1, images=1
 */
require_once dirname(__DIR__).'/config.php';
startSession();
requireAuth();

$movementId  = $_GET['movementId'] ?? null;
$site        = trim((string)($_GET['site'] ?? ''));
$date        = trim((string)($_GET['date'] ?? ''));
$idsParam    = trim((string)($_GET['ids'] ?? ''));
$recipient   = trim((string)($_GET['recipient'] ?? ''));
$destination = trim((string)($_GET['destination'] ?? ''));
$subject     = trim((string)($_GET['subject'] ?? ''));
$mergeLines  = (string)($_GET['merge'] ?? '') === '1';
$showImages  = (string)($_GET['images'] ?? '') !== '0';

$allMvs   = loadData('movements');
$prods    = loadData('products');
$whs      = loadData('warehouses');
$settings = getSettings();
$company  = trim((string)($settings['companyName'] ?? '')) ?: 'MageStock';
$user     = currentUser();
$selectedMvs = [];

$findMovement = static function(array $movements, $id): ?array {
    foreach ($movements as $m) {
        if ((string)($m['id'] ?? '') === (string)$id) return $m;
    }
    return null;
};

if ($idsParam !== '') {
    $ids = array_values(array_unique(array_filter(array_map('trim', explode(',', $idsParam)))));
    foreach ($ids as $id) {
        $m = $findMovement($allMvs, $id);
        if ($m && in_array($m['type'] ?? '', ['remove', 'transfer'], true)) {
            $selectedMvs[] = $m;
        }
    }
    if (!$selectedMvs) { http_response_code(404); echo 'Aucune sortie sélectionnable trouvée.'; exit; }
} elseif ($movementId) {
    $m = $findMovement($allMvs, $movementId);
    if ($m) $selectedMvs[] = $m;
    if (!$selectedMvs) { http_response_code(404); echo 'Mouvement introuvable.'; exit; }
} elseif ($site !== '' && $date !== '') {
    foreach ($allMvs as $m) {
        $mvDate = substr($m['date'] ?? '', 0, 10);
        if (($m['site'] ?? '') === $site && $mvDate === $date && in_array($m['type'] ?? '', ['remove','transfer'], true)) {
            $selectedMvs[] = $m;
        }
    }
    if (!$selectedMvs) { http_response_code(404); echo 'Aucun mouvement trouvé.'; exit; }
} else {
    http_response_code(400); echo 'movementId, ids ou (site + date) requis.'; exit;
}

$e = static function($s){ return htmlspecialchars((string)$s, ENT_QUOTES, 'UTF-8'); };
$findBy = static function(array $items, $id): ?array {
    foreach ($items as $it) {
        if ((string)($it['id'] ?? '') === (string)$id) return $it;
    }
    return null;
};
$productImageUrl = static function(array $p): string {
    $img = trim((string)($p['image'] ?? ''));
    if ($img === '') return '';
    if (preg_match('~^https?://~i', $img)) return $img;
    if (preg_match('#^(?:data|blob):#i', $img)) return $img;
    return appUrl(ltrim($img, '/'));
};

if ($mergeLines && count($selectedMvs) > 1) {
    $merged = [];
    foreach ($selectedMvs as $mv) {
        $key = implode('|', [
            (string)($mv['productId'] ?? ''),
            (string)($mv['warehouseId'] ?? ''),
            (string)($mv['transferTo'] ?? ''),
            (string)($mv['unit'] ?? 'u'),
            (string)($mv['lotNumber'] ?? ''),
            (string)($mv['expiryDate'] ?? ''),
        ]);
        if (!isset($merged[$key])) {
            $merged[$key] = $mv;
            $merged[$key]['quantity'] = 0;
            $merged[$key]['baseQuantity'] = 0;
            $merged[$key]['_mergedCount'] = 0;
            $merged[$key]['_notes'] = [];
        }
        $merged[$key]['quantity'] += abs((float)($mv['quantity'] ?? 0));
        $merged[$key]['baseQuantity'] += abs((float)($mv['baseQuantity'] ?? $mv['quantity'] ?? 0));
        $merged[$key]['_mergedCount']++;
        foreach (['reason','site','note'] as $field) {
            $val = trim((string)($mv[$field] ?? ''));
            if ($val !== '' && !in_array($val, $merged[$key]['_notes'], true)) $merged[$key]['_notes'][] = $val;
        }
    }
    $selectedMvs = array_values($merged);
}


$formatQty = static function($qty, $unit = 'piece') use ($e): string {
    $n = abs((float)$qty);
    $formatted = rtrim(rtrim(number_format($n, 3, ',', ' '), '0'), ',');
    $raw = trim((string)($unit ?: 'piece'));
    $lower = function_exists('mb_strtolower') ? mb_strtolower($raw, 'UTF-8') : strtolower($raw);
    if (in_array($lower, ['u','unité','unite','unit'], true)) $label = $n > 1 ? 'unités' : 'unité';
    elseif (in_array($lower, ['piece','pièce','pieces','pièces'], true)) $label = $n > 1 ? 'pièces' : 'pièce';
    else $label = ($n > 1 && !preg_match('/[sx]$/iu', $raw)) ? $raw . 's' : $raw;
    return $formatted . ' ' . $e($label);
};
$getWhName = static function($wid) use ($whs, $findBy): string {
    $w = $findBy($whs, $wid);
    return trim((string)($w['name'] ?? '')) ?: '—';
};
$warehouseNames = [];
foreach ($selectedMvs as $mv) {
    $nm = $getWhName($mv['warehouseId'] ?? 0);
    if ($nm !== '—' && !in_array($nm, $warehouseNames, true)) $warehouseNames[] = $nm;
}

$firstMv  = $selectedMvs[0];
$refDate  = date('d/m/Y \à H\hi', strtotime($firstMv['date'] ?? 'now'));
$refSite  = $destination;
$refRecipient = $recipient;
$operatorName = appUserDisplayName($user, '—');
$whName   = $warehouseNames ? implode(' · ', $warehouseNames) : '—';
$isGrouped = count($selectedMvs) > 1 || ($site && $date) || $idsParam !== '';
$hashBasis = $idsParam ?: (($site ?: 'BL') . $date . json_encode(array_column($selectedMvs, 'id')));
$blNum     = $isGrouped ? 'BL-' . strtoupper(substr(md5($hashBasis), 0, 10)) : 'BL-' . ($firstMv['id'] ?? '0');
$typeLabels = ['add'=>'Ajout','remove'=>'Sortie','transfer'=>'Transfert','entry'=>'Entrée','inventory'=>'Inventaire'];
$docSubject = $subject ?: ($isGrouped ? 'Bon de livraison groupé' : 'Bon de livraison');

$rows = '';
$totalQty = 0;
foreach ($selectedMvs as $idx => $mv) {
    $p = $findBy($prods, $mv['productId'] ?? 0) ?? [];
    $w = $findBy($whs, $mv['warehouseId'] ?? 0) ?? [];
    $dst = isset($mv['transferTo']) ? $findBy($whs, $mv['transferTo']) : null;
    $qty = abs((float)($mv['quantity'] ?? 0));
    $unit = $mv['unit'] ?? 'u';
    $baseQty = abs((float)($mv['baseQuantity'] ?? $qty));
    $totalQty += $baseQty;
    $lotInfo = '';
    if (!empty($mv['lotNumber'])) $lotInfo .= 'Lot : ' . $e($mv['lotNumber']);
    if (!empty($mv['expiryDate'])) $lotInfo .= ($lotInfo ? ' · ' : '') . 'DLUO : ' . $e($mv['expiryDate']);
    $destinationTxt = $dst ? '→ ' . $e($dst['name'] ?? '') : '';
    $imgUrl = $productImageUrl($p);
    $imgCell = '';
    if ($showImages) {
        $imgCell = $imgUrl ? '<img class="pimg" src="' . $e($imgUrl) . '" alt="">' : '<div class="pimg ph">📦</div>';
    }

    $designation = '<div class="prodline">' . $imgCell . '<div><strong>' . $e($p['name'] ?? '?') . '</strong>';
    if (!empty($p['sku'])) $designation .= '<br><span class="sm">SKU : ' . $e($p['sku']) . '</span>';
    if (!empty($p['barcode'])) $designation .= '<br><span class="sm">Code : ' . $e($p['barcode']) . '</span>';
    if (!empty($p['variantLabel'])) $designation .= '<br><span class="sm blue">' . $e($p['variantLabel']) . '</span>';
    $designation .= '</div></div>';

    $rows .= '<tr>';
    $rows .= '<td class="tc">' . ($idx + 1) . '</td>';
    $rows .= '<td>' . $designation . '</td>';
    $rows .= '<td>' . $e($w['name'] ?? '?') . ($destinationTxt ? '<br><span class="sm blue">' . $destinationTxt . '</span>' : '') . '</td>';
    $rows .= '<td class="tr bold big">' . $formatQty($qty, $unit) . '</td>';
    $rows .= '</tr>';
}

$companyLogoUrl = appCompanyLogoUrl($settings);
$appLogoUrl     = file_exists(APP_ROOT . '/assets/img/magestock-print-logo.jpg') ? assetUrl('img/magestock-print-logo.jpg') : assetUrl('img/sm_logo.png');
$primaryLogoUrl = $companyLogoUrl ?: $appLogoUrl;
$primaryLogoAlt = $companyLogoUrl ? $company : 'MageStock';
$docSubjectUpper = function_exists('mb_strtoupper') ? mb_strtoupper($docSubject, 'UTF-8') : strtoupper($docSubject);
$generatedAt = date('d/m/Y à H:i');

header('Content-Type: text/html; charset=UTF-8');
?><!DOCTYPE html>
<html lang="fr" data-orientation="portrait">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title><?= $e($blNum) ?> — <?= $e($docSubject) ?></title>
<style id="pageStyle">@page{size:A4 portrait;margin:8mm}</style>
<style>
*{box-sizing:border-box;margin:0;padding:0}
html,body{margin:0;padding:0;font-family:'Segoe UI','DM Sans',Arial,sans-serif;color:#0f172a;font-size:11.5px;line-height:1.42;background:#e8edf4;-webkit-print-color-adjust:exact;print-color-adjust:exact}
.pbar{position:sticky;top:0;z-index:10;background:#0f172a;color:#fff;padding:9px 16px;display:flex;align-items:center;justify-content:space-between;gap:14px;box-shadow:0 8px 24px rgba(15,23,42,.2)}
.pbar strong{display:block;font-size:13px}.pbar span{display:block;font-size:11px;color:#cbd5e1}.pbar-actions{display:flex;gap:8px;align-items:center}.pbtn{padding:8px 14px;border:1px solid #2563eb;border-radius:3px;font-size:13px;font-weight:900;cursor:pointer;display:inline-flex;align-items:center;gap:6px;background:#2563eb;color:#fff}.pbtn-s{background:#1e293b;border-color:#475569;color:#e2e8f0}
.page{width:194mm;max-width:calc(100vw - 24px);margin:14px auto 28px;background:#fff;min-height:281mm;padding:10mm 11mm;overflow:hidden;box-shadow:0 10px 35px rgba(15,23,42,.16)}
html[data-orientation="landscape"] .page{width:281mm;max-width:calc(100vw - 24px);min-height:194mm}
.sm{font-size:10.5px;color:#64748b}.blue{color:#1d4ed8}.muted{color:#64748b}.tc{text-align:center}.tr{text-align:right}.bold{font-weight:900}.big{font-size:13px}
.hdr{display:flex;justify-content:space-between;align-items:flex-start;padding-bottom:10px;border-bottom:3px solid #1d4ed8;margin-bottom:12px;gap:14px;break-inside:avoid}.brand{display:flex;align-items:center;gap:10px;min-width:0;max-width:calc(100% - 160px)}.brand-logos{display:flex;align-items:center;gap:8px;max-width:230px;min-width:0}.logo{max-width:138px;max-height:40px;height:auto;border-radius:2px;object-fit:contain;display:block}.app-logo{max-width:74px;max-height:24px;height:auto;opacity:.88;object-fit:contain}.logo-ph{width:44px;height:44px;border-radius:3px;background:#1d4ed8;display:flex;align-items:center;justify-content:center;color:#fff;font-size:18px;font-weight:1000}.co{font-size:15px;font-weight:1000;color:#0f172a;letter-spacing:-.02em}.co-sub{font-size:9px;color:#64748b;margin-top:1px;text-transform:uppercase;letter-spacing:.08em;font-weight:900}.ref{text-align:right;min-width:150px}.ref-num{font-size:12.5px;font-weight:1000;color:#1d4ed8;font-family:monospace;letter-spacing:.3px;word-break:break-all}.ref-date,.ref-editor{font-size:10.5px;color:#64748b;margin-top:3px}
.tbar{background:#1d4ed8;color:#fff;padding:8px 12px;border-radius:2px;margin-bottom:10px;display:flex;justify-content:space-between;align-items:center;break-inside:avoid}.tbar h1{font-size:14px;font-weight:900;margin:0;letter-spacing:.4px}.tbar .cnt{font-size:11px;opacity:.9}
.info{display:grid;grid-template-columns:1fr 1fr 1fr;gap:7px;margin-bottom:10px;break-inside:avoid}.info-b{background:#f8fafc;border:1px solid #dde3ea;border-left:4px solid #1d4ed8;border-radius:2px;padding:8px 10px}.info-l{font-size:8.5px;text-transform:uppercase;letter-spacing:.6px;color:#64748b;font-weight:1000;margin-bottom:2px}.info-v{font-size:12px;font-weight:800;color:#0f172a}
.tbl{width:100%;border-collapse:collapse;margin-bottom:11px}.tbl thead{display:table-header-group}.tbl thead th{background:#eaf1ff;padding:7px 8px;text-align:left;font-size:8.2px;font-weight:1000;text-transform:uppercase;letter-spacing:.05em;color:#1e3a8a;border-bottom:1px solid #bfdbfe}.tbl tbody td{padding:5px 7px;border-bottom:1px solid #e2e8f0;vertical-align:top;font-size:9.6px}.tbl tbody tr:nth-child(even) td{background:#f8fafc}.prodline{display:flex;align-items:flex-start;gap:9px}.pimg{width:34px;height:34px;object-fit:contain;border-radius:3px;border:1px solid #e2e8f0;background:#fff;flex:0 0 34px}.pimg.ph{display:flex;align-items:center;justify-content:center;background:#f8fafc;color:#64748b}
.sum{display:flex;justify-content:flex-end;gap:20px;padding:9px 0;border-top:2px solid #1d4ed8;margin-bottom:18px;break-inside:avoid}.sum-i{text-align:right}.sum-l{font-size:8.5px;color:#64748b;text-transform:uppercase;letter-spacing:.5px;font-weight:1000}.sum-v{font-size:16px;font-weight:1000;color:#1d4ed8}
.signs{display:grid;grid-template-columns:1fr 1fr;gap:36px;margin-top:24px;page-break-inside:avoid}.sign-l{font-size:8.8px;text-transform:uppercase;letter-spacing:.5px;color:#64748b;font-weight:1000;margin-bottom:5px}.sign-n{font-size:12px;font-weight:800;color:#0f172a;margin-bottom:25px}.sign-line{border-top:1px solid #94a3b8;padding-top:5px;font-size:9px;color:#94a3b8}.foot{margin-top:18px;padding-top:9px;border-top:1px solid #dde3ea;display:flex;justify-content:space-between;gap:12px;font-size:9px;color:#94a3b8}
@media print{html,body{background:#fff}.pbar{display:none!important}.page{box-shadow:none!important;margin:0 auto!important;width:100%!important;max-width:100%!important;min-height:calc(297mm - 16mm)!important;padding:10mm 11mm!important;overflow:hidden!important}.pimg{print-color-adjust:exact}html[data-orientation="landscape"] .page{width:100%!important;max-width:100%!important;min-height:calc(210mm - 16mm)!important}a{color:inherit;text-decoration:none}}
@media(max-width:640px){.pbar{align-items:flex-start;flex-direction:column}.pbar-actions{width:100%;display:grid;grid-template-columns:1fr 1fr 1fr}.info{grid-template-columns:1fr}.hdr{flex-direction:column;gap:10px}.ref{text-align:left}.prodline{gap:7px}.pimg{width:34px;height:34px;flex-basis:34px}.foot{flex-direction:column}.app-logo{display:none}}
</style>
<script>
function setPrintOrientation(o){
    document.documentElement.setAttribute('data-orientation', o === 'landscape' ? 'landscape' : 'portrait');
    document.getElementById('pageStyle').textContent = '@page{size:A4 '+(o === 'landscape' ? 'landscape' : 'portrait')+';margin:8mm}';
}
function printOrientation(o){ setPrintOrientation(o); setTimeout(function(){ window.focus(); window.print(); }, 40); }
</script>
</head>
<body>
<div class="pbar"><div><strong>Format A4</strong><span>Choisir l’orientation avant impression</span></div><div class="pbar-actions"><button class="pbtn" onclick="printOrientation('portrait')">Portrait</button><button class="pbtn" onclick="printOrientation('landscape')">Paysage</button><button class="pbtn pbtn-s" onclick="window.close()">Fermer</button></div></div>
<div class="page">
    <div class="hdr">
        <div class="brand"><div class="brand-logos"><?php if($primaryLogoUrl):?><img src="<?=$e($primaryLogoUrl)?>" class="logo" alt="<?=$e($primaryLogoAlt)?>"><?php else:?><div class="logo-ph">M</div><?php endif;?><?php if($companyLogoUrl):?><img src="<?=$e($appLogoUrl)?>" class="app-logo" alt="MageStock"><?php endif;?></div><div><div class="co"><?=$e($company)?></div><div class="co-sub">Gestion de stock · MageStock</div></div></div>
        <div class="ref"><div class="ref-num"><?=$e($blNum)?></div><div class="ref-date"><?=$e($generatedAt)?></div><div class="ref-editor">Édité par <?=$e($operatorName ?: '—')?></div></div>
    </div>
    <div class="tbar"><h1><?=$e($docSubjectUpper)?></h1><span class="cnt"><?=count($selectedMvs)?> ligne<?=count($selectedMvs)>1?'s':''?></span></div>
    <div class="info">
        <div class="info-b"><div class="info-l">Magasin(s) source</div><div class="info-v"><?=$e($whName)?></div></div>
        <div class="info-b"><div class="info-l">Destination</div><div class="info-v"><?=$e($refSite ?: '—')?></div></div>
        <div class="info-b"><div class="info-l">Réceptionnaire</div><div class="info-v"><?=$e($refRecipient ?: '—')?></div></div>
    </div>
    <table class="tbl"><thead><tr><th style="width:30px" class="tc">#</th><th>Désignation</th><th>Magasin source</th><th class="tr">Quantité</th></tr></thead><tbody><?=$rows?></tbody></table>
    <div class="sum"><div class="sum-i"><div class="sum-l">Lignes</div><div class="sum-v"><?=count($selectedMvs)?></div></div><div class="sum-i"><div class="sum-l">Total unités</div><div class="sum-v"><?=rtrim(rtrim(number_format($totalQty, 3, ',', ' '), '0'), ',')?></div></div></div>
    <div class="signs"><div><div class="sign-l">Émetteur</div><div class="sign-n"><?=$e($operatorName ?: '—')?></div><div class="sign-line">Signature</div></div><div><div class="sign-l">Réceptionnaire</div><div class="sign-n"><?=$e($refRecipient ?: '—')?></div><div class="sign-line">Nom, date et signature</div></div></div>
    <div class="foot"><span>Document généré par MageStock</span><span><?=$e($company)?> · <?=$e($generatedAt)?></span></div>
</div>
</body>
</html>
