<?php
// ============================================================
//  MediCore — Purchase Entry
//  File: purchase/entry.php
// ============================================================
$page  = 'purchase/entry';
$title = 'Purchase Entry';
// The shared .page-header title/actions bar is skipped entirely (same
// $hidePageHeader convention used on Sale Entry / Dashboard) to reclaim
// vertical space for the item table/totals panel. The keyboard hints
// that used to live in that bar's #ph-actions slot now render directly
// inside the Purchase Mode toggle bar below instead.
$hidePageHeader = true;
require_once '../includes/header.php';
$today = date('Y-m-d');

$cfg       = $settings;
$cur       = $cfg['currency'];
$poPrefix  = $cfg['po_prefix']  ?? 'PO';
$suppliers = db_rows("SELECT id, name, phone FROM suppliers ORDER BY name");
$today     = today();

// Pre-fill from Requisition
$fromReq = null;
if (!empty($_GET['from_req']) && !empty($_SESSION['po_from_req'])) {
    $fromReq = $_SESSION['po_from_req'];
    unset($_SESSION['po_from_req']);
}

// Auto-generate next PO number
$nextPO = next_ref($poPrefix, 'purchases', 'invoice');
?>

<!-- ── Mode toggle bar ────────────────────────────── -->
<div style="display:flex;align-items:center;gap:10px;margin-bottom:12px;flex-wrap:wrap">
  <span style="font-size:12px;color:var(--text2);font-weight:600">Purchase Mode:</span>
  <div style="display:flex;background:var(--surface2);border:1px solid var(--border);
              border-radius:var(--rsm);overflow:hidden">
    <button id="mode-btn-1" class="mode-btn active"
            onclick="setMode('unit_trvat')"
            style="padding:6px 16px;font-size:12px;font-weight:600;border:none;
                   cursor:pointer;transition:all .18s">
      Unit TP + VAT
    </button>
    <button id="mode-btn-2" class="mode-btn"
            onclick="setMode('net_value')"
            style="padding:6px 16px;font-size:12px;font-weight:600;border:none;
                   cursor:pointer;transition:all .18s">
      Net Value
    </button>
  </div>
  <span id="mode-hint" style="font-size:11px;color:var(--text3)">
    Enter BTP and VAT separately per product
  </span>
  <span style="margin-left:auto;font-size:10.5px;color:var(--text3);
              white-space:nowrap;display:flex;align-items:center;gap:5px">
    <kbd style="font-family:inherit;font-size:10px;font-weight:700;color:var(--text2);
               background:var(--surface);border:1px solid var(--border);border-bottom-width:2px;
               border-radius:5px;padding:1px 5px">F2</kbd>Search
    <kbd style="font-family:inherit;font-size:10px;font-weight:700;color:var(--text2);
               background:var(--surface);border:1px solid var(--border);border-bottom-width:2px;
               border-radius:5px;padding:1px 5px">F9</kbd>Save PO
    <kbd style="font-family:inherit;font-size:10px;font-weight:700;color:var(--text2);
               background:var(--surface);border:1px solid var(--border);border-bottom-width:2px;
               border-radius:5px;padding:1px 5px">Esc</kbd>Clear
  </span>
</div>

<!-- ── Two-column layout ──────────────────────────── -->
<div style="display:grid;grid-template-columns:minmax(0,3fr) minmax(0,1fr);gap:12px;align-items:start">

  <!-- ════════════════════════════════════════════════
       Product search + items table — larger column (75%),
       visually placed on the LEFT (order:1) while staying
       first in source so nothing referencing these ids/JS
       had to move.
  ════════════════════════════════════════════════ -->
  <div style="display:flex;flex-direction:column;gap:10px;order:1">

    <!-- Barcode + Product search bar (barcode scan merged in, same
         pattern as Sale Entry's #barcode-input) -->
    <div class="card" style="padding:10px 14px">
      <div style="display:flex;gap:8px;align-items:center;flex-wrap:wrap">
        <span title="Barcode scanner" style="font-size:15px">🔫</span>
        <input id="po-barcode-input"
               placeholder="Scan barcode…  (F3)"
               autocomplete="off"
               style="width:150px;padding:7px 10px;font-size:13px;
                      font-family:'JetBrains Mono',monospace;
                      border:2px solid var(--warn);border-radius:var(--rsm);
                      outline:none;background:var(--surface);color:var(--text)">
        <label style="font-size:11px;display:flex;align-items:center;gap:3px;
                      color:var(--text2);white-space:nowrap;cursor:pointer">
          <input type="checkbox" id="po-barcode-auto" checked>
          Auto
        </label>
        <span id="po-barcode-feedback"
              style="font-size:11px;color:var(--text3);white-space:nowrap;
                     min-width:60px"></span>

        <div style="width:1px;align-self:stretch;background:var(--border)"></div>

        <div style="position:relative;flex:1;min-width:180px">
          <input id="prod-search"
                 placeholder="🔍  Type product name or barcode to add…  (F2)"
                 autocomplete="off"
                 style="width:100%;padding:8px 12px;font-size:13px;
                        border:2px solid var(--brand);border-radius:var(--rsm);
                        font-family:inherit;outline:none;
                        background:var(--surface);color:var(--text)">
          <!-- Suggestions dropdown -->
          <div id="suggestions"
               style="display:none;position:absolute;top:100%;left:0;right:0;
                      background:var(--surface);border:1px solid var(--border);
                      border-top:none;border-radius:0 0 var(--rsm) var(--rsm);
                      box-shadow:var(--shmd);z-index:200;max-height:260px;
                      overflow-y:auto"></div>
        </div>
        <button class="btn btn-sm"
                onclick="document.getElementById('prod-search').value='';
                         document.getElementById('suggestions').style.display='none';
                         document.getElementById('prod-search').focus()"
                title="Clear search (Esc)">✕ Clear</button>
      </div>
    </div>

    <!-- Items table -->
    <div class="card" style="padding:0;min-height:260px">
      <div class="ch" style="padding:10px 14px">
        <div class="ct">Purchase Items</div>
        <span id="item-count"
              style="font-size:11px;color:var(--text3)">0 item(s)</span>
      </div>
      <div class="tw" style="overflow-x:auto">
        <table id="items-table" style="min-width:860px">
          <thead>
            <tr id="items-head">
              <!-- columns injected by setMode() -->
            </tr>
          </thead>
          <tbody id="items-body">
            <tr id="empty-row">
              <td colspan="17" class="empty-state">
                Search and add products above
              </td>
            </tr>
          </tbody>
          <tfoot id="items-foot" style="display:none"></tfoot>
        </table>
      </div>
    </div>

  </div><!-- /left -->

  <!-- ════════════════════════════════════════════════
       Supplier + Invoice details + Totals — narrower column
       (25%), visually placed on the RIGHT (order:2).
  ════════════════════════════════════════════════ -->
  <div style="display:flex;flex-direction:column;gap:7px;position:sticky;top:12px;order:2;max-height:calc(100vh - 100px);overflow-y:auto">

    <!-- Supplier selector -->
    <div class="card">
      <div class="ch"><div class="ct">Supplier</div></div>
      <div style="display:flex;flex-direction:column;gap:8px;padding-top:4px">
        <div class="fg">
          <label>Select Supplier *</label>
          <select id="supplier-id" onchange="onSupplierChange()"
                  style="width:100%">
            <option value="">— Select Supplier —</option>
            <?php foreach ($suppliers as $s): ?>
            <option value="<?= $s['id'] ?>"
                    data-phone="<?= htmlspecialchars($s['phone']) ?>">
              <?= htmlspecialchars($s['name']) ?>
            </option>
            <?php endforeach; ?>
          </select>
        </div>
        <div id="sup-info"
             style="display:none;background:var(--brand-xlight);
                    border-radius:var(--rsm);padding:8px 10px;font-size:11px">
          <div id="sup-phone" style="color:var(--text2)"></div>
        </div>
      </div>
    </div>

    <!-- Invoice details -->
    <div class="card">
      <div class="ch"><div class="ct">Invoice Details</div></div>
      <div style="display:flex;flex-direction:column;gap:8px;padding-top:4px">

        <div class="fg">
          <label>PO Number</label>
          <input id="po-number" value="<?= htmlspecialchars($nextPO) ?>"
                 style="font-family:'JetBrains Mono',monospace;
                        background:var(--surface2);font-weight:700">
        </div>

        <div class="fg2">
          <div class="fg">
            <label>Purchase Date</label>
            <input id="po-date" type="date" value="<?= $today ?>">
          </div>
          <div class="fg">
            <label>Supplier Invoice #</label>
            <input id="sup-invoice" placeholder="Optional">
          </div>
        </div>

        <div class="fg">
          <label style="display:flex;align-items:center;gap:6px">
            Discount
            <button type="button" id="po-disc-mode-btn"
                    onclick="setPoDiscMode(poDiscMode==='pct'?'flat':'pct')"
                    title="Switch between % and flat amount"
                    style="padding:1px 8px;font-size:10px;font-weight:700;
                           border-radius:7px;border:1px solid var(--brand-light);
                           cursor:pointer;background:var(--brand);color:#fff">
              %
            </button>
          </label>
          <div style="display:flex;align-items:center;gap:5px">
            <input id="discount" type="number" step="0.01" min="0"
                   max="100" value="0"
                   oninput="recalcTotals()" style="flex:1">
            <span id="po-disc-suffix" style="color:var(--text3);font-size:11px;
                                              min-width:12px">%</span>
          </div>
          <div id="po-disc-hint" class="input-hint" style="display:none"></div>
        </div>

      </div>
    </div>

    <!-- Totals panel -->
    <div class="card" style="background:var(--brand-xlight);
                              border:1px solid var(--brand-light)">
      <div class="ch"><div class="ct" style="color:var(--brand)">📊 Totals</div></div>
      <div style="display:flex;flex-direction:column;gap:6px;font-size:12.5px">

        <!-- 2x2 grid instead of 4 stacked rows — same figures, roughly
             half the vertical space, so the right column is less likely
             to force the page to scroll. -->
        <div style="display:grid;grid-template-columns:1fr 1fr;gap:4px 10px">
          <div>
            <div style="color:var(--text2);font-size:10.5px">Total BTP</div>
            <span class="mono" id="tot-btp" style="font-weight:600">
              <?= $cur ?>0.00
            </span>
          </div>
          <div>
            <div style="color:var(--text2);font-size:10.5px">Total VAT</div>
            <span class="mono" id="tot-vat" style="font-weight:600;color:var(--warn)">
              <?= $cur ?>0.00
            </span>
          </div>
          <div>
            <div style="color:var(--text2);font-size:10.5px">Sub Total</div>
            <span class="mono" id="tot-sub" style="font-weight:600">
              <?= $cur ?>0.00
            </span>
          </div>
          <div>
            <div style="color:var(--text2);font-size:10.5px">Discount</div>
            <span class="mono" id="tot-disc"
                  style="font-weight:600;color:var(--danger)">
              - <?= $cur ?>0.00
            </span>
          </div>
        </div>

        <div style="border-top:2px solid var(--brand-light);
                    padding-top:8px;margin-top:2px;
                    display:flex;justify-content:space-between;
                    align-items:center">
          <span style="font-weight:700;color:var(--brand)">Final Amount</span>
          <span class="mono" id="tot-final"
                style="font-weight:800;font-size:16px;color:var(--brand)">
            <?= $cur ?>0.00
          </span>
        </div>

        <!-- Payment -->
        <div style="margin-top:6px;display:flex;flex-direction:column;gap:6px">
          <div class="fg">
            <label>Paid Now (<?= $cur ?>)</label>
            <input id="paid" type="number" step="0.01" min="0"
                   value="0" oninput="recalcPayment()"
                   style="font-weight:700;font-size:13px">
          </div>
          <div class="fg">
            <label>Account</label>
            <select id="pay-account" style="width:100%">
              <option>Cash</option>
              <option>Bank</option>
              <option>MFS</option>
              <option>Card</option>
            </select>
          </div>
          <div style="display:flex;justify-content:space-between;
                      padding:8px 10px;background:var(--surface);
                      border-radius:var(--rsm);
                      border:1px solid var(--border)">
            <span style="color:var(--text2);font-size:12px">Due / Pending</span>
            <span class="mono" id="tot-due"
                  style="font-weight:700;color:var(--danger)">
              <?= $cur ?>0.00
            </span>
          </div>
        </div>

        <div class="fg" style="margin-top:4px">
          <label>Notes</label>
          <textarea id="po-notes" rows="2"
            style="padding:6px 9px;border:1px solid var(--border);
                   border-radius:var(--rsm);font-family:inherit;
                   font-size:11.5px;resize:vertical;outline:none;
                   background:var(--surface);color:var(--text)"
            placeholder="Optional notes…"></textarea>
        </div>

      </div>
    </div><!-- /totals -->

    <!-- Save button -->
    <div style="display:flex;gap:6px;margin-bottom:6px">
      <button onclick="holdPurchase()"
              class="btn"
              style="flex:1;padding:9px;font-size:13px;font-weight:700;
                     background:#fbbf24;color:#78350f;border-color:#f59e0b"
              title="Hold Purchase (Ctrl+H)">
        🔒 Hold
      </button>
      <button onclick="showPurchaseHolds()"
              class="btn"
              style="flex:1;padding:9px;font-size:13px;font-weight:700;
                     background:var(--surface2);color:var(--text2)"
              title="Resume held purchase (Ctrl+R)">
        📋 Resume
      </button>
    </div>
    <button id="save-btn" onclick="savePO()"
            class="btn btn-primary"
            style="width:100%;padding:11px;font-size:14px;
                   font-weight:700;letter-spacing:.3px">
      💾 Save Purchase Order &nbsp; (F9)
    </button>

    <div id="save-err"
         style="display:none;background:#fde8e8;border:1px solid #f5a0a0;
                color:var(--danger);padding:8px 12px;border-radius:var(--rsm);
                font-size:12px"></div>

    <div id="po-draft-banner"
         style="display:none;background:#fff3dc;border:1px solid #f59e0b;
                border-radius:var(--rsm);padding:8px 12px;font-size:12.5px;
                color:#78350f">
      ⚡ Unsaved draft found.
      <button onclick="restorePoDraft()"
              style="margin-left:6px;font-weight:700;color:var(--brand);
                     background:none;border:none;cursor:pointer">Continue</button>
      <button onclick="discardPoDraft()"
              style="margin-left:4px;color:var(--text3);background:none;border:none;cursor:pointer">Discard</button>
    </div>

  </div><!-- /right -->

</div><!-- /grid -->

<!-- ── Styles for mode buttons ────────────────────── -->
<style>
.mode-btn          { background:transparent; color:var(--text2); }
.mode-btn.active   { background:var(--brand); color:#fff; }
.mode-btn:hover:not(.active) { background:var(--brand-xlight); }

.sug-item {
  padding:9px 12px;font-size:12px;cursor:pointer;
  display:grid;grid-template-columns:1fr auto auto;gap:8px;
  align-items:center;border-bottom:1px solid var(--border);
}
.sug-item:hover       { background:var(--brand-xlight); }
.sug-item:last-child  { border-bottom:none; }
/* .sug-out removed — purchase allows all products */
.sug-badge            { font-size:10px;padding:2px 6px;border-radius:10px; }
</style>

<script>
// ── State ─────────────────────────────────────────────────────
let MODE    = 'unit_trvat';   // 'unit_trvat' | 'net_value'
let items   = [];             // array of item objects
let srchTmr = null;
const CUR   = '<?= addslashes($cur) ?>';

// ── Mode toggle ───────────────────────────────────────────────
function setMode(m) {
  MODE = m;
  document.getElementById('mode-btn-1').classList.toggle('active', m === 'unit_trvat');
  document.getElementById('mode-btn-2').classList.toggle('active', m === 'net_value');
  document.getElementById('mode-hint').textContent = m === 'unit_trvat'
    ? 'Enter BTP and VAT separately per product'
    : 'Enter combined net TP+VAT per product';
  renderHead();
  renderItems();
  renderFoot();
}

// ── Table header columns per mode ────────────────────────────
function renderHead() {
  const head = document.getElementById('items-head');
  if (MODE === 'unit_trvat') {
    head.innerHTML = `
      <th style="width:30px">#</th>
      <th>Product</th>
      <th style="width:60px;text-align:center">Boxes</th>
      <th style="width:55px;text-align:center">Qty/Box</th>
      <th style="width:65px;text-align:center">Total Pcs</th>
      <th style="width:80px;text-align:right">BTP/Box</th>
      <th style="width:70px;text-align:right">VAT/Box</th>
      <th style="width:80px;text-align:right">TP+VAT</th>
      <th style="width:80px;text-align:right">MRP/pc</th>
      <th style="width:78px;text-align:right" title="Current/default Sale Rate — starts at MRP, editable to model a planned selling price">Sale Rate</th>
      ${marginTh('buy','Buy %','Buy % = Discount received from MRP',58)}
      ${marginTh('sale','Sale %','Sale % = Discount given to customers based on Sale Rate',58)}
      ${marginTh('profit','Profit %','Profit % = Net margin retained from MRP',64)}
      <th style="width:55px;text-align:center">Free</th>
      <th style="width:75px;text-align:center">Expiry</th>
      <th style="width:80px;text-align:right">Total</th>
      <th style="width:32px"></th>`;
  } else {
    head.innerHTML = `
      <th style="width:30px">#</th>
      <th>Product</th>
      <th style="width:60px;text-align:center">Boxes</th>
      <th style="width:55px;text-align:center">Qty/Box</th>
      <th style="width:65px;text-align:center">Total Pcs</th>
      <th style="width:90px;text-align:right">Net TP+VAT/Box</th>
      <th style="width:80px;text-align:right">MRP/pc</th>
      <th style="width:78px;text-align:right" title="Current/default Sale Rate — starts at MRP, editable to model a planned selling price">Sale Rate</th>
      ${marginTh('buy','Buy %','Buy % = Discount received from MRP',58)}
      ${marginTh('sale','Sale %','Sale % = Discount given to customers based on Sale Rate',58)}
      ${marginTh('profit','Profit %','Profit % = Net margin retained from MRP',64)}
      <th style="width:55px;text-align:center">Free</th>
      <th style="width:75px;text-align:center">Expiry</th>
      <th style="width:80px;text-align:right">Total</th>
      <th style="width:32px"></th>`;
  }
}

// ── Product search ────────────────────────────────────────────
const searchInput = document.getElementById('prod-search');
const sugBox      = document.getElementById('suggestions');

searchInput.addEventListener('input', () => {
  clearTimeout(srchTmr);
  const q = searchInput.value.trim();
  // A single character matches almost the entire catalog — wait for
  // 2+ chars before hitting the server at all.
  if (q.length < 2) { sugBox.style.display = 'none'; return; }
  srchTmr = setTimeout(() => fetchSuggestions(q), 220);
});

searchInput.addEventListener('keydown', e => {
  if (e.key === 'Escape') {
    searchInput.value = '';
    sugBox.style.display = 'none';
  }
  if (e.key === 'ArrowDown') {
    const first = sugBox.querySelector('.sug-item');
    first?.focus();
  }
});

// Close suggestions on outside click
document.addEventListener('click', e => {
  if (!searchInput.contains(e.target) && !sugBox.contains(e.target))
    sugBox.style.display = 'none';
});

async function fetchSuggestions(q) {
  const offline = window.MediOffline && !MediOffline.isOnline();
  let data = [];
  if (offline) {
    data = (await MediOffline.searchProducts(q)) || [];
  } else {
    // suggest:true — lightweight dropdown mode (skips server COUNT(*),
    // hard-caps rows) since only the first 12 are ever rendered below.
    const r = await apiPost('../api/products.php', { action: 'list', q, suggest: true, page_size: 12 }).catch(() => null);
    if (r && r.success) {
      data = r.data;
      if (window.MediOffline) data.forEach(p => MediOffline.cacheOne('products', p));
    } else if (window.MediOffline) {
      data = (await MediOffline.searchProducts(q)) || [];
    }
  }
  if (!data.length) {
    sugBox.innerHTML = `<div style="padding:10px 12px;font-size:12px;
      color:var(--text3)">No products found for "${esc(q)}"</div>`;
    sugBox.style.display = 'block';
    return;
  }

  sugBox.innerHTML = data.slice(0, 12).map(p => {
    const stock   = parseInt(p.stock_pcs);
    // Purchase page: ALL products are selectable regardless of stock
    const stockBg = stock <= 0  ? '#fde8e8' : stock < 10 ? '#fff3dc' : '#d4f0e6';
    const stockCl = stock <= 0  ? 'var(--danger)' : stock < 10 ? 'var(--warn)' : 'var(--accent)';

    return `<div class="sug-item"
               tabindex="0"
               onkeydown="if(event.key==='Enter')addItem(${JSON.stringify(p).replace(/"/g,'&quot;')})"
               onclick="addItem(${JSON.stringify(p).replace(/"/g,'&quot;')})">
      <div>
        <div style="font-weight:600;color:var(--text)">${formIconName(p.form, p.name)}
          ${p.strength?`<span style="font-size:10px;color:var(--text3);margin-left:4px">${esc(p.strength)}</span>`:''}
        </div>
        <div style="font-size:10px;color:var(--text3);margin-top:1px">
          ${esc(p.company_name||'')}${p.form?` · ${esc(p.form)}`:''}
          ${p.rack?` · Rack: ${esc(p.rack)}`:''}
        </div>
      </div>
      <span class="sug-badge"
            style="background:${stockBg};color:${stockCl}">
        Stock: ${stock}
      </span>
      <span class="sug-badge"
            style="background:var(--brand-xlight);color:var(--brand)">
        ${CUR}${parseFloat(p.btp_vat||0).toFixed(2)}/box
      </span>
    </div>`;
  }).join('');

  sugBox.style.display = 'block';
}

// ── Add item to table ─────────────────────────────────────────
function addItem(p) {
  sugBox.style.display = 'none';
  searchInput.value    = '';
  searchInput.focus();

  // If already in list, just focus its boxes input
  const existing = items.findIndex(i => i.product_id === p.id);
  if (existing >= 0) {
    document.getElementById(`boxes-${existing}`)?.select();
    return;
  }

  items.push({
    product_id:   p.id,
    product_name: p.name,
    form:         p.form        || '',
    strength:     p.strength    || '',
    qty_in_box:   parseInt(p.qty_in_box) || 1,
    btp:          parseFloat(p.btp)      || 0,
    vat:          parseFloat(p.vat)      || 0,
    btp_vat:      parseFloat(p.btp_vat)  || 0,
    sale_price:   parseFloat(p.sale_price)|| 0,
    // Sale Rate defaults to the product's current/default sale price —
    // independently editable so purchasing staff can model a different
    // planned selling price without changing the MRP itself.
    sale_rate:    parseFloat(p.sale_price)|| 0,
    // entry fields
    boxes:        1,
    free:         0,
    exp:          '',
  });

  renderItems();
  recalcTotals();
  const idx = items.length - 1;
  setTimeout(() => document.getElementById(`boxes-${idx}`)?.select(), 50);
}

// ── Render items table rows ───────────────────────────────────
function renderItems() {
  const tbody = document.getElementById('items-body');
  document.getElementById('item-count').textContent = `${items.length} item(s)`;

  if (!items.length) {
    tbody.innerHTML = `<tr id="empty-row">
      <td colspan="${MODE === 'unit_trvat' ? 17 : 15}" class="empty-state">Search and add products above</td>
    </tr>`;
    renderFoot();
    return;
  }

  tbody.innerHTML = items.map((item, i) => {
    const totalPcs   = (item.boxes * item.qty_in_box);
    const lineTotal  = item.boxes * (MODE === 'unit_trvat' ? item.btp_vat : item.btp_vat);

    if (MODE === 'unit_trvat') {
      return `<tr>
        <td style="color:var(--text3);font-size:11px">${i+1}</td>
        <td>
          <div style="font-weight:600;font-size:12px">${formIconName(item.form, item.product_name)}</div>
          ${item.strength?`<div style="font-size:10px;color:var(--text3)">${esc(item.strength)}</div>`:''}
        </td>
        <td style="text-align:center">
          <input type="number" id="boxes-${i}" value="${item.boxes}"
                 min="1" style="width:54px;text-align:center"
                 oninput="updItem(${i},'boxes',this.value)">
        </td>
        <td style="text-align:center">
          <input type="number" id="qib-${i}" value="${item.qty_in_box}"
                 min="1" style="width:50px;text-align:center"
                 oninput="updItem(${i},'qty_in_box',this.value)">
        </td>
        <td class="mono" style="text-align:center;font-weight:700">
          ${totalPcs}
        </td>
        <td style="text-align:right">
          <input type="number" id="btp-${i}" value="${item.btp.toFixed(2)}"
                 min="0" step="0.01" style="width:76px;text-align:right"
                 oninput="updItem(${i},'btp',this.value)">
        </td>
        <td style="text-align:right">
          <input type="number" id="vat-${i}" value="${item.vat.toFixed(2)}"
                 min="0" step="0.01" style="width:66px;text-align:right"
                 oninput="updItem(${i},'vat',this.value)">
        </td>
        <td class="mono" style="text-align:right;font-weight:700;color:var(--brand)">
          ${CUR}${item.btp_vat.toFixed(2)}
        </td>
        <td style="text-align:right">
          <input type="number" id="mrp-${i}" value="${item.sale_price.toFixed(2)}"
                 min="0" step="0.01" style="width:76px;text-align:right"
                 oninput="updItem(${i},'sale_price',this.value)">
        </td>
        <td style="text-align:right">
          <input type="number" id="salerate-${i}" value="${item.sale_rate.toFixed(2)}"
                 min="0" step="0.01" style="width:74px;text-align:right"
                 oninput="updItem(${i},'sale_rate',this.value)">
        </td>
        <td class="mono" id="buy-${i}" style="text-align:right;font-size:11px;color:var(--text2)"
            title="Buy % = Discount received from MRP">${buyPct(item).toFixed(2)}%</td>
        <td class="mono" id="sale-${i}" style="text-align:right;font-size:11px;color:var(--text2)"
            title="Sale % = Discount given to customers based on Sale Rate">${salePct(item).toFixed(2)}%</td>
        <td class="mono" id="profit-${i}" style="text-align:right;font-size:11px;font-weight:700;color:${profitColor(profitPct(item))}"
            title="Profit % = Net margin retained from MRP">${profitPct(item).toFixed(2)}%</td>
        <td style="text-align:center">
          <input type="number" id="free-${i}" value="${item.free}"
                 min="0" style="width:50px;text-align:center"
                 oninput="updItem(${i},'free',this.value)">
        </td>
        <td style="text-align:center">
          <input type="month" id="exp-${i}" value="${item.exp}"
                 style="width:112px;font-size:11px"
                 oninput="updItem(${i},'exp',this.value)">
        </td>
        <td class="mono" style="text-align:right;font-weight:700">
          ${CUR}${lineTotal.toFixed(2)}
        </td>
        <td>
          <button onclick="removeItem(${i})"
                  style="background:none;border:none;cursor:pointer;
                         color:var(--danger);font-size:14px;padding:2px 4px"
                  title="Remove">✕</button>
        </td>
      </tr>`;
    } else {
      // Net Value mode — single combined TP+VAT column
      return `<tr>
        <td style="color:var(--text3);font-size:11px">${i+1}</td>
        <td>
          <div style="font-weight:600;font-size:12px">${formIconName(item.form, item.product_name)}</div>
          ${item.strength?`<div style="font-size:10px;color:var(--text3)">${esc(item.strength)}</div>`:''}
        </td>
        <td style="text-align:center">
          <input type="number" id="boxes-${i}" value="${item.boxes}"
                 min="1" style="width:54px;text-align:center"
                 oninput="updItem(${i},'boxes',this.value)">
        </td>
        <td style="text-align:center">
          <input type="number" id="qib-${i}" value="${item.qty_in_box}"
                 min="1" style="width:50px;text-align:center"
                 oninput="updItem(${i},'qty_in_box',this.value)">
        </td>
        <td class="mono" style="text-align:center;font-weight:700">
          ${totalPcs}
        </td>
        <td style="text-align:right">
          <input type="number" id="btpvat-${i}" value="${item.btp_vat.toFixed(2)}"
                 min="0" step="0.01" style="width:86px;text-align:right"
                 oninput="updItem(${i},'btp_vat',this.value)">
        </td>
        <td style="text-align:right">
          <input type="number" id="mrp-${i}" value="${item.sale_price.toFixed(2)}"
                 min="0" step="0.01" style="width:76px;text-align:right"
                 oninput="updItem(${i},'sale_price',this.value)">
        </td>
        <td style="text-align:right">
          <input type="number" id="salerate-${i}" value="${item.sale_rate.toFixed(2)}"
                 min="0" step="0.01" style="width:74px;text-align:right"
                 oninput="updItem(${i},'sale_rate',this.value)">
        </td>
        <td class="mono" id="buy-${i}" style="text-align:right;font-size:11px;color:var(--text2)"
            title="Buy % = Discount received from MRP">${buyPct(item).toFixed(2)}%</td>
        <td class="mono" id="sale-${i}" style="text-align:right;font-size:11px;color:var(--text2)"
            title="Sale % = Discount given to customers based on Sale Rate">${salePct(item).toFixed(2)}%</td>
        <td class="mono" id="profit-${i}" style="text-align:right;font-size:11px;font-weight:700;color:${profitColor(profitPct(item))}"
            title="Profit % = Net margin retained from MRP">${profitPct(item).toFixed(2)}%</td>
        <td style="text-align:center">
          <input type="number" id="free-${i}" value="${item.free}"
                 min="0" style="width:50px;text-align:center"
                 oninput="updItem(${i},'free',this.value)">
        </td>
        <td style="text-align:center">
          <input type="month" id="exp-${i}" value="${item.exp}"
                 style="width:112px;font-size:11px"
                 oninput="updItem(${i},'exp',this.value)">
        </td>
        <td class="mono" style="text-align:right;font-weight:700">
          ${CUR}${lineTotal.toFixed(2)}
        </td>
        <td>
          <button onclick="removeItem(${i})"
                  style="background:none;border:none;cursor:pointer;
                         color:var(--danger);font-size:14px;padding:2px 4px"
                  title="Remove">✕</button>
        </td>
      </tr>`;
    }
  }).join('');
  renderFoot();
}

// ── Update a single item field ────────────────────────────────
function updItem(i, field, val) {
  const item = items[i];
  if (['boxes','qty_in_box','free'].includes(field))
    item[field] = Math.max(0, parseInt(val) || 0);
  else if (['btp','vat','btp_vat','sale_price','sale_rate'].includes(field))
    item[field] = Math.max(0, parseFloat(val) || 0);
  else
    item[field] = val;

  // Auto-calc btp_vat when btp or vat changes (unit_trvat mode)
  if (field === 'btp' || field === 'vat')
    item.btp_vat = item.btp + item.vat;

  // Update total pcs display and line total without full re-render
  const totalPcs  = item.boxes * item.qty_in_box;
  const lineTotal = item.boxes * item.btp_vat;

  const tpcEl = document.querySelector(`#items-body tr:nth-child(${i+1}) td:nth-child(5)`);
  const totEl = document.querySelector(
    `#items-body tr:nth-child(${i+1}) td:nth-last-child(2)`
  );
  if (tpcEl) tpcEl.textContent = totalPcs;
  if (totEl) totEl.textContent = CUR + lineTotal.toFixed(2);

  // Auto-update btp_vat display in unit_trvat mode
  if (MODE === 'unit_trvat' && (field === 'btp' || field === 'vat')) {
    const bvEl = document.querySelector(
      `#items-body tr:nth-child(${i+1}) td:nth-child(8)`
    );
    if (bvEl) bvEl.textContent = CUR + item.btp_vat.toFixed(2);
  }

  // Margin columns (Buy % / Sale % / Profit %) — recalculate instantly
  // whenever Purchase Rate (btp/vat/btp_vat), MRP (sale_price), or
  // Sale Rate changes.
  const buyEl = document.getElementById(`buy-${i}`);
  if (buyEl) buyEl.textContent = buyPct(item).toFixed(2) + '%';

  const saleEl = document.getElementById(`sale-${i}`);
  if (saleEl) saleEl.textContent = salePct(item).toFixed(2) + '%';

  const profEl = document.getElementById(`profit-${i}`);
  if (profEl) {
    const pp = profitPct(item);
    profEl.textContent = pp.toFixed(2) + '%';
    profEl.style.color = profitColor(pp);
  }

  renderFoot();
  recalcTotals();
}

function removeItem(i) {
  items.splice(i, 1);
  renderItems();
  recalcTotals();
}

// ── Margin-analysis columns (Buy % / Sale % / Profit %) ──────────
// MRP = the "MRP/pc" field (the retail ceiling this purchase will set).
// Purchase Rate = this PO's cost per piece (BTP+VAT / Qty per Box).
// Sale Rate = the current/default selling price — pre-filled from the
// product's existing sale_price when added, but independently editable
// so purchasing staff can model a different planned selling price.
function pcPurchaseRate(it) { return it.qty_in_box > 0 ? (it.btp_vat / it.qty_in_box) : 0; }
function pcMRP(it)          { return parseFloat(it.sale_price) || 0; }
function pcSaleRate(it)     { return parseFloat(it.sale_rate)  || 0; }

function buyPct(it) {
  const mrp = pcMRP(it);
  return mrp > 0 ? ((mrp - pcPurchaseRate(it)) / mrp) * 100 : 0;
}
function salePct(it) {
  const mrp = pcMRP(it);
  return mrp > 0 ? ((mrp - pcSaleRate(it)) / mrp) * 100 : 0;
}
function profitPct(it) {
  // Always equals buyPct(it) - salePct(it) by construction:
  // (mrp-purchase)/mrp*100 - (mrp-sale)/mrp*100 = (sale-purchase)/mrp*100
  const mrp = pcMRP(it);
  return mrp > 0 ? ((pcSaleRate(it) - pcPurchaseRate(it)) / mrp) * 100 : 0;
}
function profitColor(pct) {
  if (pct < 3)   return 'var(--danger)';  // Red    < 3%
  if (pct < 7)   return '#f59e0b';        // Orange 3%–7%
  if (pct <= 12) return 'var(--accent)';  // Green  7%–12%
  return '#0d6e49';                       // Dark Green > 12%
}

// ── Sortable margin headers ───────────────────────────────────
let marginSort = { col: null, dir: 1 };

function sortArrow(col) {
  if (marginSort.col !== col) return '';
  return marginSort.dir === 1 ? ' ▲' : ' ▼';
}

function marginTh(col, label, title, width) {
  return `<th style="width:${width}px;text-align:right;cursor:pointer;user-select:none"
              title="${title} — click to sort" onclick="sortByMargin('${col}')">
            ${label}${sortArrow(col)}
          </th>`;
}

function sortByMargin(col) {
  if (!items.length) return;
  if (marginSort.col === col) marginSort.dir *= -1;
  else { marginSort.col = col; marginSort.dir = 1; }

  const fn = col === 'buy' ? buyPct : col === 'sale' ? salePct : profitPct;
  items.sort((a, b) => (fn(a) - fn(b)) * marginSort.dir);

  renderHead();
  renderItems();
}

// ── Footer summary — Avg Buy % / Avg Sale % / Avg Profit % / Total ──
function renderFoot() {
  const tfoot = document.getElementById('items-foot');
  if (!tfoot) return;

  if (!items.length) {
    tfoot.style.display = 'none';
    tfoot.innerHTML = '';
    return;
  }
  tfoot.style.display = '';

  const n           = items.length;
  const avgBuy       = items.reduce((s, it) => s + buyPct(it),    0) / n;
  const avgSale      = items.reduce((s, it) => s + salePct(it),   0) / n;
  const avgProfit    = items.reduce((s, it) => s + profitPct(it), 0) / n;
  const totalValue   = items.reduce((s, it) => s + it.boxes * it.btp_vat, 0);

  // Columns before "Sale Rate" (inclusive) differ by mode — everything
  // up to and including MRP/pc + Sale Rate gets spanned by the label.
  const leadCols = MODE === 'unit_trvat' ? 10 : 8;

  tfoot.innerHTML = `
    <tr style="background:var(--surface2);font-weight:700">
      <td colspan="${leadCols}"
          style="text-align:right;padding:8px 10px;font-size:12px;color:var(--text2)">
        Averages / Total Purchase Value
      </td>
      <td class="mono" style="text-align:right;padding:8px 10px;color:var(--text2)">
        ${avgBuy.toFixed(2)}%
      </td>
      <td class="mono" style="text-align:right;padding:8px 10px;color:var(--text2)">
        ${avgSale.toFixed(2)}%
      </td>
      <td class="mono" style="text-align:right;padding:8px 10px;color:${profitColor(avgProfit)}">
        ${avgProfit.toFixed(2)}%
      </td>
      <td colspan="2"></td>
      <td class="mono" style="text-align:right;padding:8px 10px;color:var(--brand);font-weight:800">
        ${CUR}${totalValue.toFixed(2)}
      </td>
      <td></td>
    </tr>`;
}

// ── Discount mode toggle (% or flat amount) ────────────────────
// Same pattern as Sale Entry's #disc-mode-btn — the `discount` input
// itself is reused for both modes, and whatever the user types is
// always converted to an equivalent percentage before it's sent to
// the server (see savePO()), so api/purchases.php needs no changes
// at all to support this.
let poDiscMode = 'pct'; // 'pct' | 'flat'

function setPoDiscMode(mode) {
  poDiscMode = mode;
  const btn    = document.getElementById('po-disc-mode-btn');
  const suffix = document.getElementById('po-disc-suffix');
  const input  = document.getElementById('discount');
  input.value  = ''; // clear on mode switch to avoid confusion
  if (mode === 'flat') {
    btn.textContent      = CUR;
    btn.style.background = 'var(--warn)';
    suffix.textContent   = '';
    input.removeAttribute('max'); // flat has no fixed max — capped at bill total in recalcTotals()
    input.placeholder    = '0.00';
  } else {
    btn.textContent      = '%';
    btn.style.background = 'var(--brand)';
    suffix.textContent   = '%';
    input.max             = '100';
    input.placeholder    = '0';
  }
  recalcTotals();
}

// ── Recalculate totals panel ──────────────────────────────────
function recalcTotals() {
  let totalBtp = 0, totalVat = 0, totalBtpVat = 0;

  items.forEach(item => {
    if (MODE === 'unit_trvat') {
      totalBtp    += item.boxes * item.btp;
      totalVat    += item.boxes * item.vat;
      totalBtpVat += item.boxes * item.btp_vat;
    } else {
      totalBtpVat += item.boxes * item.btp_vat;
    }
  });

  const discInput = document.getElementById('discount');
  const discHint  = document.getElementById('po-disc-hint');
  let discAmt;
  if (poDiscMode === 'flat') {
    discAmt = Math.max(0, parseFloat(discInput.value) || 0);
    if (discAmt > totalBtpVat && totalBtpVat > 0) {
      discAmt = totalBtpVat;
      discInput.value = totalBtpVat.toFixed(2);
      if (discHint) {
        discHint.textContent   = `⚠ Discount capped at bill total (${CUR}${totalBtpVat.toFixed(2)})`;
        discHint.style.display = 'block';
      }
      discInput.style.borderColor = 'var(--warn)';
    } else {
      if (discHint) discHint.style.display = 'none';
      discInput.style.borderColor = '';
    }
  } else {
    const discPctIn = Math.max(0, Math.min(100, parseFloat(discInput.value) || 0));
    discAmt = totalBtpVat * (discPctIn / 100);
    if (discHint) discHint.style.display = 'none';
    discInput.style.borderColor = '';
  }

  const final    = totalBtpVat - discAmt;
  const paid     = parseFloat(document.getElementById('paid').value) || 0;
  const due      = Math.max(0, final - paid);

  document.getElementById('tot-btp').textContent
    = CUR + totalBtp.toFixed(2);
  document.getElementById('tot-vat').textContent
    = CUR + totalVat.toFixed(2);
  document.getElementById('tot-sub').textContent
    = CUR + totalBtpVat.toFixed(2);
  document.getElementById('tot-disc').textContent
    = '- ' + CUR + discAmt.toFixed(2);
  document.getElementById('tot-final').textContent
    = CUR + final.toFixed(2);
  document.getElementById('tot-due').textContent
    = CUR + due.toFixed(2);

  document.getElementById('tot-due').style.color
    = due > 0 ? 'var(--danger)' : 'var(--accent)';

  // Event-driven auto-draft — fires on every recalculation
  // (item add/remove/qty/price/discount/payment change), per spec.
  if (typeof savePoDraft === 'function' && items.length) savePoDraft();
}

function recalcPayment() { recalcTotals(); }

// ── Supplier info display ─────────────────────────────────────
function onSupplierChange() {
  const sel   = document.getElementById('supplier-id');
  const opt   = sel.options[sel.selectedIndex];
  const phone = opt?.dataset?.phone || '';
  const info  = document.getElementById('sup-info');
  const pEl   = document.getElementById('sup-phone');

  if (sel.value && phone) {
    pEl.textContent = '📞 ' + phone;
    info.style.display = 'block';
  } else {
    info.style.display = 'none';
  }
}

// ── Barcode scanner ───────────────────────────────────────────
// Same pattern as Sale Entry's #barcode-input: a USB/handheld scanner
// types the code then sends Enter. "Auto" checked (default) adds the
// product straight away via addItem() — which already handles both
// cases (brand-new row, or focusing the existing row's Boxes input if
// it's already on this PO) — same as clicking a search suggestion.
// Unchecked, it just drops the code into the product search box so
// the cashier can review suggestions before picking one.
(function() {
  const bEl      = document.getElementById('po-barcode-input');
  const feedback = document.getElementById('po-barcode-feedback');
  let bTimer     = null;

  if (!bEl) return;

  bEl.addEventListener('keydown', async e => {
    if (e.key !== 'Enter') return;
    e.preventDefault();
    const code = bEl.value.trim();
    if (!code) return;

    feedback.textContent = '🔍 Searching…';
    feedback.style.color = 'var(--text3)';

    const r = await apiPost('../api/products.php', {
      action: 'list', q: code
    });

    if (!r.success || !r.data.length) {
      feedback.textContent = '❌ Not found';
      feedback.style.color = 'var(--danger)';
      bEl.select();
      setTimeout(() => { feedback.textContent = ''; }, 2000);
      return;
    }

    // Find exact barcode match first, then fallback to first result
    const exact = r.data.find(p =>
      (p.barcode || '').toLowerCase() === code.toLowerCase()
    );
    const product = exact || r.data[0];

    const autoAdd = document.getElementById('po-barcode-auto').checked;
    if (autoAdd) {
      addItem(product);
      feedback.textContent = `✅ Added: ${product.name}`;
      feedback.style.color = 'var(--accent)';
    } else {
      searchInput.value = code;
      fetchSuggestions(code);
      feedback.textContent = '⬆ See suggestions';
      feedback.style.color = 'var(--brand)';
    }

    bEl.value = '';
    setTimeout(() => { feedback.textContent = ''; }, 2500);
    bEl.focus();
  });

  // Visual feedback while typing (scanner speed detection)
  bEl.addEventListener('input', () => {
    clearTimeout(bTimer);
    bTimer = setTimeout(() => {
      const v = bEl.value.trim();
      if (v.length > 3) {
        feedback.textContent = `${v.length} chars — press Enter`;
        feedback.style.color = 'var(--text3)';
      }
    }, 300);
  });
})();

// ── Keyboard shortcuts ────────────────────────────────────────
document.addEventListener('keydown', e => {
  if (e.key === 'F2') {
    e.preventDefault();
    document.getElementById('prod-search').focus();
    document.getElementById('prod-search').select();
  }
  if (e.key === 'F3') {
    e.preventDefault();
    const bEl = document.getElementById('po-barcode-input');
    bEl.focus(); bEl.select();
  }
  if (e.key === 'F9') {
    e.preventDefault();
    savePO();
  }
});

// ── Save Purchase Order ───────────────────────────────────────
async function savePO() {
  const errEl  = document.getElementById('save-err');
  const saveBtn = document.getElementById('save-btn');
  errEl.style.display = 'none';

  // ── Validation ──────────────────────────────────────────────
  const suppId = document.getElementById('supplier-id').value;
  if (!suppId) {
    showErr('⚠ Please select a supplier.'); return;
  }
  if (!items.length) {
    showErr('⚠ Add at least one product before saving.'); return;
  }

  // Check every item has at least 1 box and a price
  for (let i = 0; i < items.length; i++) {
    const it = items[i];
    if (!it.boxes || it.boxes < 1) {
      showErr(`⚠ Row ${i+1} (${it.product_name}): Boxes must be ≥ 1.`); return;
    }
    if (!it.btp_vat || it.btp_vat <= 0) {
      showErr(`⚠ Row ${i+1} (${it.product_name}): Enter a valid price.`); return;
    }
    if (!it.exp) {
      showErr(`⚠ Row ${i+1} (${it.product_name}): Expiry date is required.`); return;
    }
  }

  // ── Low-margin warning — surfaced once more right before save ────
  // (rows are already colour-coded red/orange/green in the grid; this
  // is the explicit "before purchase is saved" confirmation gate).
  const lowMargin = items.filter(it => profitPct(it) < 3);
  if (lowMargin.length) {
    const names = lowMargin
      .map(it => `• ${it.product_name} — ${profitPct(it).toFixed(2)}% profit`)
      .join('\n');
    if (!confirm(
      `⚠ Low margin warning!\n\n` +
      `${lowMargin.length} item(s) have less than 3% profit margin:\n\n${names}\n\n` +
      `Continue saving this purchase order anyway?`
    )) return;
  }

  // ── Collect totals ───────────────────────────────────────────
  let totalBtp = 0, totalVat = 0, totalBtpVat = 0;
  items.forEach(it => {
    totalBtp    += it.boxes * it.btp;
    totalVat    += it.boxes * it.vat;
    totalBtpVat += it.boxes * it.btp_vat;
  });

  // Whatever mode the user entered the discount in, convert it to an
  // equivalent percentage here — api/purchases.php only ever receives
  // `discount_pct`, so the flat-amount toggle needs no backend changes.
  const discInputVal = parseFloat(document.getElementById('discount').value) || 0;
  const discAmt  = poDiscMode === 'flat'
    ? Math.max(0, Math.min(discInputVal, totalBtpVat))
    : totalBtpVat * (Math.max(0, Math.min(100, discInputVal)) / 100);
  const discPct  = totalBtpVat > 0 ? (discAmt / totalBtpVat) * 100 : 0;
  const final    = totalBtpVat - discAmt;
  const paid     = parseFloat(document.getElementById('paid').value) || 0;
  const due      = Math.max(0, final - paid);
  const account  = document.getElementById('pay-account').value;

  if (paid > final + 0.001) {
    showErr(`⚠ Paid amount (${CUR}${paid.toFixed(2)}) exceeds final amount (${CUR}${final.toFixed(2)}).`);
    return;
  }

  // ── Build payload ────────────────────────────────────────────
  const payload = {
    action:       'save',
    invoice:      document.getElementById('po-number').value.trim(),
    supplier_id:  suppId,
    inv_no:       document.getElementById('sup-invoice').value.trim(),
    date:         document.getElementById('po-date').value,
    mode:         MODE,
    discount_pct: discPct,
    total_btp:    totalBtp,
    total_vat:    totalVat,
    final_price:  final,
    paid,
    due,
    account,
    notes:        document.getElementById('po-notes').value.trim(),
    items: items.map(it => ({
      product_id:   it.product_id,
      product_name: it.product_name,
      boxes:        it.boxes,
      qty_in_box:   it.qty_in_box,
      qty_pcs:      it.boxes * it.qty_in_box,
      btp:          it.btp,
      vat:          it.vat,
      btp_vat:      it.btp_vat,
      sale_price:   it.sale_price,
      free:         it.free,
      exp:          it.exp,
      total_price:  it.boxes * it.btp_vat,
    }))
  };

  // ── POST to API ──────────────────────────────────────────────
  saveBtn.disabled    = true;
  saveBtn.textContent = '⏳ Saving…';

  // Offline-first: queue locally when no connection
  if (window.MediOffline && !MediOffline.isOnline()) {
    saveBtn.disabled    = false;
    saveBtn.textContent = '💾 Save Purchase Order   (F9)';
    await MediOffline.queuePurchase(payload);
    discardPoDraft();
    resetForm();
    MediOffline.toast(`📴 Purchase saved offline (${payload.invoice}) — will sync when connected`, 'warn', 6000);
    return;
  }

  const r = await apiPost('../api/purchases.php', payload);

  saveBtn.disabled    = false;
  saveBtn.textContent = '💾 Save Purchase Order   (F9)';

  if (!r.success) {
    if (window.MediOffline && r.error && r.error.includes('Network error')) {
      await MediOffline.queuePurchase(payload);
      discardPoDraft();
      resetForm();
      MediOffline.toast(`📴 Purchase queued offline — will sync when connection returns`, 'warn', 6000);
      return;
    }
    showErr('⚠ ' + (r.error || 'Save failed. Please try again.'));
    return;
  }

  // ── Success ──────────────────────────────────────────────────
  discardPoDraft();
  showSuccess(r.invoice, final, paid, due);
}

// ── Success modal ─────────────────────────────────────────────
function showSuccess(invoice, final, paid, due) {
  showModal(`
  <div style="text-align:center;padding:10px 0 6px">
    <div style="font-size:36px;margin-bottom:8px">✅</div>
    <div style="font-size:16px;font-weight:700;color:var(--accent);margin-bottom:4px">
      Purchase Order Saved!
    </div>
    <div class="mono" style="font-size:18px;font-weight:800;color:var(--brand);
                              margin-bottom:16px">${esc(invoice)}</div>

    <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:8px;
                margin-bottom:20px;text-align:left">
      <div style="background:var(--surface2);padding:10px;border-radius:var(--rsm)">
        <div style="font-size:10px;color:var(--text3)">Final Amount</div>
        <div class="mono" style="font-weight:700;font-size:14px">
          ${CUR}${final.toFixed(2)}
        </div>
      </div>
      <div style="background:var(--accent-xlight);padding:10px;border-radius:var(--rsm)">
        <div style="font-size:10px;color:var(--accent)">Paid</div>
        <div class="mono" style="font-weight:700;font-size:14px;color:var(--accent)">
          ${CUR}${paid.toFixed(2)}
        </div>
      </div>
      <div style="background:${due>0?'#fde8e8':'var(--accent-xlight)'};
                  padding:10px;border-radius:var(--rsm)">
        <div style="font-size:10px;color:${due>0?'var(--danger)':'var(--accent)'}">Due</div>
        <div class="mono" style="font-weight:700;font-size:14px;
                                  color:${due>0?'var(--danger)':'var(--accent)'}">
          ${CUR}${due.toFixed(2)}
        </div>
      </div>
    </div>

    <div style="display:flex;gap:8px;justify-content:center">
      <button class="btn btn-primary" onclick="closeModal();resetForm()">
        ➕ New Purchase
      </button>
      <a class="btn" href="list.php">📋 View All POs</a>
    </div>
  </div>`, 'Purchase Saved');
}

// ── Reset form after save ─────────────────────────────────────
async function resetForm() {
  items = [];
  renderItems();
  recalcTotals();
  document.getElementById('supplier-id').value  = '';
  document.getElementById('sup-info').style.display = 'none';
  document.getElementById('sup-invoice').value  = '';
  setPoDiscMode('pct');
  document.getElementById('discount').value     = '0';
  document.getElementById('paid').value         = '0';
  document.getElementById('po-notes').value     = '';
  document.getElementById('po-date').value      =
    new Date().toISOString().split('T')[0];

  // Fetch next PO number from server
  const r = await apiPost('../api/purchases.php', { action: 'next_ref' });
  if (r.success)
    document.getElementById('po-number').value = r.ref;

  document.getElementById('prod-search').focus();
}

// ── Error helper ──────────────────────────────────────────────
function showErr(msg) {
  const el = document.getElementById('save-err');
  el.textContent     = msg;
  el.style.display   = 'block';
  el.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
  setTimeout(() => el.style.display = 'none', 5000);
}

// ── Hold & Resume (Purchase) ──────────────────────────────────
const HOLD_API_PO   = '../api/holds.php';
const PO_DRAFT_KEY  = 'medicore_po_draft';
const PO_DRAFT_TS   = 'medicore_po_draft_ts';

function collectPoState() {
  const suppSel = document.getElementById('supplier-id');
  const suppOpt = suppSel?.options[suppSel.selectedIndex];
  return {
    supplier_id:   suppSel?.value || '',
    supplier_name: suppOpt?.text  || '',
    pur_mode:      MODE || 'unit_trvat',
    notes:         document.getElementById('po-notes')?.value || '',
    items:         items,
    total_amount:  parseFloat(
                     (document.getElementById('tot-final')?.textContent || '0')
                     .replace(/[^\d.]/g, '')
                   ) || 0,
  };
}

async function holdPurchase() {
  if (!items.length) { showErr('⚠ Add at least one item before holding.'); return; }
  const state = collectPoState();

  if (window.MediOffline && !MediOffline.isOnline()) {
    await MediOffline.queueHold(state, 'purchase');
    discardPoDraft();
    resetForm();
    MediOffline.toast('🔒 Purchase held offline — will sync when connected', 'warn', 4000);
    return;
  }

  const r = await apiPost(HOLD_API_PO, { action: 'save_purchase', ...state });
  if (!r.success) { showErr('⚠ Hold failed: ' + r.error); return; }
  discardPoDraft();
  resetForm();
  const t = document.createElement('div');
  t.style.cssText = 'position:fixed;top:56px;left:50%;transform:translateX(-50%);z-index:9999;' +
    'background:#fff3dc;border:2px solid #f59e0b;border-radius:var(--r);padding:10px 18px;' +
    'font-size:13px;font-weight:700;color:#78350f;box-shadow:var(--shmd)';
  t.textContent = '🔒 Purchase held — ' + r.hold_ref;
  document.body.appendChild(t);
  setTimeout(() => t.remove(), 3500);
}

async function showPurchaseHolds(q = '') {
  const r = await apiPost(HOLD_API_PO, { action: 'list_purchases', q });
  const rows = r.success ? r.data : [];
  const isAdminOrMgr = <?= (is_admin() || (current_user_role() === 'manager')) ? 'true' : 'false' ?>;

  showModal(`
    <div style="margin-bottom:10px;display:flex;gap:7px;align-items:center">
      <input placeholder="🔍 Search…" oninput="showPurchaseHolds(this.value)"
             style="flex:1;padding:6px 10px;border:1px solid var(--border);
                    border-radius:var(--rsm);font-size:13px;font-family:inherit">
      <span style="font-size:12px;color:var(--text3)">${rows.length} hold(s)</span>
    </div>
    ${!rows.length ? '<div style="text-align:center;padding:20px;color:var(--text3)">No held purchases found.</div>' :
      '<div style="display:flex;flex-direction:column;gap:6px;max-height:380px;overflow-y:auto">' +
      rows.map(h => {
        const ts = new Date(h.created_at).toLocaleString();
        return `<div style="border:1px solid var(--border);border-radius:var(--rsm);
                             padding:9px 12px;background:var(--surface)">
          <div style="display:flex;align-items:center;gap:8px;flex-wrap:wrap">
            <span style="font-size:11px;font-family:monospace;font-weight:700;
                         background:#fff3dc;padding:2px 7px;border-radius:5px;
                         color:#78350f">${esc(h.hold_ref)}</span>
            ${h.is_auto == 1 ? `<span style="font-size:10px;font-weight:700;
                         background:#fde8e8;color:var(--danger);padding:2px 7px;
                         border-radius:5px" title="Automatically saved when the page was
                         closed/reloaded mid-purchase, not a deliberate Hold">⚡ Auto-saved</span>` : ''}
            <span style="font-weight:700;font-size:13px">${esc(h.supplier_name || 'No supplier')}</span>
            <span style="font-size:11px;color:var(--text3)">${h.item_count} item(s)</span>
            <span class="mono" style="font-weight:700;color:var(--brand)">
              ৳${parseFloat(h.total_amount).toFixed(2)}
            </span>
            <span style="margin-left:auto;font-size:11px;color:var(--text3)">${ts}</span>
          </div>
          <div style="font-size:11px;color:var(--text3);margin-top:4px">by ${esc(h.user_name)}</div>
          <div style="display:flex;gap:6px;margin-top:8px">
            <button class="btn btn-sm"
                    onclick="viewPurchaseHold(${h.id})">
              👁 View
            </button>
            <button class="btn btn-primary btn-sm"
                    onclick="closeModal();resumePurchaseHold(${h.id})">
              ▶ Resume
            </button>
            ${isAdminOrMgr ? `<button class="btn btn-sm btn-danger"
                    onclick="deletePurchaseHold(${h.id},this)">
              🗑 Delete
            </button>` : ''}
          </div>
        </div>`;
      }).join('') + '</div>'
    }
  `, '🔒 Held Purchases');
}

// Read-only preview — uses action:'view_purchase' (does NOT mark the hold
// 'resumed' the way action:'get_purchase' does), so accidentally opening a
// preview never burns the hold. Offers Resume right from the preview too.
async function viewPurchaseHold(id) {
  const r = await apiPost(HOLD_API_PO, { action: 'view_purchase', id });
  if (!r.success) { alert('⚠ ' + r.error); return; }
  const d = r.data;
  const itemsHtml = (d.items || []).map(it => `
    <tr>
      <td style="padding:6px 8px;font-size:12.5px">${esc(it.product_name || '')}</td>
      <td style="padding:6px 8px;text-align:center;font-size:12.5px">${it.boxes} × ${it.qty_in_box}</td>
      <td style="padding:6px 8px;text-align:right;font-size:12.5px" class="mono">৳${parseFloat(it.btp_vat || 0).toFixed(2)}</td>
      <td style="padding:6px 8px;text-align:right;font-size:12.5px;font-weight:700" class="mono">৳${parseFloat(it.total_price || 0).toFixed(2)}</td>
    </tr>`).join('');

  showModal(`
    <div style="margin-bottom:8px;font-size:12.5px;color:var(--text2)">
      <strong>${esc(d.hold_ref)}</strong> — ${esc(d.supplier_name || 'No supplier')}
      ${d.is_auto == 1 ? ' · <span style="color:var(--danger);font-weight:700">⚡ Auto-saved</span>' : ''}
    </div>
    <div style="max-height:360px;overflow-y:auto;border:1px solid var(--border);border-radius:var(--rsm)">
      <table style="width:100%;border-collapse:collapse">
        <thead>
          <tr style="background:var(--surface2)">
            <th style="padding:6px 8px;text-align:left;font-size:11px">Product</th>
            <th style="padding:6px 8px;text-align:center;font-size:11px">Box × Qty</th>
            <th style="padding:6px 8px;text-align:right;font-size:11px">Rate</th>
            <th style="padding:6px 8px;text-align:right;font-size:11px">Total</th>
          </tr>
        </thead>
        <tbody>${itemsHtml || '<tr><td colspan="4" style="padding:14px;text-align:center;color:var(--text3)">No items.</td></tr>'}</tbody>
      </table>
    </div>
    <div style="display:flex;justify-content:space-between;align-items:center;margin-top:10px">
      <span class="mono" style="font-weight:800;font-size:15px;color:var(--brand)">
        ৳${parseFloat(d.total_amount || 0).toFixed(2)}
      </span>
      <button class="btn btn-primary btn-sm" onclick="closeModal();resumePurchaseHold(${d.id})">▶ Resume This</button>
    </div>
  `, '👁 Preview Held Purchase', 'lg');
}

async function resumePurchaseHold(id) {
  const r = await apiPost(HOLD_API_PO, { action: 'get_purchase', id });
  if (!r.success) { alert('⚠ ' + r.error); return; }
  const d = r.data;

  await resetForm();
  items = d.items || [];
  if (d.pur_mode) setMode(d.pur_mode);
  if (d.supplier_id) {
    const suppSel = document.getElementById('supplier-id');
    if (suppSel) { suppSel.value = d.supplier_id; suppSel.dispatchEvent(new Event('change')); }
  }
  document.getElementById('po-notes').value = d.notes || '';
  renderItems();
  recalcTotals();

  const t = document.createElement('div');
  t.style.cssText = 'position:fixed;top:56px;left:50%;transform:translateX(-50%);z-index:9999;' +
    'background:#d4f0e6;border:2px solid var(--accent);border-radius:var(--r);padding:10px 18px;' +
    'font-size:13px;font-weight:700;color:#065f46;box-shadow:var(--shmd)';
  t.textContent = '▶ Resumed: ' + d.hold_ref;
  document.body.appendChild(t);
  setTimeout(() => t.remove(), 3000);
}

async function deletePurchaseHold(id, btn) {
  if (!confirm('Delete this held purchase?')) return;
  const r = await apiPost(HOLD_API_PO, { action: 'delete', id, type: 'purchase' });
  if (!r.success) { alert('⚠ ' + r.error); return; }
  btn.closest('div[style*="border"]').remove();
}

// ── Auto-draft (crash recovery) ───────────────────────────────
function savePoDraft() {
  if (!items.length) return;
  localStorage.setItem(PO_DRAFT_KEY, JSON.stringify(collectPoState()));
  localStorage.setItem(PO_DRAFT_TS, Date.now());
}
function discardPoDraft() {
  localStorage.removeItem(PO_DRAFT_KEY);
  localStorage.removeItem(PO_DRAFT_TS);
  document.getElementById('po-draft-banner').style.display = 'none';
}
async function restorePoDraft() {
  try {
    const state = JSON.parse(localStorage.getItem(PO_DRAFT_KEY));
    if (!state) return;
    document.getElementById('po-draft-banner').style.display = 'none';
    await resetForm();
    items = state.items || [];
    if (state.pur_mode) setMode(state.pur_mode);
    if (state.supplier_id) {
      const suppSel = document.getElementById('supplier-id');
      if (suppSel) { suppSel.value = state.supplier_id; suppSel.dispatchEvent(new Event('change')); }
    }
    document.getElementById('po-notes').value = state.notes || '';
    renderItems(); recalcTotals();
    discardPoDraft();
  } catch(e) { discardPoDraft(); }
}

function esc(s) {
  return String(s||'').replace(/&/g,'&amp;').replace(/</g,'&lt;')
                      .replace(/>/g,'&gt;').replace(/"/g,'&quot;');
}

// ── Auto-hold on accidental reload/close ───────────────────────
// fetch() can be cancelled mid-flight when the page is navigating away,
// so we use sendBeacon (fire-and-forget, survives page unload) to push
// the in-progress purchase into the Hold/Resume list as an auto-saved hold.
window.addEventListener('pagehide', () => {
  try {
    if (!items.length) return;
    const state = collectPoState();
    const payload = Object.assign({ action: 'save_purchase', auto: 1 }, state);
    const blob = new Blob([JSON.stringify(payload)], { type: 'application/json' });
    navigator.sendBeacon(HOLD_API_PO, blob);
  } catch (e) { /* best-effort */ }
});

// ── Init ──────────────────────────────────────────────────────
document.addEventListener('DOMContentLoaded', () => {
  <?php if ($fromReq): ?>
  // Pre-fill from requisition
  setMode('<?= $fromReq['mode'] ?? 'unit_trvat' ?>');
  // Pre-select supplier
  const suppSel = document.getElementById('supplier-id');
  if (suppSel && '<?= $fromReq['supplier_id'] ?>') {
    suppSel.value = '<?= $fromReq['supplier_id'] ?>';
    onSupplierChange();
  }
  // Pre-fill items
  const reqItems = <?= json_encode($fromReq['items'] ?? []) ?>;
  reqItems.forEach(p => {
    // Map to product format expected by addItem
    addItem({
      id:          p.product_id,
      name:        p.product_name,
      strength:    '',
      form:        '',
      sale_price:  p.sale_price || 0,
      btp:         p.btp        || 0,
      vat:         p.vat        || 0,
      btp_vat:     p.btp_vat    || 0,
      qty_in_box:  p.qty_in_box || 1,
      stock_pcs:   999,   // allow any qty on PO
      company_name:'',
      barcode:     '',
      rack:        '',
      wholesale_price:    0,
      wholesale_disc_pct: 0,
    });
    // Set correct boxes qty
    const lastIdx = items.length - 1;
    if (lastIdx >= 0 && p.boxes > 1) {
      items[lastIdx].boxes = p.boxes;
    }
  });
  renderItems();
  recalcTotals();
  // Show info banner
  const banner = document.createElement('div');
  banner.className = 'al al-i';
  banner.innerHTML = '📝 Pre-filled from Requisition: <strong><?= htmlspecialchars($fromReq['req_ref'] ?? '') ?></strong>';
  banner.style.cssText = 'margin-bottom:10px';
  document.querySelector('.content')?.prepend(banner);
  <?php endif; ?>
  setMode('<?= $fromReq ? ($fromReq['mode'] ?? 'unit_trvat') : 'unit_trvat' ?>');

  // Draft recovery check
  const poTs  = parseInt(localStorage.getItem(PO_DRAFT_TS) || '0');
  if (localStorage.getItem(PO_DRAFT_KEY) && (Date.now() - poTs) < 3600000) {
    document.getElementById('po-draft-banner').style.display = 'block';
  }

  // Auto-draft every 30 seconds
  setInterval(savePoDraft, 15000);

  // Keyboard shortcuts
  document.addEventListener('keydown', e => {
    if (e.ctrlKey && e.key === 'h') { e.preventDefault(); holdPurchase(); }
    if (e.ctrlKey && e.key === 'r') { e.preventDefault(); showPurchaseHolds(); }
  });
});
</script>

<?php require_once '../includes/footer.php'; ?>
