<?php
declare(strict_types=1);

mb_internal_encoding('UTF-8');
date_default_timezone_set('Asia/Jakarta');

const STORAGE_DIR = __DIR__ . '/data-struk';
const MAX_PAYLOAD_LENGTH = 60000;

header('X-Content-Type-Options: nosniff');
header('Referrer-Policy: no-referrer');
header('Cache-Control: no-store, no-cache, must-revalidate, max-age=0');

function h($value): string
{
    return htmlspecialchars((string) $value, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
}

function base64url_decode_string(string $payload)
{
    $payload = strtr($payload, '-_', '+/');
    $padding = strlen($payload) % 4;
    if ($padding > 0) {
        $payload .= str_repeat('=', 4 - $padding);
    }
    return base64_decode($payload, true);
}

function decode_receipt_payload(?string $payload): ?array
{
    if ($payload === null) {
        return null;
    }

    $payload = trim($payload);
    if ($payload === '' || strlen($payload) > MAX_PAYLOAD_LENGTH) {
        return null;
    }

    $json = base64url_decode_string($payload);
    if ($json === false) {
        $json = base64_decode($payload, true);
    }
    if ($json === false) {
        $json = $payload;
    }

    $data = json_decode($json, true);
    return is_array($data) ? $data : null;
}

function clean_text($value, string $default = '-'): string
{
    if (is_array($value) || is_object($value)) {
        $value = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
    }
    $value = trim((string) ($value ?? ''));
    $value = preg_replace('/[\x00-\x08\x0B\x0C\x0E-\x1F\x7F]/u', '', $value) ?? '';
    if ($value === '') {
        return $default;
    }
    return mb_substr($value, 0, 700);
}

function first_value(array $data, array $keys, $default = '-')
{
    foreach ($keys as $key) {
        if (isset($data[$key]) && trim((string) $data[$key]) !== '') {
            return $data[$key];
        }
    }
    return $default;
}

function money_number($value): int
{
    if (is_int($value) || is_float($value)) {
        return max(0, (int) $value);
    }
    $digits = preg_replace('/[^0-9]/', '', (string) ($value ?? ''));
    return $digits === '' ? 0 : max(0, (int) $digits);
}

function normalize_receipt(array $raw): array
{
    $status = clean_text(first_value($raw, ['status_pengiriman', 'status', 'status_transaksi', 'transaction_status'], '-'));

    return [
        'merchant' => clean_text(first_value($raw, ['merchant', 'nama_toko', 'store', 'brand'], 'STRUK TRANSAKSI')),
        'nomor_pembayaran' => clean_text(first_value($raw, ['nomor_pembayaran', 'id_transaksi', 'invoice', 'ref_id', 'trxid', 'kode_transaksi'], '-')),
        'id' => clean_text(first_value($raw, ['id', 'dbid', 'database_id', 'id_transaksi'], '-')),
        'tanggal' => clean_text(first_value($raw, ['tanggal', 'created_at', 'waktu', 'date'], date('d M Y H:i'))),
        'status' => $status,
        'nama_produk' => clean_text(first_value($raw, ['nama_produk', 'produk', 'product_name', 'product'], '-')),
        'tujuan' => clean_text(first_value($raw, ['tujuan', 'target', 'nomor_tujuan', 'customer_no'], '-')),
        'sn' => clean_text(first_value($raw, ['sn', 'serial_number', 'token', 'voucher', 'kode'], '')),
        'info' => clean_text(first_value($raw, ['info', 'catatan', 'message', 'keterangan'], '')),
        'harga' => money_number(first_value($raw, ['harga', 'total', 'amount', 'price'], 0)),
        'printed_at' => date('d M Y H:i'),
    ];
}

function status_class(string $status): string
{
    $status = mb_strtolower($status);
    if (str_contains($status, 'gagal') || str_contains($status, 'batal') || str_contains($status, 'fail')) {
        return 'fail';
    }
    if (str_contains($status, 'pending') || str_contains($status, 'menunggu')) {
        return 'pending';
    }
    if (str_contains($status, 'proses') || str_contains($status, 'jadwal') || str_contains($status, 'process')) {
        return 'process';
    }
    return 'success';
}

function ensure_storage(): bool
{
    if (!is_dir(STORAGE_DIR)) {
        @mkdir(STORAGE_DIR, 0755, true);
    }
    return is_dir(STORAGE_DIR) && is_writable(STORAGE_DIR);
}

function save_receipt(array $data): ?string
{
    if (!ensure_storage()) {
        return null;
    }

    $token = bin2hex(random_bytes(16));
    $file = STORAGE_DIR . '/' . $token . '.json';
    $payload = json_encode([
        'created_at' => date(DATE_ATOM),
        'receipt' => $data,
    ], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT);

    return file_put_contents($file, $payload, LOCK_EX) === false ? null : $token;
}

function load_receipt(string $token): ?array
{
    if (!preg_match('/^[a-f0-9]{32}$/', $token)) {
        return null;
    }

    $file = STORAGE_DIR . '/' . $token . '.json';
    if (!is_file($file)) {
        return null;
    }

    $data = json_decode((string) file_get_contents($file), true);
    return is_array($data['receipt'] ?? null) ? $data['receipt'] : null;
}

function script_url(): string
{
    $https = (!empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] !== 'off') || (($_SERVER['SERVER_PORT'] ?? '') === '443');
    $scheme = $https ? 'https' : 'http';
    $host = $_SERVER['HTTP_HOST'] ?? 'localhost';
    $path = parse_url($_SERVER['REQUEST_URI'] ?? '/struk.php', PHP_URL_PATH) ?: '/struk.php';
    return $scheme . '://' . $host . $path;
}

function render_empty_page(string $message): void
{
    http_response_code(400);
    ?><!doctype html>
<html lang="id">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Struk Transaksi</title>
  <style>
    body{margin:0;min-height:100vh;display:grid;place-items:center;background:#f6f4fb;font-family:Arial,sans-serif;color:#222;padding:20px}.box{max-width:460px;background:#fff;border-radius:14px;padding:22px;box-shadow:0 12px 34px rgba(44,28,74,.12)}h1{font-size:20px;margin:0 0 10px}p{line-height:1.55;color:#555}.code{background:#f3eefc;padding:10px;border-radius:10px;font-size:13px;word-break:break-all}
  </style>
</head>
<body>
  <main class="box">
    <h1>Data struk tidak ditemukan</h1>
    <p><?= h($message) ?></p>
    <p class="code">Pastikan riwayat-transaksi.html mengirim POST ke URL file struk.php ini.</p>
  </main>
</body>
</html><?php
}

$action = preg_replace('/[^a-z]/', '', (string) ($_POST['action'] ?? $_GET['action'] ?? 'print')) ?: 'print';
$rawReceipt = null;

if ($_SERVER['REQUEST_METHOD'] === 'POST') {
    $payload = $_POST['receipt_data'] ?? $_POST['data'] ?? null;
    $decoded = decode_receipt_payload(is_string($payload) ? $payload : null);
    if ($decoded !== null) {
        $receipt = normalize_receipt($decoded);
        $token = save_receipt($receipt);
        if ($token !== null) {
            header('Location: ' . script_url() . '?id=' . rawurlencode($token) . '&action=' . rawurlencode($action), true, 303);
            exit;
        }
        $rawReceipt = $receipt;
    }
} elseif (isset($_GET['id']) && is_string($_GET['id'])) {
    $rawReceipt = load_receipt($_GET['id']);
} elseif (isset($_GET['receipt_data']) || isset($_GET['data'])) {
    $payload = $_GET['receipt_data'] ?? $_GET['data'] ?? null;
    $decoded = decode_receipt_payload(is_string($payload) ? $payload : null);
    $rawReceipt = $decoded === null ? null : normalize_receipt($decoded);
}

if ($rawReceipt === null) {
    render_empty_page('Tidak ada data yang diterima oleh website PHP.');
    exit;
}

$receipt = normalize_receipt($rawReceipt);
$statusClass = status_class($receipt['status']);
$total = $receipt['harga'] > 0 ? 'Rp ' . number_format($receipt['harga'], 0, ',', '.') : '-';
$fileId = preg_replace('/[^a-zA-Z0-9_-]/', '-', $receipt['nomor_pembayaran']);
if ($fileId === '' || $fileId === '-') {
    $fileId = date('Ymd-His');
}
?><!doctype html>
<html lang="id">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1, viewport-fit=cover">
  <title>Struk Transaksi</title>
  <script src="https://cdnjs.cloudflare.com/ajax/libs/html2canvas/1.4.1/html2canvas.min.js" defer></script>
  <style>
    :root{--primary:#673ab7;--primary-dark:#4f2498;--ink:#242129;--muted:#6d6675;--line:#d8d0e6;--paper:#fff;--bg:#f5f2fb;--ok:#1e7a3a;--ok-bg:#e6f7ec;--proc:#0b6b80;--proc-bg:#e0f3f7;--pend:#805f00;--pend-bg:#fff4cf;--fail:#8a1c1c;--fail-bg:#fde2e2}*{box-sizing:border-box}body{margin:0;background:var(--bg);color:var(--ink);font-family:Arial,Helvetica,sans-serif;min-height:100vh;padding:18px}.wrap{width:min(420px,100%);margin:0 auto}.receipt{background:var(--paper);border-radius:14px;padding:22px 20px;box-shadow:0 14px 40px rgba(64,40,102,.14)}.head{text-align:center}.head h1{font-size:17px;margin:0 0 5px;letter-spacing:.4px;text-transform:uppercase}.head p{font-size:12px;color:var(--muted);margin:0}.divider{border:0;border-top:1px dashed var(--line);margin:13px 0}.row{display:flex;justify-content:space-between;gap:14px;margin:7px 0;font-size:13px;line-height:1.35}.key{color:var(--muted);flex:0 0 42%}.val{text-align:right;font-weight:600;word-break:break-word}.sn{color:var(--primary-dark);font-size:14px}.total .key,.total .val{font-size:16px;font-weight:800;color:var(--ink)}.badge{display:inline-block;border-radius:999px;padding:3px 9px;font-size:11px;font-weight:800}.badge.success{background:var(--ok-bg);color:var(--ok)}.badge.process{background:var(--proc-bg);color:var(--proc)}.badge.pending{background:var(--pend-bg);color:var(--pend)}.badge.fail{background:var(--fail-bg);color:var(--fail)}.foot{text-align:center;font-size:11px;color:var(--muted);line-height:1.5}.actions{display:grid;grid-template-columns:1fr 1fr 1fr;gap:9px;margin-top:14px}.btn{border:0;border-radius:10px;background:var(--primary);color:#fff;font-weight:800;font-size:13px;padding:12px 8px;cursor:pointer}.btn.secondary{background:#fff;color:var(--primary);border:1px solid var(--primary)}.btn.ghost{background:#efe8fb;color:var(--primary-dark)}.hint{margin:12px 0 0;color:var(--muted);font-size:12px;text-align:center;line-height:1.45}.preview{display:none;margin-top:14px;background:#17131f;color:#fff;border-radius:14px;padding:12px;text-align:center}.preview img{width:100%;height:auto;background:#fff;border-radius:10px}.preview p{font-size:12px;line-height:1.45;margin:0 0 10px;color:#eee}.edit-panel{display:none;margin-top:14px;background:#fff;border:1px solid var(--line);border-radius:14px;padding:16px;box-shadow:0 8px 24px rgba(64,40,102,.08)}.edit-panel.open{display:block}.edit-panel h2{margin:0 0 12px;font-size:14px;color:var(--primary-dark);text-transform:uppercase;letter-spacing:.5px}.field{display:block;margin-bottom:12px}.field label{display:block;font-size:12px;color:var(--muted);margin-bottom:5px;font-weight:700}.field input{width:100%;padding:11px 12px;border-radius:9px;border:1.5px solid var(--line);font-size:14px;font-family:inherit;color:var(--ink);background:#fafafd;outline:none;transition:border-color .15s}.field input:focus{border-color:var(--primary)}.edit-row{display:grid;grid-template-columns:1fr 1fr;gap:9px;margin-top:6px}@media print{body{background:#fff;padding:0}.wrap{width:100%;margin:0}.receipt{box-shadow:none;border-radius:0}.actions,.hint,.preview,.edit-panel{display:none!important}}
  </style>
</head>
<body>
  <main class="wrap">
    <section class="receipt" id="receiptCard">
      <div class="head">
        <h1 id="receiptMerchant"><?= h($receipt['merchant']) ?></h1>
        <p>Bukti Transaksi Digital</p>
      </div>
      <hr class="divider">
      <div class="row"><span class="key">No. Transaksi</span><span class="val"><?= h($receipt['nomor_pembayaran']) ?></span></div>
      <div class="row"><span class="key">DBid</span><span class="val"><?= h($receipt['id']) ?></span></div>
      <div class="row"><span class="key">Tanggal</span><span class="val"><?= h($receipt['tanggal']) ?></span></div>
      <div class="row"><span class="key">Status</span><span class="val"><span class="badge <?= h($statusClass) ?>"><?= h($receipt['status']) ?></span></span></div>
      <hr class="divider">
      <div class="row"><span class="key">Produk</span><span class="val"><?= h($receipt['nama_produk']) ?></span></div>
      <div class="row"><span class="key">Tujuan</span><span class="val"><?= h($receipt['tujuan']) ?></span></div>
      <?php if ($receipt['sn'] !== ''): ?><div class="row"><span class="key">SN/Token</span><span class="val sn"><?= h($receipt['sn']) ?></span></div><?php endif; ?>
      <?php if ($receipt['info'] !== ''): ?><div class="row"><span class="key">Info</span><span class="val"><?= h($receipt['info']) ?></span></div><?php endif; ?>
      <hr class="divider">
      <div class="row total"><span class="key">TOTAL</span><span class="val" id="receiptTotal"><?= h($total) ?></span></div>
      <hr class="divider">
      <div class="foot">Dicetak: <?= h($receipt['printed_at']) ?><br>Terima kasih atas transaksi Anda.</div>
    </section>

    <div class="actions">
      <button class="btn ghost" type="button" onclick="toggleEdit()" id="editToggleBtn"><i class="fa"></i>Edit</button>
      <button class="btn secondary" type="button" onclick="downloadReceipt()">Download</button>
      <button class="btn" type="button" onclick="printReceipt()">Cetak</button>
    </div>

    <section class="edit-panel" id="editPanel">
      <h2>Edit Struk</h2>
      <div class="field">
        <label for="editMerchant">Nama Toko</label>
        <input type="text" id="editMerchant" maxlength="60" value="<?= h($receipt['merchant']) ?>">
      </div>
      <div class="field">
        <label for="editHarga">Harga (Rp)</label>
        <input type="text" inputmode="numeric" id="editHarga" maxlength="12" value="<?= h($receipt['harga']) ?>">
      </div>
      <div class="edit-row">
        <button class="btn secondary" type="button" onclick="toggleEdit()">Batal</button>
        <button class="btn" type="button" onclick="saveEdit()">Simpan</button>
      </div>
    </section>

    <p class="hint">Edit nama toko atau harga, simpan, lalu Download / Cetak. Jika download diblokir, tekan & tahan gambar struk untuk menyimpan.</p>
    <div class="preview" id="imagePreviewBox">
      <p>Jika file tidak langsung tersimpan, tekan dan tahan gambar di bawah ini lalu pilih Simpan Gambar.</p>
      <img id="imagePreview" alt="Preview struk">
    </div>
  </main>

  <script>
    const ACTION = <?= json_encode($action) ?>;
    const FILE_NAME = <?= json_encode('struk-' . $fileId . '.png') ?>;

    function formatRupiah(n) {
      n = Math.max(0, parseInt(String(n).replace(/[^0-9]/g, ''), 10) || 0);
      return 'Rp ' + n.toLocaleString('id-ID');
    }

    function toggleEdit() {
      const panel = document.getElementById('editPanel');
      panel.classList.toggle('open');
      if (panel.classList.contains('open')) {
        panel.scrollIntoView({ behavior: 'smooth', block: 'center' });
      }
    }

    function saveEdit() {
      const merchant = (document.getElementById('editMerchant').value || '').trim().slice(0, 60) || 'STRUK TRANSAKSI';
      const hargaRaw = document.getElementById('editHarga').value;
      const hargaNum = parseInt(String(hargaRaw).replace(/[^0-9]/g, ''), 10) || 0;
      document.getElementById('receiptMerchant').textContent = merchant.toUpperCase();
      document.getElementById('receiptTotal').textContent = hargaNum > 0 ? formatRupiah(hargaNum) : '-';
      document.getElementById('editHarga').value = hargaNum;
      toggleEdit();
    }


    function printReceipt() {
      // Build a clean print document so mobile browsers (including in-app
      // browsers like Bukaolshop) reliably trigger the system print dialog.
      try {
        const card = document.getElementById('receiptCard');
        const html = '<!doctype html><html><head><meta charset="utf-8">'
          + '<title>Struk</title>'
          + '<style>body{margin:0;padding:16px;font-family:Arial,sans-serif;color:#000;background:#fff}'
          + '.r{width:320px;max-width:100%;margin:0 auto;font-size:13px;line-height:1.45}'
          + 'h1{font-size:16px;margin:0 0 4px;text-align:center;text-transform:uppercase}'
          + '.sub{text-align:center;font-size:12px;color:#444;margin-bottom:8px}'
          + 'hr{border:0;border-top:1px dashed #888;margin:8px 0}'
          + '.row{display:flex;justify-content:space-between;gap:10px;margin:5px 0}'
          + '.key{color:#555}.val{font-weight:700;text-align:right;word-break:break-word}'
          + '.total{font-size:15px;font-weight:800}'
          + '.foot{text-align:center;font-size:11px;color:#555;margin-top:8px}'
          + '@page{margin:8mm}</style></head><body>'
          + '<div class="r">' + card.innerHTML + '</div>'
          + '<script>window.onload=function(){setTimeout(function(){window.focus();window.print();},250);};<\/script>'
          + '</body></html>';

        // Try popup window first (works on desktop + most mobile browsers).
        const w = window.open('', '_blank');
        if (w && w.document) {
          w.document.open();
          w.document.write(html);
          w.document.close();
          return;
        }
      } catch (e) {}

      // Fallback: hidden iframe (works when popups are blocked, e.g. in-app browsers).
      try {
        let frame = document.getElementById('printFrame');
        if (frame) frame.remove();
        frame = document.createElement('iframe');
        frame.id = 'printFrame';
        frame.style.cssText = 'position:fixed;right:0;bottom:0;width:0;height:0;border:0;';
        document.body.appendChild(frame);
        const card = document.getElementById('receiptCard');
        const doc = frame.contentWindow.document;
        doc.open();
        doc.write('<!doctype html><html><head><meta charset="utf-8"><title>Struk</title>'
          + '<style>body{margin:0;padding:12px;font-family:Arial,sans-serif;color:#000}'
          + '.row{display:flex;justify-content:space-between;gap:10px;margin:4px 0;font-size:13px}'
          + '.key{color:#555}.val{font-weight:700;text-align:right}'
          + 'hr{border:0;border-top:1px dashed #888;margin:8px 0}'
          + '@page{margin:8mm}</style></head><body>'
          + card.innerHTML + '</body></html>');
        doc.close();
        setTimeout(function () {
          try {
            frame.contentWindow.focus();
            frame.contentWindow.print();
          } catch (err) {
            window.print();
          }
        }, 300);
        return;
      } catch (e) {}

      // Last resort.
      window.print();
    }

    async function downloadReceipt() {
      const card = document.getElementById('receiptCard');
      if (!window.html2canvas) {
        alert('Fitur download belum siap. Coba tekan tombol ini sekali lagi.');
        return;
      }
      try {
        const scale = Math.min(3, Math.max(2, window.devicePixelRatio || 2));
        const canvas = await html2canvas(card, { backgroundColor: '#ffffff', scale: scale, useCORS: true });
        const dataUrl = canvas.toDataURL('image/png');
        const link = document.createElement('a');
        link.href = dataUrl;
        link.download = FILE_NAME;
        document.body.appendChild(link);
        link.click();
        link.remove();
        document.getElementById('imagePreview').src = dataUrl;
        document.getElementById('imagePreviewBox').style.display = 'block';
      } catch (error) {
        alert('Gagal membuat gambar struk. Silakan gunakan fitur screenshot atau cetak.');
      }
    }

    window.addEventListener('load', function () {
      setTimeout(function () {
        if (ACTION === 'download') {
          downloadReceipt();
        }
        // NOTE: auto-print dihapus karena banyak browser mobile memblokir
        // window.print() tanpa interaksi pengguna. Pengguna klik tombol Cetak.
      }, 700);
    });
  </script>
</body>
</html>
