<?php
/**
 * printnode_print.php
 * ─────────────────────────────────────────────────────────────────────────────
 * PrintNode thermal printing for Epson TM-T88V / TM-T20.
 *
 * SETUP:
 *   1. Sign up at https://app.printnode.com
 *   2. Install PrintNode agent on the PC next to the printer
 *   3. Get your API key from https://app.printnode.com/account/credentials
 *   4. Get your printer ID from the PrintNode dashboard
 *   5. Set PRINTNODE_API_KEY and printer IDs below
 *
 * USAGE (include this file in thermal_print.php or call directly):
 *   $result = printnode_send_receipt($receiptData);
 *   $result = printnode_send_kitchen($kitchenTicket);
 *   $result = printnode_send_raw($printerId, $escposBytes);
 */

// ─── CONFIGURE HERE ───────────────────────────────────────────────────────────
const PRINTNODE_API_KEY      = 'eGbNOVIA7Ig8mRO1vq-SfUZ40tuQIWPTBXMi_iIKP68';   // from printnode.com/account/credentials
const PRINTNODE_RECEIPT_ID   = 75588294;    // printer ID from PrintNode dashboard e.g. 12345678
const PRINTNODE_RECEIPT_ID    = 75588294;
const PRINTNODE_KITCHEN_ID    = 75588294;
const PRINTNODE_BAR_ID        = 75588294;
const PRINTNODE_PAPER_WIDTH   = 48;
const PRINTNODE_PROPERTY_NAME = 'THE PROPERTY';
// ─────────────────────────────────────────────────────────────────────────────

// ── ESC/POS helpers ───────────────────────────────────────────────────────────
function pn_init(): string {
    return
        "\x1B\x40"      // ESC @ — full printer reset
      . "\x1B\x74\x00"  // ESC t 0 — code page PC437 (standard ASCII, no accents)
      . "\x1B\x52\x00"  // ESC R 0 — international character set: USA
      . "\x1B\x6D\x00"  // disable auto-sleep
      . "\x1C\x2E";     // cancel kanji mode (prevents Asian char mode on some units)
}
function pn_cut():                    string { return "\x1D\x56\x42\x00"; }
function pn_feed(int $n = 3):         string { return str_repeat("\x0A", $n); }
function pn_bold(bool $on):           string { return $on ? "\x1B\x45\x01" : "\x1B\x45\x00"; }
function pn_align(string $a):         string {
    return "\x1B\x61" . (['left'=>"\x00",'center'=>"\x01",'right'=>"\x02"][$a] ?? "\x00");
}
function pn_double_height(bool $on):  string { return $on ? "\x1B\x21\x10" : "\x1B\x21\x00"; }
function pn_invert(bool $on):         string { return $on ? "\x1D\x42\x01" : "\x1D\x42\x00"; }

function pn_pad(string $left, string $right, int $w = PRINTNODE_PAPER_WIDTH): string {
    $left  = mb_substr($left, 0, $w - mb_strlen($right) - 1);
    $space = max(1, $w - mb_strlen($left) - mb_strlen($right));
    return $left . str_repeat(' ', $space) . $right . "\n";
}
function pn_divider(string $c = '-', int $w = PRINTNODE_PAPER_WIDTH): string {
    return str_repeat($c, $w) . "\n";
}
function pn_money(float $v): string { return number_format($v, 2); }

// Strip any non-ASCII chars so they don't confuse the printer
function pn_safe(string $text): string {
    // Replace common accented chars with ASCII equivalents
    $from = ['À','Á','Â','Ã','Ä','Å','à','á','â','ã','ä','å',
             'È','É','Ê','Ë','è','é','ê','ë',
             'Ì','Í','Î','Ï','ì','í','î','ï',
             'Ò','Ó','Ô','Õ','Ö','Ø','ò','ó','ô','õ','ö','ø',
             'Ù','Ú','Û','Ü','ù','ú','û','ü',
             'Ý','ý','ÿ','Ñ','ñ','Ç','ç','ß',
             "\u{2018}","\u{2019}","\u{201C}","\u{201D}","\u{2013}","\u{2014}","\u{00A0}"];
    $to   = ["A","A","A","A","A","A","a","a","a","a","a","a",
             "E","E","E","E","e","e","e","e",
             "I","I","I","I","i","i","i","i",
             "O","O","O","O","O","O","o","o","o","o","o","o",
             "U","U","U","U","u","u","u","u",
             "Y","y","y","N","n","C","c","ss",
             "'","'",'"','"','-','-',' '];
    $text = str_replace($from, $to, $text);
    // Drop anything still non-ASCII
    return preg_replace('/[^\x00-\x7F]/', '?', $text);
}

// ── Build receipt bytes ───────────────────────────────────────────────────────
function pn_build_receipt(array $t): string {
    $w   = PRINTNODE_PAPER_WIDTH;
    $out = pn_init();

    // Header
    $out .= pn_align('center') . pn_double_height(true) . pn_bold(true);
    $out .= pn_safe(strtoupper(PRINTNODE_PROPERTY_NAME)) . "\n";
    $out .= pn_double_height(false) . pn_bold(false);
    $out .= pn_divider('=', $w);
    $out .= pn_align('left');

    // Meta
    if (!empty($t['order_no'])) $out .= pn_bold(true) . 'Order:   ' . pn_bold(false) . pn_safe($t['order_no']) . "\n";
    if (!empty($t['table']))    $out .= 'Table:   ' . pn_safe($t['table'])   . "\n";
    if (!empty($t['guest']))    $out .= 'Guest:   ' . pn_safe($t['guest'])   . "\n";
    if (!empty($t['cashier']))  $out .= 'Cashier: ' . pn_safe($t['cashier']) . "\n";
    $out .= 'Date:    ' . pn_safe(!empty($t['date']) ? $t['date'] : date('Y-m-d H:i')) . "\n";
    $out .= pn_divider('-', $w);

    // Items
    $subtotal = 0.0;
    foreach ((array)($t['items'] ?? []) as $item) {
        if (($item['status'] ?? 'active') !== 'active') continue;
        $qty    = (float)($item['qty']    ?? 1);
        $amount = (float)($item['amount'] ?? 0);
        $subtotal += $amount;
        $label  = ($qty != 1 ? ($qty == intval($qty) ? (int)$qty : $qty) . ' x ' : '') . pn_safe($item['name'] ?? 'Item');
        $out   .= pn_pad(mb_substr($label, 0, $w - 10), pn_money($amount), $w);
        if (!empty($item['notes'])) $out .= '  * ' . pn_safe(mb_substr($item['notes'], 0, $w - 4)) . "\n";
    }

    // Totals
    $out .= pn_divider('-', $w);
    if (!empty($t['tax']) && (float)$t['tax'] > 0) {
        $out .= pn_pad('Subtotal', pn_money((float)($t['subtotal'] ?? $subtotal)), $w);
        $out .= pn_pad('Tax',      pn_money((float)$t['tax']), $w);
    }
    $out .= pn_bold(true) . pn_pad('TOTAL', pn_money((float)($t['total'] ?? $subtotal)), $w) . pn_bold(false);

    // Payments
    if (!empty($t['payments'])) {
        $out .= pn_divider('-', $w);
        foreach ((array)$t['payments'] as $pay) {
            $out .= pn_pad(strtoupper(pn_safe($pay['method'] ?? '')), pn_money((float)($pay['amount'] ?? 0)), $w);
            if (!empty($pay['reference'])) $out .= '  Ref: ' . pn_safe(mb_substr($pay['reference'], 0, $w - 6)) . "\n";
        }
    }
    if (!empty($t['surcharge']) && (float)$t['surcharge'] > 0)
        $out .= '  * Incl. 3% card surcharge ' . pn_money((float)$t['surcharge']) . "\n";

    $out .= pn_divider('=', $w);
    $out .= pn_align('center') . "Thank you for your visit!\n" . pn_align('left');
    $out .= pn_feed(4) . pn_cut();
    return $out;
}

// ── Build kitchen bytes ───────────────────────────────────────────────────────
function pn_build_kitchen(array $t): string {
    $w    = PRINTNODE_PAPER_WIDTH;
    $role = strtoupper(pn_safe($t['station'] ?? 'KITCHEN'));
    $out  = pn_init();
    $out .= pn_align('center') . pn_invert(true) . pn_bold(true);
    $out .= " $role TICKET \n";
    $out .= pn_bold(false) . pn_invert(false);
    $out .= pn_divider('=', $w) . pn_align('left');
    if (!empty($t['order_no'])) $out .= pn_bold(true) . 'ORDER: ' . pn_safe($t['order_no']) . pn_bold(false) . "\n";
    if (!empty($t['table']))    $out .= 'Table:  ' . pn_safe($t['table'])  . "\n";
    if (!empty($t['covers']))   $out .= 'Covers: ' . pn_safe((string)$t['covers']) . "\n";
    $out .= 'Time:   ' . pn_safe(!empty($t['date']) ? $t['date'] : date('H:i')) . "\n";
    $out .= pn_divider('=', $w);
    foreach ((array)($t['items'] ?? []) as $item) {
        $qty  = (float)($item['qty'] ?? 1);
        $name = strtoupper(pn_safe($item['name'] ?? 'Item'));
        $out .= pn_bold(true) . ' ' . ($qty == intval($qty) ? (int)$qty : $qty) . ' x ' . $name . "\n" . pn_bold(false);
        if (!empty($item['seat']))  $out .= '   Seat:  ' . pn_safe((string)$item['seat'])  . "\n";
        if (!empty($item['notes'])) $out .= '   ** '    . pn_safe(mb_substr($item['notes'], 0, $w - 6)) . "\n";
    }
    $out .= pn_divider('=', $w) . pn_feed(4) . pn_cut();
    return $out;
}

// ── Send raw ESC/POS bytes to PrintNode ──────────────────────────────────────
function printnode_send_raw(int $printerId, string $escposBytes, string $title = 'Print Job'): array {
    if (!defined('PRINTNODE_API_KEY') || PRINTNODE_API_KEY === 'YOUR_API_KEY_HERE') {
        return ['ok' => false, 'error' => 'PrintNode API key not configured'];
    }
    if ($printerId <= 0) {
        return ['ok' => false, 'error' => 'PrintNode printer ID not configured'];
    }
    if (!function_exists('curl_init')) {
        return ['ok' => false, 'error' => 'cURL not available on this server'];
    }

    $payload = json_encode([
        'printerId'   => $printerId,
        'title'       => $title,
        'contentType' => 'raw_base64',
        'content'     => base64_encode($escposBytes),
        'source'      => 'PMS Front Office POS',
    ]);

    $ch = curl_init('https://api.printnode.com/printjobs');
    curl_setopt_array($ch, [
        CURLOPT_POST           => true,
        CURLOPT_POSTFIELDS     => $payload,
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 10,
        CURLOPT_HTTPHEADER     => [
            'Content-Type: application/json',
            'Authorization: Basic ' . base64_encode(PRINTNODE_API_KEY . ':'),
        ],
    ]);

    $response = curl_exec($ch);
    $curlErr  = curl_error($ch);
    $httpCode = (int)curl_getinfo($ch, CURLINFO_HTTP_CODE);
    curl_close($ch);

    if ($curlErr) {
        return ['ok' => false, 'error' => 'cURL: ' . $curlErr];
    }
    if ($httpCode === 201) {
        return ['ok' => true, 'job_id' => json_decode($response, true), 'method' => 'printnode'];
    }

    $body = json_decode($response, true);
    $msg  = is_array($body) ? ($body['message'] ?? $response) : $response;
    return ['ok' => false, 'error' => "PrintNode HTTP $httpCode: $msg"];
}

function printnode_send_receipt(array $t, ?int $printerId = null): array {
    return printnode_send_raw(
        $printerId ?? PRINTNODE_RECEIPT_ID,
        pn_build_receipt($t),
        'Receipt ' . ($t['order_no'] ?? '')
    );
}

function printnode_send_kitchen(array $t, ?int $printerId = null): array {
    $station = strtolower($t['station'] ?? 'kitchen');
    return printnode_send_raw(
        $printerId ?? ($station === 'bar' ? PRINTNODE_BAR_ID : PRINTNODE_KITCHEN_ID),
        pn_build_kitchen($t),
        strtoupper($station) . ' Ticket ' . ($t['order_no'] ?? '')
    );
}

// ── List printers ─────────────────────────────────────────────────────────────
function printnode_list_printers(): array {
    if (!function_exists('curl_init')) return [];
    $ch = curl_init('https://api.printnode.com/printers');
    curl_setopt_array($ch, [
        CURLOPT_RETURNTRANSFER => true,
        CURLOPT_TIMEOUT        => 10,
        CURLOPT_HTTPHEADER     => ['Authorization: Basic ' . base64_encode(PRINTNODE_API_KEY . ':')],
    ]);
    $response = curl_exec($ch);
    curl_close($ch);
    return json_decode($response, true) ?: [];
}

// ── Test page ─────────────────────────────────────────────────────────────────
if (basename($_SERVER['PHP_SELF'] ?? '') === 'printnode_print.php' && isset($_GET['test'])) {
    header('Content-Type: text/html; charset=utf-8');
    echo '<!doctype html><html><head><meta charset="utf-8"><title>PrintNode Test</title>
    <style>body{font-family:Arial,sans-serif;margin:30px}table{border-collapse:collapse}td,th{border:1px solid #ccc;padding:8px}th{background:#f3f4f6}</style></head><body>';
    echo '<h2>PrintNode Printer List</h2>';

    $printers = printnode_list_printers();

    if (empty($printers)) {
        echo '<p style="color:red">&#10007; No printers found. Check:<ul>
            <li>API key is correct in PRINTNODE_API_KEY</li>
            <li>PrintNode agent is installed and running on the local PC</li>
            <li>Agent is logged in with the same PrintNode account</li>
        </ul></p>';
    } else {
        echo '<table><tr><th>ID</th><th>Name</th><th>State</th><th>Computer</th></tr>';
        foreach ($printers as $p) {
            $state = (string)($p['state'] ?? 'unknown');
            $color = $state === 'online' ? 'green' : 'red';
            echo '<tr>'
                . '<td><strong>' . (int)($p['id'] ?? 0) . '</strong></td>'
                . '<td>' . htmlspecialchars((string)($p['name'] ?? '')) . '</td>'
                . '<td style="color:' . $color . '">' . htmlspecialchars($state) . '</td>'
                . '<td>' . htmlspecialchars((string)($p['computer']['name'] ?? '')) . '</td>'
                . '</tr>';
        }
        echo '</table>';
        echo '<p>&#8593; Copy the <strong>ID</strong> of your Epson printer into <code>PRINTNODE_RECEIPT_ID</code> at the top of this file.</p>';
    }

    echo '<hr><h2>Test Print</h2>';
    if (PRINTNODE_RECEIPT_ID <= 0) {
        echo '<p style="color:orange">Set PRINTNODE_RECEIPT_ID to a printer ID from the table above, then reload this page.</p>';
    } else {
        if (isset($_GET['print_test'])) {
            $result = printnode_send_receipt([
                'order_no' => 'TEST-001',
                'guest'    => 'Test Guest',
                'cashier'  => 'Front Office',
                'date'     => date('Y-m-d H:i'),
                'total'    => 25.00,
                'tax'      => 3.35,
                'subtotal' => 25.00,
                'items'    => [
                    ['name' => 'Spa Massage 60min', 'qty' => 1, 'amount' => 15.00, 'status' => 'active'],
                    ['name' => 'Curio Keyring',     'qty' => 2, 'amount' => 10.00, 'status' => 'active'],
                ],
                'payments' => [
                    ['method' => 'Cash', 'amount' => 25.00],
                ],
            ]);

            if ($result['ok']) {
                echo '<p style="color:green">&#10003; Print job sent to printer ID ' . PRINTNODE_RECEIPT_ID . '. Job ID: ' . htmlspecialchars((string)($result['job_id'] ?? '')) . '</p>';
                echo '<p>Check if the printer printed. If it still prints gibberish, the printer driver on the PC needs to be set to <strong>Generic / Text Only</strong> or <strong>ESC/POS</strong>.</p>';
            } else {
                echo '<p style="color:red">&#10007; Failed: ' . htmlspecialchars((string)($result['error'] ?? 'Unknown error')) . '</p>';
            }
        } else {
            echo '<p>Printer ID configured: <strong>' . PRINTNODE_RECEIPT_ID . '</strong></p>';
            echo '<p><a href="?test=1&print_test=1" style="background:#2563eb;color:#fff;padding:10px 20px;text-decoration:none;border-radius:6px">&#128424; Send Test Print</a></p>';
        }
    }

    echo '</body></html>';
    exit;
}
