<?php
/* Lieferschein-Endpoints fuer die PWA.
 *
 * GET  /api/shipments.php?order_id=<id>         - Liste der Lieferungen zu einem Auftrag
 * GET  /api/shipments.php?id=<id>               - Detail einer Lieferung (mit Bericht-Info wenn vorhanden)
 * GET  /api/shipments.php?id=<id>&action=pdf    - Lieferschein-PDF (Original oder unterschrieben)
 * POST /api/shipments.php?id=<id>&action=confirm
 *      multipart: file=<PNG Signatur>, signer_name, gps_lat?, gps_lon?
 *      → stempelt PNG in das Lieferschein-PDF (Position aus llx_bericht_signature_box bzw. Default),
 *        speichert Bericht (element_type='shipment'), setzt signed_status=1,
 *        validiert+schliesst die Expedition wenn sie noch im Draft war.
 */
require_once __DIR__.'/_inc.php';

// Robust gegen unerwartete Fehler — sonst landet PHP-Fatal als leerer 500 ohne Hinweis
set_error_handler(function ($errno, $errstr, $errfile, $errline) {
    if (!(error_reporting() & $errno)) return false;
    api_fail('PHP-Error: '.$errstr.' in '.basename($errfile).':'.$errline, 500);
    return true;
});
set_exception_handler(function ($e) {
    api_fail('Exception: '.$e->getMessage().' in '.basename($e->getFile()).':'.$e->getLine(), 500);
});

api_authenticate();
global $db, $user, $conf, $langs;

require_once DOL_DOCUMENT_ROOT.'/expedition/class/expedition.class.php';
require_once DOL_DOCUMENT_ROOT.'/commande/class/commande.class.php';
require_once DOL_DOCUMENT_ROOT.'/core/lib/files.lib.php';

$id        = (int) ($_GET['id']       ?? 0);
$order_id  = (int) ($_GET['order_id'] ?? 0);
$action    = $_GET['action'] ?? '';
$method    = $_SERVER['REQUEST_METHOD'];

/* ----- LISTE der Lieferungen zu einem Auftrag ----- */
if ($order_id > 0 && !$id) {
    // llx_element_element: commande = source, shipping = target
    $sql = "SELECT e.rowid, e.ref, e.date_creation, e.date_delivery, e.date_expedition,"
        ." e.fk_statut, e.signed_status, e.tracking_number, e.fk_soc,"
        ." (SELECT COUNT(*) FROM ".$db->prefix()."bericht b WHERE b.element_type='shipment' AND b.fk_element=e.rowid) AS bericht_count"
        ." FROM ".$db->prefix()."expedition e"
        ." JOIN ".$db->prefix()."element_element ee ON ee.fk_target = e.rowid AND ee.targettype = 'shipping'"
        ." WHERE ee.fk_source = ".((int) $order_id)." AND ee.sourcetype = 'commande'"
        ." AND e.entity IN (".getEntity('expedition').")"
        ." ORDER BY e.date_creation DESC, e.rowid DESC";
    $r = $db->query($sql);
    if (!$r) api_fail('DB-Fehler: '.$db->lasterror(), 500);

    $out = array();
    while ($o = $db->fetch_object($r)) {
        $out[] = array(
            'id'              => (int) $o->rowid,
            'ref'             => $o->ref,
            'date_creation'   => $db->jdate($o->date_creation),
            'date_delivery'   => $db->jdate($o->date_delivery),
            'date_expedition' => $db->jdate($o->date_expedition),
            'status'          => (int) $o->fk_statut,
            'signed_status'   => $o->signed_status !== null ? (int) $o->signed_status : null,
            'tracking_number' => $o->tracking_number,
            'soc_id'          => (int) $o->fk_soc,
            'bericht_count'   => (int) $o->bericht_count,
        );
    }
    api_ok(array('shipments' => $out, 'count' => count($out)));
}

if ($id <= 0) api_fail('id oder order_id fehlt');

// Lieferung + verknuepfter Auftrag laden
$ctx = bericht_fetch_shipment_with_order($db, $id);
if (!$ctx) api_fail('Lieferung nicht gefunden', 404);
$shipment = $ctx['expedition'];
$commande = $ctx['commande'];
$kunde    = $ctx['thirdparty'];

/* ----- PDF-Stream -----
 * Variante: ?variant=signed | unsigned | auto (default = auto: signed wenn signiert, sonst Original)
 */
if ($action === 'pdf' && $method === 'GET') {
    $variant = $_GET['variant'] ?? 'auto';
    $shipment_signed = (int) ($shipment->signed_status ?? 0) === 1;

    $pdf_path = null;
    if ($variant === 'signed' || ($variant === 'auto' && $shipment_signed)) {
        // Suche explizit nach <ref>-signed.pdf
        $ref_sane = dol_sanitizeFileName($shipment->ref);
        $shipment_dir = $conf->expedition->multidir_output[$shipment->entity ?: $conf->entity].'/sending/'.$ref_sane;
        if (!is_dir($shipment_dir)) {
            $shipment_dir = $conf->expedition->multidir_output[$shipment->entity ?: $conf->entity].'/'.$ref_sane;
        }
        $candidate = $shipment_dir.'/'.$ref_sane.'-signed.pdf';
        if (file_exists($candidate)) $pdf_path = $candidate;
    }
    if (!$pdf_path) {
        // Fallback / unsigned-Variante
        $pdf_path = bericht_get_shipment_pdf($db, $shipment, false);
    }
    if (!$pdf_path || !file_exists($pdf_path)) api_fail('Lieferschein-PDF nicht verfuegbar', 404);

    // Header zuruecksetzen — _inc.php hat application/json gesetzt
    header_remove('Content-Type');
    header('Content-Type: application/pdf');
    header('Content-Disposition: inline; filename="'.basename($pdf_path).'"');
    header('Content-Length: '.filesize($pdf_path));
    readfile($pdf_path);
    exit;
}

/* ----- DETAIL einer Lieferung ----- */
if ($method === 'GET' && !$action) {
    $bericht_id = 0;
    $bres = $db->query("SELECT rowid FROM ".$db->prefix()."bericht"
        ." WHERE element_type = 'shipment' AND fk_element = ".((int) $id)
        ." ORDER BY datec DESC LIMIT 1");
    if ($bres && ($br = $db->fetch_object($bres))) $bericht_id = (int) $br->rowid;

    api_ok(array(
        'shipment' => array(
            'id'              => (int) $shipment->id,
            'ref'             => $shipment->ref,
            'date_creation'   => $shipment->date_creation,
            'date_delivery'   => $shipment->date_delivery ?? null,
            'date_expedition' => $shipment->date_expedition ?? null,
            'status'          => (int) ($shipment->statut ?? $shipment->fk_statut ?? 0),
            'signed_status'   => isset($shipment->signed_status) ? (int) $shipment->signed_status : null,
            'tracking_number' => $shipment->tracking_number ?? null,
        ),
        'order' => $commande ? array(
            'id'  => (int) $commande->id,
            'ref' => $commande->ref,
        ) : null,
        'customer' => array(
            'id'      => (int) ($kunde->id ?? 0),
            'name'    => $kunde->name ?? '',
            'address' => $kunde->address ?? '',
            'zip'     => $kunde->zip ?? '',
            'town'    => $kunde->town ?? '',
        ),
        'bericht_id' => $bericht_id,
    ));
}

/* ----- POST confirm: Unterschrift einstempeln ----- */
if ($action === 'confirm' && $method === 'POST') {
    if (!$user->hasRight('bericht', 'write')) api_fail('Schreibrechte fehlen', 403);
    if (empty($_FILES['file']['tmp_name'])) api_fail('file (PNG) fehlt');

    $signer_name = trim((string) ($_POST['signer_name'] ?? ''));
    if ($signer_name === '') api_fail('signer_name fehlt');

    $gps_lat = isset($_POST['gps_lat']) && $_POST['gps_lat'] !== '' ? (float) $_POST['gps_lat'] : null;
    $gps_lon = isset($_POST['gps_lon']) && $_POST['gps_lon'] !== '' ? (float) $_POST['gps_lon'] : null;

    // 1) Signatur-PNG zwischenspeichern (vor ODT-Render noetig)
    require_once __DIR__.'/../class/bericht.class.php';
    $workdir = DOL_DATA_ROOT.'/bericht/work/shipment_'.((int) $shipment->id);
    if (!is_dir($workdir)) dol_mkdir($workdir);
    $sig_png = $workdir.'/signature_'.dol_print_date(dol_now(), '%Y%m%d_%H%M%S').'.png';
    if (!move_uploaded_file($_FILES['file']['tmp_name'], $sig_png)) {
        api_fail('Signatur-Upload fehlgeschlagen', 500);
    }

    $signed_at     = date('Y-m-d H:i:s');
    $active_module = getDolGlobalString('EXPEDITION_ADDON_PDF', 'merou');

    // Ziel-Pfad fuer signed-PDF (Expedition-ECM-Verzeichnis)
    $shipment_dir = $conf->expedition->multidir_output[$shipment->entity ?: $conf->entity].'/sending/'.dol_sanitizeFileName($shipment->ref);
    if (!is_dir($shipment_dir)) {
        $shipment_dir = $conf->expedition->multidir_output[$shipment->entity ?: $conf->entity].'/'.dol_sanitizeFileName($shipment->ref);
    }
    if (!is_dir($shipment_dir)) dol_mkdir($shipment_dir);
    $out_pdf = $shipment_dir.'/'.dol_sanitizeFileName($shipment->ref).'-signed.pdf';

    $meta_render = array(
        'signer_name' => $signer_name,
        'signed_at'   => $signed_at,
        'gps_lat'     => $gps_lat,
        'gps_lon'     => $gps_lon,
        'shipment'    => $shipment,
        'order'       => $commande,
        'kunde'       => $kunde,
    );

    $template_name = $active_module;  // fuer Audit-Meta
    $ok = false;

    if ($active_module === 'generic_shipment_odt') {
        // ODT-Pfad: Dolibarrs Standard-Pipeline nutzen (generateDocument), damit ALLE Substitutionen
        // (Kunde, Adresse, Lieferpositionen, Extrafelder, line-Iteration) sauber durchlaufen.
        // Unser actions_bericht::beforeODTSave-Hook stempelt die Unterschrift via setImage('signature').
        //
        // Wichtig: generateDocument überschreibt IMMER das Standard-PDF am gleichen Pfad.
        // Damit das normale Lieferschein-PDF (ohne Unterschrift) erhalten bleibt, sichern wir
        // es vorher und stellen es nach der signed-Kopie wieder her.
        $template_name = 'generic_shipment_odt';

        // 1) Original-PDF sichern (falls vorhanden)
        $original_pdf = bericht_get_shipment_pdf($db, $shipment, false);
        $backup_pdf = null;
        if ($original_pdf && file_exists($original_pdf) && !preg_match('/-signed\.pdf$/i', $original_pdf)) {
            $backup_pdf = $original_pdf.'.bericht-backup';
            @copy($original_pdf, $backup_pdf);
        }

        // 2) generateDocument mit Hook (Signatur eingestempelt)
        $shipment->bericht_signature_png  = $sig_png;
        $shipment->bericht_signature_meta = array(
            'signer_name' => $signer_name,
            'signed_at'   => $signed_at,
            'gps_lat'     => $gps_lat,
            'gps_lon'     => $gps_lon,
        );
        try {
            $rd = $shipment->generateDocument('generic_shipment_odt', $langs);
            if ($rd <= 0) api_fail('generateDocument fehlgeschlagen: '.($shipment->error ?: 'unbekannt'), 500);
        } catch (Throwable $e) {
            api_fail('Exception bei generateDocument: '.$e->getMessage(), 500);
        }

        // 3) Das frisch generierte (signed) PDF zu <ref>-signed.pdf kopieren
        //    bericht_get_shipment_pdf mit include_signed=true findet die Standard-Datei
        //    (die jetzt aber die signierte Version ist)
        $signed_in_place = bericht_get_shipment_pdf($db, $shipment, true);
        if (!$signed_in_place || !file_exists($signed_in_place)) {
            api_fail('Kein signiertes PDF nach generateDocument gefunden', 500);
        }
        if (!@copy($signed_in_place, $out_pdf)) {
            api_fail('Kopieren des signierten PDF fehlgeschlagen: '.$out_pdf, 500);
        }

        // 4) Original wieder herstellen — Hook-Property entfernen, dann nochmal generateDocument
        //    Falls Backup vorhanden, kann der schnellere Weg verwendet werden.
        unset($shipment->bericht_signature_png);
        unset($shipment->bericht_signature_meta);
        if ($backup_pdf && file_exists($backup_pdf)) {
            @copy($backup_pdf, $signed_in_place);
            @unlink($backup_pdf);
        } else {
            // Es gab kein Original-Backup → neues unsigned-PDF regenerieren
            try { $shipment->generateDocument('generic_shipment_odt', $langs); }
            catch (Throwable $e) { dol_syslog('Restaurieren unsigned PDF fehlgeschlagen: '.$e->getMessage(), LOG_WARNING); }
        }

        $ok = true;
    } else {
        // PDF-Modul: Original-Lieferschein-PDF holen und Signatur per FPDI an konfigurierter Box-Position stempeln.
        $src_pdf = bericht_get_shipment_pdf($db, $shipment);
        if (!$src_pdf || !file_exists($src_pdf)) api_fail('Lieferschein-PDF konnte nicht erzeugt werden', 500);

        $box = bericht_get_signature_box($db, $template_name);
        $ok = bericht_stamp_signature_on_pdf($src_pdf, $sig_png, $box, array(
            'signer_name'  => $signer_name,
            'signed_at'    => $signed_at,
            'gps_lat'      => $gps_lat,
            'gps_lon'      => $gps_lon,
            'shipment_ref' => $shipment->ref,
        ), $out_pdf);
        if (!$ok) api_fail('PDF-Stempel fehlgeschlagen', 500);
    }

    // 5) Bericht-Record (element_type='shipment') anlegen, damit wir die Signatur-Metadaten
    //    in der gleichen Audit-Logik wie Berichts-Signaturen ablegen koennen
    $bericht = null;
    $existing = $db->query("SELECT rowid FROM ".$db->prefix()."bericht"
        ." WHERE element_type = 'shipment' AND fk_element = ".((int) $shipment->id)
        ." ORDER BY datec DESC LIMIT 1");
    if ($existing && ($row = $db->fetch_object($existing))) {
        $bericht = new Bericht($db);
        $bericht->fetch((int) $row->rowid);
    } else {
        $bericht = new Bericht($db);
        $bericht->element_type   = 'shipment';
        $bericht->fk_element     = (int) $shipment->id;
        $bericht->titel          = 'Lieferschein '.$shipment->ref;
        $bericht->auftragsnummer = $commande ? $commande->ref : '';
        $bericht->status         = Bericht::STATUS_FINAL;
        $bericht->final_pdf_path = str_replace(DOL_DATA_ROOT.'/', '', $out_pdf);
        $bericht->create($user);
    }
    if ($bericht && $bericht->id) {
        $bericht->status = Bericht::STATUS_FINAL;
        $bericht->final_pdf_path = str_replace(DOL_DATA_ROOT.'/', '', $out_pdf);
        $bericht->update($user);
    }

    // Metadaten neben dem PNG ablegen (Audit-Spur)
    @file_put_contents($sig_png.'.meta.json', json_encode(array(
        'shipment_id'  => (int) $shipment->id,
        'shipment_ref' => $shipment->ref,
        'order_ref'    => $commande ? $commande->ref : null,
        'customer'     => $kunde->name ?? '',
        'signer_name'  => $signer_name,
        'signed_at'    => $signed_at,
        'signed_at_unix' => time(),
        'user_id'      => $user->id,
        'user_login'   => $user->login,
        'gps_lat'      => $gps_lat,
        'gps_lon'      => $gps_lon,
        'template'     => $template_name,
        'render_mode'  => ($active_module === 'generic_shipment_odt' ? 'odt' : 'pdf_stamp'),
        'remote_ip'    => $_SERVER['REMOTE_ADDR'] ?? '',
        'signed_pdf'   => str_replace(DOL_DATA_ROOT.'/', '', $out_pdf),
    ), JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE));

    // 6) Expedition-Status: signed_status=1 + ggf. validieren+schliessen
    $status_changes = array();
    if (method_exists($shipment, 'setSignedStatus')) {
        $shipment->setSignedStatus($user, 1, 0, 'BERICHT_SIGNED');
        $status_changes[] = 'signed_status=1';
    } else {
        // Fallback: direktes SQL falls Trait fehlt
        $db->query("UPDATE ".$db->prefix()."expedition SET signed_status = 1 WHERE rowid = ".((int) $shipment->id));
        $status_changes[] = 'signed_status=1 (sql)';
    }

    // Status-Workflow: Draft → Validated → Closed
    $current_status = (int) ($shipment->statut ?? $shipment->fk_statut ?? 0);
    if ($current_status === 0 || $current_status === Expedition::STATUS_DRAFT) {
        if (method_exists($shipment, 'valid')) {
            $rv = $shipment->valid($user);
            if ($rv > 0) $status_changes[] = 'validated';
        }
        // erneut fetchen, damit setClosed konsistente Daten sieht
        $shipment->fetch((int) $shipment->id);
    }
    if (defined('Expedition::STATUS_CLOSED') && method_exists($shipment, 'setClosed')) {
        $rc = $shipment->setClosed();
        if ($rc > 0) $status_changes[] = 'closed';
    } elseif (method_exists($shipment, 'setClosed')) {
        $shipment->setClosed();
        $status_changes[] = 'closed';
    }

    api_ok(array(
        'bericht_id'    => (int) ($bericht->id ?? 0),
        'signed_pdf'    => str_replace(DOL_DATA_ROOT.'/', '', $out_pdf),
        'signed_at'     => $signed_at,
        'status_changes' => $status_changes,
    ));
}

api_fail('Unbekannte Aktion', 400);
