<?php
// ============================================================
//  MediCore — Product List
//  File: product/list.php
// ============================================================
$page  = 'product/list';
$title = 'Product List';
require_once '../includes/header.php';

$cfg = $settings;
$cur = $cfg['currency'];

ensure_products_supplier_column();

// Load companies, generics, categories & suppliers for the modal dropdowns / filters
$companies  = db_rows("SELECT id, name FROM companies  ORDER BY name");
$generics   = db_rows("SELECT id, name FROM generics   ORDER BY name");
$categories = db_rows("SELECT id, name FROM categories ORDER BY name");
$suppliersL = db_rows("SELECT id, name FROM suppliers  ORDER BY name");
$forms      = db_rows("SELECT DISTINCT form FROM products WHERE form IS NOT NULL AND form != '' ORDER BY form");

// Summary counts for top cards
$total    = (int) db_val("SELECT COUNT(*)             FROM products WHERE is_active=1");
$lowStock = (int) db_val("SELECT COUNT(*)             FROM products WHERE is_active=1 AND stock_pcs > 0 AND stock_pcs < qty_in_box*2");
$outStock = (int) db_val("SELECT COUNT(*)             FROM products WHERE is_active=1 AND stock_pcs <= 0");
$inStock  = $total - $lowStock - $outStock;

// Product History / Stock Modification permissions — gate both the
// row action buttons and the tab itself. Read-only History has just
// one permission; Stock Modification splits view-the-log from
// actually-create-an-adjustment, same view/create split used by
// every other module in the app's permission system.
$canViewHistory   = can('product_history', 'view');
$canViewStockMod  = can('stock_modification', 'view');
$canCreateStockMod = can('stock_modification', 'create');
?>

<!-- ── Page action buttons ─────────────────────────── -->
<script>
document.getElementById('ph-actions').innerHTML = `
  <input type="text" id="srch" placeholder="🔍  Search name, generic, company, barcode…"
    style="padding:5px 10px;border:1px solid var(--border);border-radius:var(--rsm);
           font-family:inherit;font-size:12px;width:230px;outline:none">
  <button class="btn btn-primary btn-sm" onclick="openAdd()">➕ Add Product</button>`;
</script>

<?php if ($canViewStockMod): ?>
<!-- ── Tabs ───────────────────────────────────────── -->
<div style="display:flex;gap:4px;margin-bottom:10px;border-bottom:2px solid var(--border)">
  <button type="button" id="tabbtn-products" class="pl-tab-btn active" onclick="switchTab('products')">📋 Products</button>
  <button type="button" id="tabbtn-stockmod" class="pl-tab-btn" onclick="switchTab('stockmod')">🛠️ Stock Modification</button>
</div>
<style>
  .pl-tab-btn{padding:9px 16px;border:none;background:transparent;font-size:13px;font-weight:600;
    color:var(--text2);cursor:pointer;border-bottom:2px solid transparent;margin-bottom:-2px;transition:color .12s,border-color .12s}
  .pl-tab-btn:hover{color:var(--brand)}
  .pl-tab-btn.active{color:var(--brand);border-bottom-color:var(--brand)}
</style>
<?php endif; ?>

<div id="tab-products">

<!-- ── Filter bar ─────────────────────────────────── -->
<div class="card" style="padding:10px 12px;margin-bottom:10px;display:flex;flex-wrap:wrap;gap:8px;align-items:center">
  <span style="font-size:11px;color:var(--text3);font-weight:600">FILTERS:</span>
  <select id="flt-category" onchange="applyFilters()"
          style="padding:5px 8px;border:1px solid var(--border);border-radius:var(--rsm);font-family:inherit;font-size:12px;outline:none">
    <option value="">All Categories</option>
    <?php foreach ($categories as $c): ?>
      <option value="<?= (int)$c['id'] ?>"><?= htmlspecialchars($c['name']) ?></option>
    <?php endforeach; ?>
  </select>
  <select id="flt-generic" onchange="applyFilters()"
          style="padding:5px 8px;border:1px solid var(--border);border-radius:var(--rsm);font-family:inherit;font-size:12px;outline:none">
    <option value="">All Generics</option>
    <?php foreach ($generics as $g): ?>
      <option value="<?= (int)$g['id'] ?>"><?= htmlspecialchars($g['name']) ?></option>
    <?php endforeach; ?>
  </select>
  <select id="flt-company" onchange="applyFilters()"
          style="padding:5px 8px;border:1px solid var(--border);border-radius:var(--rsm);font-family:inherit;font-size:12px;outline:none">
    <option value="">All Companies</option>
    <?php foreach ($companies as $co): ?>
      <option value="<?= (int)$co['id'] ?>"><?= htmlspecialchars($co['name']) ?></option>
    <?php endforeach; ?>
  </select>
  <select id="flt-form" onchange="applyFilters()"
          style="padding:5px 8px;border:1px solid var(--border);border-radius:var(--rsm);font-family:inherit;font-size:12px;outline:none">
    <option value="">All Forms</option>
    <?php foreach ($forms as $f): ?>
      <option value="<?= htmlspecialchars($f['form']) ?>"><?= htmlspecialchars($f['form']) ?></option>
    <?php endforeach; ?>
  </select>
  <select id="flt-stock" onchange="applyFilters()"
          style="padding:5px 8px;border:1px solid var(--border);border-radius:var(--rsm);font-family:inherit;font-size:12px;outline:none">
    <option value="">All Stock Levels</option>
    <option value="in">In Stock</option>
    <option value="low">Low Stock</option>
    <option value="out">Out of Stock</option>
  </select>
  <button class="btn btn-sm" onclick="clearFilters()">✕ Clear Filters</button>
</div>

<!-- ── Summary cards ──────────────────────────────── -->
<div style="display:grid;grid-template-columns:repeat(4,1fr);gap:10px;margin-bottom:12px">
  <div class="sc" style="border-top:3px solid var(--brand)">
    <div class="slabel">Total Products</div>
    <div class="sval b" id="cnt-total"><?= $total ?></div>
  </div>
  <div class="sc" style="border-top:3px solid var(--accent)">
    <div class="slabel">In Stock</div>
    <div class="sval b" style="color:var(--accent)" id="cnt-in"><?= $inStock ?></div>
  </div>
  <div class="sc" style="border-top:3px solid var(--warn)">
    <div class="slabel">Low Stock</div>
    <div class="sval b" style="color:var(--warn)" id="cnt-low"><?= $lowStock ?></div>
  </div>
  <div class="sc" style="border-top:3px solid var(--danger)">
    <div class="slabel">Out of Stock</div>
    <div class="sval b" style="color:var(--danger)" id="cnt-out"><?= $outStock ?></div>
  </div>
</div>

<!-- ── Legend ─────────────────────────────────────── -->
<div style="display:flex;gap:14px;margin-bottom:8px;font-size:11px;color:var(--text2)">
  <span><span style="display:inline-block;width:10px;height:10px;background:#f5fff9;border:1px solid #1a9e6e;border-radius:2px;margin-right:4px"></span>In Stock</span>
  <span><span style="display:inline-block;width:10px;height:10px;background:#fff9f0;border:1px solid var(--warn);border-radius:2px;margin-right:4px"></span>Low Stock</span>
  <span><span style="display:inline-block;width:10px;height:10px;background:#fff5f5;border:1px solid var(--danger);border-radius:2px;margin-right:4px"></span>Out of Stock</span>
</div>

<!-- ── Products Table ─────────────────────────────── -->
<div class="card" style="padding:0">
  <div class="tw">
    <table id="prod-table">
      <thead>
        <tr>
          <th>#</th>
          <th>Name / Strength</th>
          <th>Form</th>
          <th>Generic</th>
          <th>Company</th>
          <th>Category</th>
          <th>Rack</th>
          <th>Barcode</th>
          <th style="text-align:right">MRP/pc</th>
          <th style="text-align:center">Qty/Box</th>
          <th style="text-align:right">Stock (pcs)</th>
          <th style="text-align:center">Actions</th>
        </tr>
      </thead>
      <tbody id="prod-body">
        <tr><td colspan="12" class="empty-state">Loading…</td></tr>
      </tbody>
    </table>
  </div>
  <div id="prod-footer"
       style="padding:8px 14px;font-size:11px;color:var(--text3);border-top:1px solid var(--border)">
  </div>
</div>

</div><!-- /tab-products -->

<?php if ($canViewStockMod): ?>
<div id="tab-stockmod" style="display:none">

  <!-- ── Stock Modification filter bar ─────────────── -->
  <div class="card" style="padding:10px 12px;margin-bottom:10px;display:flex;flex-wrap:wrap;gap:8px;align-items:center">
    <span style="font-size:11px;color:var(--text3);font-weight:600">FILTERS:</span>
    <input type="date" id="sm-flt-from" onchange="loadStockModLog(1)"
           style="padding:5px 8px;border:1px solid var(--border);border-radius:var(--rsm);font-family:inherit;font-size:12px;outline:none">
    <span style="color:var(--text3);font-size:11px">to</span>
    <input type="date" id="sm-flt-to" onchange="loadStockModLog(1)"
           style="padding:5px 8px;border:1px solid var(--border);border-radius:var(--rsm);font-family:inherit;font-size:12px;outline:none">
    <div style="position:relative">
      <input type="text" id="sm-flt-product-name" placeholder="🔍 Filter by product…" autocomplete="off"
             oninput="smProductFilterInput(this.value)"
             style="padding:5px 8px;border:1px solid var(--border);border-radius:var(--rsm);font-family:inherit;font-size:12px;outline:none;width:200px">
      <input type="hidden" id="sm-flt-product-id" value="">
      <div id="sm-product-suggest" style="display:none;position:absolute;top:calc(100% + 3px);left:0;width:280px;
           max-height:260px;overflow-y:auto;background:var(--surface);border:1px solid var(--border);
           border-radius:var(--rsm);box-shadow:var(--shmd);z-index:20"></div>
    </div>
    <select id="sm-flt-user" onchange="loadStockModLog(1)"
            style="padding:5px 8px;border:1px solid var(--border);border-radius:var(--rsm);font-family:inherit;font-size:12px;outline:none">
      <option value="">All Users</option>
    </select>
    <select id="sm-flt-type" onchange="loadStockModLog(1)"
            style="padding:5px 8px;border:1px solid var(--border);border-radius:var(--rsm);font-family:inherit;font-size:12px;outline:none">
      <option value="">All Types</option>
      <option value="increase">Increase</option>
      <option value="decrease">Decrease</option>
    </select>
    <button class="btn btn-sm" onclick="clearStockModFilters()">✕ Clear Filters</button>
  </div>

  <!-- ── Stock Modification Log ────────────────────── -->
  <div class="card" style="padding:0">
    <div style="padding:10px 14px;display:flex;flex-wrap:wrap;gap:8px;align-items:center;border-bottom:1px solid var(--border)">
      <input type="text" id="sm-search" placeholder="🔍 Search reason, remarks, product…"
             oninput="smSearchInput(this.value)"
             style="padding:5px 10px;border:1px solid var(--border);border-radius:var(--rsm);
                    font-family:inherit;font-size:12px;width:240px;outline:none">
      <div style="margin-left:auto;display:flex;gap:6px">
        <button class="btn btn-sm" onclick="printStockModLog()">🖨 Print</button>
        <button class="btn btn-sm" onclick="exportStockModExcel()">📊 Export Excel</button>
        <button class="btn btn-sm" onclick="exportStockModPDF()">📄 Export PDF</button>
      </div>
    </div>
    <div class="tw">
      <table id="sm-table">
        <thead>
          <tr>
            <th>Date &amp; Time</th>
            <th>Product</th>
            <th>Type</th>
            <th style="text-align:right">Quantity</th>
            <th style="text-align:right">Stock Before</th>
            <th style="text-align:right">Stock After</th>
            <th>Reason</th>
            <th>Remarks</th>
            <th>User</th>
          </tr>
        </thead>
        <tbody id="sm-body">
          <tr><td colspan="9" class="empty-state">Loading…</td></tr>
        </tbody>
      </table>
    </div>
    <div id="sm-footer" style="padding:8px 14px;font-size:11px;color:var(--text3);border-top:1px solid var(--border)"></div>
  </div>
</div>
<?php endif; ?>

<!-- ── Add / Edit Modal ───────────────────────────── -->
<div id="mroot"></div>

<script>
// ── Data ──────────────────────────────────────────────────────
const COMPANIES  = <?= json_encode($companies)  ?>;
const GENERICS   = <?= json_encode($generics)   ?>;
const CATEGORIES = <?= json_encode($categories) ?>;
const SUPPLIERS  = <?= json_encode($suppliersL) ?>;
const CUR       = '<?= addslashes($cur) ?>';
const API       = '../api/products.php';
const HISTORY_API = '../api/product-history.php';
const STOCKMOD_API = '../api/stock-modifications.php';
const CAN_VIEW_HISTORY    = <?= $canViewHistory   ? 'true' : 'false' ?>;
const CAN_VIEW_STOCKMOD   = <?= $canViewStockMod  ? 'true' : 'false' ?>;
const CAN_CREATE_STOCKMOD = <?= $canCreateStockMod ? 'true' : 'false' ?>;
const CURRENT_USER_NAME   = <?= json_encode(current_user_name()) ?>;
// Note: Trade Price/VAT/Buying Price fields in the Add/Edit form are
// intentionally NOT gated by the "Profit Visibility" permission —
// anyone allowed to add/edit products needs to enter real cost data
// to create a correct catalog entry. Profit Visibility still gates
// *computed margin* elsewhere (Sale Entry's Buy%/Profit% columns,
// Dashboard, Sale Record, Profit Report) — a separate concern from
// "can this person record what they paid for stock."

let allProducts = [];   // local cache of the CURRENT PAGE's rows only
let searchTimer = null;
let curPage     = 1;
let curQuery    = '';
let curPageSize = 50;

// ── Filters ──────────────────────────────────────────────────────
function applyFilters() { loadProducts(curQuery, 1); }
function clearFilters() {
  document.getElementById('flt-category').value = '';
  document.getElementById('flt-generic').value   = '';
  document.getElementById('flt-company').value   = '';
  document.getElementById('flt-form').value      = '';
  document.getElementById('flt-stock').value     = '';
  loadProducts(curQuery, 1);
}

// ── Load products (paginated) ───────────────────────────────────
// The catalog can run into the tens of thousands of rows — loading
// and rendering everything in one shot used to hang the page
// indefinitely. We now fetch one page at a time from the server.
async function loadProducts(q = '', page = 1) {
  curQuery = q;
  curPage  = page;

  const tbody = document.getElementById('prod-body');
  tbody.innerHTML = '<tr><td colspan="12" class="empty-state">Loading…</td></tr>';

  const r = await apiPost(API, {
    action: 'list', q, page, page_size: curPageSize,
    category_id:  document.getElementById('flt-category')?.value || '',
    generic_id:   document.getElementById('flt-generic')?.value  || '',
    company_id:   document.getElementById('flt-company')?.value  || '',
    form:         document.getElementById('flt-form')?.value     || '',
    stock_status: document.getElementById('flt-stock')?.value    || '',
  });
  if (!r.success) {
    tbody.innerHTML = `<tr><td colspan="12" class="empty-state" style="color:var(--danger)">
      Error: ${r.error}</td></tr>`;
    return;
  }

  allProducts = r.data;
  renderTable(r.data, r);
}

// Shared by renderTable() (initial render) and the medi:stock-sync
// listener below (live in-place patch) so both always agree on exactly
// what counts as "low"/"out" — extracted once instead of duplicated so
// the two can't quietly drift out of sync with each other.
function rowClassFor(p) {
  const stock  = parseInt(p.stock_pcs);
  const minQty = parseInt(p.qty_in_box) * 2;
  if (stock <= 0)          return 'row-out';
  if (stock < minQty)      return 'row-low';
  return 'row-good';
}
function stockBadgeHtml(p) {
  const stock    = parseInt(p.stock_pcs);
  const alertQty = parseInt(p.low_stock_alert) || (parseInt(p.qty_in_box) * 2);
  if (stock <= 0) return `<span class="badge" style="background:#fde8e8;color:var(--danger)">Out</span>`;
  if (stock < alertQty) return `<span class="badge" style="background:#fff3dc;color:var(--warn)">${stock} ⚠</span>`;
  return `<span class="badge" style="background:#d4f0e6;color:var(--accent)">${stock}</span>`;
}

function renderTable(rows, meta = {}) {
  const tbody  = document.getElementById('prod-body');
  const footer = document.getElementById('prod-footer');

  if (!rows.length) {
    tbody.innerHTML = '<tr><td colspan="12" class="empty-state">No products found.</td></tr>';
    footer.innerHTML = '';
    return;
  }

  const startIdx = ((meta.page || 1) - 1) * (meta.page_size || curPageSize);

  tbody.innerHTML = rows.map((p, i) => {
    return `<tr class="${rowClassFor(p)}" data-id="${p.id}">
      <td style="color:var(--text3)">${startIdx + i + 1}</td>
      <td>
        <div style="display:flex;align-items:center;gap:8px">
          ${p.image
            ? `<img src="${p.image}" alt="" class="prod-thumb">`
            : `<div class="prod-thumb prod-thumb-ph">${formIcon(p.form) || '📦'}</div>`}
          <div>
            <div style="font-weight:600;color:var(--text)">${formIconName(p.form, p.name)}</div>
            ${p.strength ? `<div style="font-size:10px;color:var(--text2)">${esc(p.strength)}</div>` : ''}
          </div>
        </div>
      </td>
      <td><span class="badge" style="background:var(--brand-xlight);color:var(--brand)">${formIcon(p.form)?formIcon(p.form)+' ':''}${esc(p.form||'—')}</span></td>
      <td style="color:var(--text2)">${esc(p.generic_name||'—')}</td>
      <td style="color:var(--text2)">${esc(p.company_name||'—')}</td>
      <td>${p.category_name ? `<span class="badge" style="background:var(--teal-light);color:var(--teal)">${esc(p.category_name)}</span>` : '<span style="color:var(--text3)">—</span>'}</td>
      <td class="mono" style="font-size:11px">${esc(p.rack||'—')}</td>
      <td class="mono" style="font-size:10.5px;color:var(--text3)">${esc(p.barcode||'—')}</td>
      <td class="mono cell-price" style="text-align:right;font-weight:700">${CUR}${parseFloat(p.sale_price||0).toFixed(2)}</td>
      <td style="text-align:center">${p.qty_in_box}</td>
      <td class="cell-stock" style="text-align:right">${stockBadgeHtml(p)}</td>
      <td style="text-align:center;white-space:nowrap">
        <button class="btn btn-sm btn-teal"   onclick="openEdit(${p.id})">✏ Edit</button>
        ${CAN_VIEW_HISTORY ? `<button class="btn btn-sm" onclick="openHistory(${p.id})" title="Purchase/sale summary + full stock movement timeline">🕒 History</button>` : ''}
        ${CAN_CREATE_STOCKMOD ? `<button class="btn btn-sm" onclick="openStockMod(${p.id},'${esc(p.name).replace(/'/g,"\\'")}',${p.stock_pcs})" title="Manually increase or decrease stock with a mandatory reason">🛠️ Stock Mod</button>` : ''}
        <button class="btn btn-sm btn-danger"  onclick="delProduct(${p.id},'${esc(p.name).replace(/'/g,"\\'")}')">🗑</button>
      </td>
    </tr>`;
  }).join('');

  const total      = meta.total ?? rows.length;
  const totalPages = meta.total_pages ?? 1;
  const page       = meta.page ?? 1;
  const rangeStart = startIdx + 1;
  const rangeEnd   = startIdx + rows.length;

  footer.innerHTML = `
    <div style="display:flex;align-items:center;justify-content:space-between;gap:10px">
      <span>Showing ${rangeStart}–${rangeEnd} of ${total} product(s)</span>
      <div style="display:flex;align-items:center;gap:10px">
        <div style="display:flex;align-items:center;gap:5px">
          <span>Show</span>
          <select onchange="changePageSize(this.value)"
                  style="padding:3px 6px;border:1px solid var(--border);border-radius:var(--rsm);
                         font-family:inherit;font-size:11px;outline:none">
            ${[10,20,50,100,200].map(n =>
              `<option value="${n}" ${n===curPageSize?'selected':''}>${n}</option>`).join('')}
          </select>
        </div>
        <div style="display:flex;align-items:center;gap:6px">
          <button class="btn btn-sm" ${page<=1?'disabled':''} onclick="loadProducts(curQuery, 1)">« First</button>
          <button class="btn btn-sm" ${page<=1?'disabled':''} onclick="loadProducts(curQuery, curPage-1)">‹ Prev</button>
          <span style="padding:0 6px">Page ${page} of ${totalPages}</span>
          <button class="btn btn-sm" ${page>=totalPages?'disabled':''} onclick="loadProducts(curQuery, curPage+1)">Next ›</button>
          <button class="btn btn-sm" ${page>=totalPages?'disabled':''} onclick="loadProducts(curQuery, totalPages)">Last »</button>
        </div>
      </div>
    </div>`;
}

function changePageSize(size) {
  curPageSize = parseInt(size) || 50;
  loadProducts(curQuery, 1);
}

// ── Live search ───────────────────────────────────────────────
// Deep-link support — dashboard.php's low-stock/out-of-stock KPI boxes
// link here as product/list.php?stock=low / ?stock=out so the filter
// is already applied on arrival instead of dumping the user on the
// unfiltered full catalog.
document.addEventListener('DOMContentLoaded', () => {
  const stockParam = new URLSearchParams(location.search).get('stock');
  if (stockParam && ['in','low','out'].includes(stockParam)) {
    const sel = document.getElementById('flt-stock');
    if (sel) sel.value = stockParam;
  }
  loadProducts();
  const srch = document.getElementById('srch');
  if (srch) {
    srch.addEventListener('input', () => {
      clearTimeout(searchTimer);
      searchTimer = setTimeout(() => loadProducts(srch.value.trim(), 1), 280);
    });
    srch.addEventListener('keydown', e => {
      if (e.key === 'Escape') { srch.value = ''; loadProducts('', 1); }
    });
  }
});

// ── Live sync — reflect edits made on ANOTHER terminal in place ────
// Someone editing this same product's price/stock on a different tab
// (or another PC) shows up here within a couple of seconds via
// assets/stock-sync.js's poll, patched straight into the visible row —
// no manual reload needed. Only touches rows on the CURRENT page
// (allProducts is just the current page's slice); a row that scrolled
// off onto another page picks up the fresh values next time it's loaded.
window.addEventListener('medi:stock-sync', (e) => {
  const changed = e.detail?.changed || [];
  if (!changed.length || !allProducts.length) return;

  changed.forEach(cp => {
    const p = allProducts.find(x => x.id === cp.id);
    if (!p) return; // not visible on this page right now
    if (typeof cp.stock_pcs === 'number')       p.stock_pcs       = cp.stock_pcs;
    if (typeof cp.low_stock_alert === 'number') p.low_stock_alert = cp.low_stock_alert;
    if (typeof cp.sale_price === 'number')      p.sale_price      = cp.sale_price;

    const row = document.querySelector(`#prod-body tr[data-id="${p.id}"]`);
    if (!row) return;
    row.className = rowClassFor(p);

    const priceCell = row.querySelector('.cell-price');
    if (priceCell) {
      priceCell.textContent = CUR + parseFloat(p.sale_price || 0).toFixed(2);
      priceCell.classList.remove('stock-flash'); void priceCell.offsetWidth; priceCell.classList.add('stock-flash');
    }
    const stockCell = row.querySelector('.cell-stock');
    if (stockCell) {
      stockCell.innerHTML = stockBadgeHtml(p);
      stockCell.classList.remove('stock-flash'); void stockCell.offsetWidth; stockCell.classList.add('stock-flash');
    }
  });
});

// ── Modal helpers ─────────────────────────────────────────────
function companyOptions(sel = '') {
  return `<option value="">— Select —</option>` +
    COMPANIES.map(c =>
      `<option value="${c.id}" ${c.id == sel ? 'selected' : ''}>${esc(c.name)}</option>`
    ).join('');
}
function genericOptions(sel = '') {
  return `<option value="">— Select —</option>` +
    GENERICS.map(g =>
      `<option value="${g.id}" ${g.id == sel ? 'selected' : ''}>${esc(g.name)}</option>`
    ).join('');
}
function categoryOptions(sel = '') {
  return `<option value="">— Select —</option>` +
    CATEGORIES.map(cat =>
      `<option value="${cat.id}" ${cat.id == sel ? 'selected' : ''}>${esc(cat.name)}</option>`
    ).join('');
}
function supplierOptions(sel = '') {
  return `<option value="">— Select —</option>` +
    SUPPLIERS.map(s =>
      `<option value="${s.id}" ${s.id == sel ? 'selected' : ''}>${esc(s.name)}</option>`
    ).join('');
}

// Lightweight section divider for the Add/Edit form — groups related
// fields (Classification, Supplier & Location, Pricing & Cost, Stock &
// Alerts) so the wider/denser layout below still reads as organized
// rather than one long flat stack of inputs.
function pfSection(icon, label) {
  return `<div style="font-size:11px;font-weight:800;color:var(--brand);
              text-transform:uppercase;letter-spacing:.5px;
              border-bottom:2px solid var(--brand-light);padding-bottom:5px;
              margin-top:4px;display:flex;align-items:center;gap:6px">
      ${icon} ${label}
    </div>`;
}

function productForm(p = {}) {
  return `
  <div style="display:flex;flex-direction:column;gap:10px">

    <div class="fg">
      <label>Product Name *</label>
      <input id="f-name" value="${esc(p.name||'')}" placeholder="e.g. NAPA 500mg Tablet">
    </div>

    <div class="fg">
      <label>Product Photo</label>
      <div style="display:flex;gap:14px;align-items:center">
        <div style="position:relative;flex-shrink:0">
          <img id="f-pic-preview" src="" alt=""
               style="width:76px;height:76px;border-radius:var(--rsm);object-fit:cover;
                      background:var(--surface2);display:none;border:1px solid var(--border)">
          <div id="f-pic-placeholder"
               style="width:76px;height:76px;border-radius:var(--rsm);background:var(--surface2);
                      display:flex;align-items:center;justify-content:center;font-size:26px;color:var(--text3)">📦</div>
        </div>
        <div>
          <input type="file" id="f-pic-input" accept="image/*" style="display:none" onchange="onProductPicSelected(event)">
          <button type="button" class="btn btn-sm" onclick="document.getElementById('f-pic-input').click()">
            🖼️ Change Photo
          </button>
          <div id="f-pic-remove-wrap" style="display:none;margin-top:6px">
            <a href="#" onclick="removeProductPic();return false" style="font-size:11px;color:var(--danger)">Remove photo</a>
          </div>
          <div style="font-size:10px;color:var(--text3);margin-top:6px">
            Photo of the box / packaging — helps staff tell look-alike medicines apart
          </div>
        </div>
      </div>
    </div>

    ${pfSection('🏷️', 'Classification')}
    <div class="fg3">
      <div class="fg">
        <label>Form</label>
        <select id="f-form">
          ${['','TABLET','CAPSULE','SYRUP','INJECTION','CREAM','DROPS','INHALER',
             'SUPPOSITORY','POWDER','SUSPENSION','GEL','OINTMENT','PATCH','OTHER']
            .map(v => `<option ${p.form===v?'selected':''}>${v}</option>`).join('')}
        </select>
      </div>
      <div class="fg">
        <label>Strength</label>
        <input id="f-strength" value="${esc(p.strength||'')}" placeholder="e.g. 500mg">
      </div>
      <div class="fg">
        <label>Barcode</label>
        <input id="f-barcode" value="${esc(p.barcode||'')}" placeholder="Optional">
      </div>
    </div>
    <div class="fg3">
      <div class="fg">
        <label>Generic</label>
        <select id="f-generic">${genericOptions(p.generic_id)}</select>
      </div>
      <div class="fg">
        <label>Company</label>
        <select id="f-company">${companyOptions(p.company_id)}</select>
      </div>
      <div class="fg">
        <label>Category</label>
        <select id="f-category">${categoryOptions(p.category_id)}</select>
      </div>
    </div>

    ${pfSection('📍', 'Supplier & Location')}
    <div class="fg2">
      <div class="fg">
        <label>Supplier</label>
        <select id="f-supplier">${supplierOptions(p.supplier_id)}</select>
        <span style="font-size:10px;color:var(--text3);margin-top:2px">
          Default supplier for reorder requisitions
        </span>
      </div>
      <div class="fg">
        <label>Rack / Shelf</label>
        <input id="f-rack" value="${esc(p.rack||'')}" placeholder="e.g. A-12">
      </div>
    </div>

    ${pfSection('💰', 'Pricing & Cost')}
    <div class="fg4">
      <div class="fg">
        <label>Pcs per Box *</label>
        <input id="f-qib" type="number" min="1" value="${p.qty_in_box||1}" oninput="calcDerivedPrices()">
      </div>
      <div class="fg">
        <label>Retail Price / pc *</label>
        <input id="f-price" type="number" step="0.01" min="0" value="${p.sale_price||0}" oninput="calcDerivedPrices()">
      </div>
      <div class="fg">
        <label>Retail Price / Box (auto)</label>
        <input id="f-retail-box" type="number" step="0.01" min="0" value="0" readonly
               style="background:var(--surface2)">
      </div>
      <div class="fg">
        <label>Trade Price / Box (TP)</label>
        <input id="f-btp" type="number" step="0.01" min="0" value="${p.btp||0}" oninput="calcDerivedPrices()">
      </div>
    </div>
    <div class="fg4">
      <div class="fg">
        <label>VAT / Box (auto 17.4% of TP)</label>
        <input id="f-vat" type="number" step="0.01" min="0" value="${p.vat||0}" readonly
               style="background:var(--surface2)">
      </div>
      <div class="fg">
        <label>Buying Price / Box (auto)</label>
        <input id="f-btpvat" type="number" step="0.01" min="0" value="${p.btp_vat||0}" readonly
               style="background:var(--surface2)">
      </div>
      <div class="fg">
        <label>Buying Price / Pc (auto)</label>
        <input id="f-buy-pc" type="number" step="0.01" min="0" value="0" readonly
               style="background:var(--surface2)">
      </div>
      <div class="fg">
        <label>Wholesale Price / pc</label>
        <input id="f-ws-price" type="number" step="0.01" min="0"
               value="${p.wholesale_price||0}"
               placeholder="0 = use retail price">
      </div>
    </div>
    <div class="fg2">
      <div class="fg">
        <label>Wholesale Discount %</label>
        <input id="f-ws-disc" type="number" step="0.1" min="0" max="100"
               value="${p.wholesale_disc_pct||0}"
               placeholder="e.g. 10">
        <span style="font-size:10px;color:var(--text3);margin-top:2px">
          Auto-applied when wholesale mode is selected
        </span>
      </div>
      <div class="fg">
        <label>Max Retail Discount %</label>
        <input id="f-maxdisc" type="number" step="0.1" min="0" max="100"
               value="${p.max_retail_disc||0}" placeholder="0 = use system default">
        <span style="font-size:10px;color:var(--text3);margin-top:2px">
          Per-product cap; 0 falls back to the global setting
        </span>
      </div>
    </div>

    ${pfSection('📦', 'Stock & Alerts')}
    <div class="fg3">
      <div class="fg">
        <label>🔔 Low Stock Alert Qty (pcs)</label>
        <input id="f-alert" type="number" min="0"
               value="${p.low_stock_alert||0}">
        <span style="font-size:10px;color:var(--text3);margin-top:2px">
          Alert triggers when stock falls below this number
        </span>
      </div>
      <div class="fg">
        <label>Min Order Qty (pcs)</label>
        <input id="f-minqty" type="number" min="0"
               value="${p.min_qty||0}">
      </div>
      <div class="fg">
        <label>Opening Stock (pcs)</label>
        <input id="f-stock" type="number" min="0"
               value="${p.stock_pcs||0}"
               ${p.id ? 'style="background:var(--surface2)" title="Update stock via Purchase"' : ''}>
      </div>
    </div>

    <div id="f-err" style="color:var(--danger);font-size:11px;display:none"></div>
  </div>
  <div style="display:flex;gap:8px;justify-content:flex-end;margin-top:14px;padding-top:12px;border-top:1px solid var(--border)">
    <button class="btn" onclick="closeModal()">Cancel</button>
    <button class="btn btn-primary" onclick="saveProduct(${p.id||0})">
      ${p.id ? '💾 Save Changes' : '➕ Add Product'}
    </button>
  </div>`;
}

// Recomputes every derived pricing field whenever any of their inputs
// change (pcs/box, retail price/pc, trade price/box). VAT/Box is itself
// derived — always 17.4% of Trade Price/Box (TP), not independently
// editable. Nothing here is stored server-side except btp/vat (and the
// DB-persisted btp_vat) — Retail Price/Box and Buying Price/Pc are pure
// display convenience, recomputed fresh from those same stored fields
// every time the form opens or a value changes.
const VAT_RATE = 0.174; // 17.4% of Trade Price/Box (TP)

function calcDerivedPrices() {
  const qib   = parseInt(document.getElementById('f-qib')?.value)     || 0;
  const price = parseFloat(document.getElementById('f-price')?.value) || 0;
  const btp   = parseFloat(document.getElementById('f-btp')?.value)   || 0;
  const vat   = btp * VAT_RATE;

  const vatEl = document.getElementById('f-vat');
  if (vatEl) vatEl.value = vat.toFixed(2);

  const btpVat = btp + vat;

  const btpVatEl = document.getElementById('f-btpvat');
  if (btpVatEl) btpVatEl.value = btpVat.toFixed(2);

  const retailBoxEl = document.getElementById('f-retail-box');
  if (retailBoxEl) retailBoxEl.value = (qib * price).toFixed(2);

  const buyPcEl = document.getElementById('f-buy-pc');
  if (buyPcEl) buyPcEl.value = (qib > 0 ? btpVat / qib : 0).toFixed(2);
}

// ── Product photo (box/packaging picture) ─────────────────────
// undefined = unchanged, '' = removed, 'data:...' = new/changed —
// same three-state convention as profile.php's pendingPic, so a plain
// price/stock edit never re-uploads (or wipes) an existing photo.
let pendingProductImage = undefined;

function renderProductPic(src) {
  const img = document.getElementById('f-pic-preview');
  const ph  = document.getElementById('f-pic-placeholder');
  const rm  = document.getElementById('f-pic-remove-wrap');
  if (!img) return;
  if (src) {
    img.src = src; img.style.display = '';
    ph.style.display = 'none';
    rm.style.display = '';
  } else {
    img.style.display = 'none';
    ph.style.display = 'flex';
    rm.style.display = 'none';
  }
}

// Downscales to ~360px on the longer side and re-encodes as JPEG at
// moderate quality before storing — a full-resolution phone photo can be
// several MB, but this column gets read back on every product-list page
// and every Sale Entry keystroke search, so keeping it small (typically
// 5-15KB, depending on how detailed the photo is) matters far more here
// than for a one-off profile photo. This is a small identification
// thumbnail, not a zoomable product photo, so the extra resolution isn't
// useful anyway.
function onProductPicSelected(ev) {
  const file = ev.target.files[0];
  const errEl = document.getElementById('f-err');
  if (!file) return;
  if (!file.type.startsWith('image/')) {
    errEl.textContent = '⚠ Please choose an image file.'; errEl.style.display = 'block'; return;
  }
  if (file.size > 8 * 1024 * 1024) {
    errEl.textContent = '⚠ Image is too large (max 8MB).'; errEl.style.display = 'block'; return;
  }
  const reader = new FileReader();
  reader.onload = () => {
    const img = new Image();
    img.onload = () => {
      const maxDim = 360;
      let w = img.width, h = img.height;
      if (w > maxDim || h > maxDim) {
        if (w > h) { h = Math.round(h * maxDim / w); w = maxDim; }
        else       { w = Math.round(w * maxDim / h); h = maxDim; }
      }
      const canvas = document.createElement('canvas');
      canvas.width = w; canvas.height = h;
      canvas.getContext('2d').drawImage(img, 0, 0, w, h);
      pendingProductImage = canvas.toDataURL('image/jpeg', 0.6);
      renderProductPic(pendingProductImage);
      errEl.style.display = 'none';
    };
    img.src = reader.result;
  };
  reader.readAsDataURL(file);
}
function removeProductPic() {
  pendingProductImage = '';
  renderProductPic(null);
}

function openAdd() {
  pendingProductImage = undefined;
  showModal(productForm({}), '➕ Add New Product', 'xl');
  renderProductPic(null);
  setTimeout(calcDerivedPrices, 50);
}

async function openEdit(id) {
  const r = await apiPost(API, { action: 'detail', id });
  if (!r.success) { alert('Could not load product.'); return; }
  pendingProductImage = undefined;
  showModal(productForm(r.data), '✏ Edit Product', 'xl');
  renderProductPic(r.data.image || null);
  setTimeout(calcDerivedPrices, 50);
}

// ── Save (add / edit) ─────────────────────────────────────────
async function saveProduct(id) {
  const name  = document.getElementById('f-name').value.trim();
  const price = parseFloat(document.getElementById('f-price').value) || 0;
  const qib   = parseInt(document.getElementById('f-qib').value)     || 1;
  const errEl = document.getElementById('f-err');

  if (!name) {
    errEl.textContent = '⚠ Product name is required.';
    errEl.style.display = 'block';
    document.getElementById('f-name').focus();
    return;
  }

  const payload = {
    action:     id ? 'edit' : 'add',
    id,
    name,
    form:       document.getElementById('f-form').value,
    strength:   document.getElementById('f-strength').value.trim(),
    generic_id: document.getElementById('f-generic').value || null,
    company_id: document.getElementById('f-company').value || null,
    category_id: document.getElementById('f-category').value || null,
    supplier_id: document.getElementById('f-supplier').value || null,
    barcode:    document.getElementById('f-barcode').value.trim(),
    rack:       document.getElementById('f-rack').value.trim(),
    qty_in_box: qib,
    sale_price: price,
    btp:        parseFloat(document.getElementById('f-btp').value)    || 0,
    vat:        parseFloat(document.getElementById('f-vat').value)    || 0,
    btp_vat:    parseFloat(document.getElementById('f-btpvat').value) || 0,
    stock_pcs:  id ? null : (parseInt(document.getElementById('f-stock').value) || 0),
    // These four were rendered in the form but never actually sent —
    // every save silently reset them to 0 server-side even though the
    // field showed the real value. Fixed here; nothing server-side
    // needed changing, api_add()/api_edit() already accept all of them.
    low_stock_alert:     parseInt(document.getElementById('f-alert').value)   || 0,
    min_qty:             parseInt(document.getElementById('f-minqty').value)  || 0,
    wholesale_price:     parseFloat(document.getElementById('f-ws-price').value) || 0,
    wholesale_disc_pct:  parseFloat(document.getElementById('f-ws-disc').value)  || 0,
    max_retail_disc:     parseFloat(document.getElementById('f-maxdisc').value)  || 0,
  };
  if (pendingProductImage !== undefined) payload.image = pendingProductImage;

  const r = await apiPost(API, payload);
  if (r.success) {
    pendingProductImage = undefined;
    closeModal();
    loadProducts(document.getElementById('srch')?.value || '', curPage);
  } else {
    errEl.textContent = '⚠ ' + (r.error || 'Save failed.');
    errEl.style.display = 'block';
  }
}

// ── Delete ────────────────────────────────────────────────────
async function delProduct(id, name) {
  if (!confirm(`Delete "${name}"?\n\nThis cannot be undone.`)) return;
  const r = await apiPost(API, { action: 'delete', id });
  if (r.success) {
    loadProducts(document.getElementById('srch')?.value || '', curPage);
  } else {
    alert('⚠ ' + (r.error || 'Delete failed.'));
  }
}

// ── Tabs ────────────────────────────────────────────────────
let stockModLoaded = false;
function switchTab(name) {
  document.getElementById('tab-products').style.display  = name === 'products'  ? '' : 'none';
  const smTab = document.getElementById('tab-stockmod');
  if (smTab) smTab.style.display = name === 'stockmod' ? '' : 'none';
  document.getElementById('tabbtn-products')?.classList.toggle('active', name === 'products');
  document.getElementById('tabbtn-stockmod')?.classList.toggle('active', name === 'stockmod');
  if (name === 'stockmod' && !stockModLoaded) {
    stockModLoaded = true;
    loadStockModUsers();
    loadStockModLog(1);
  }
}

// ── Product History ────────────────────────────────────────
async function openHistory(productId) {
  showModal(`<div id="hist-body" style="min-height:200px;display:flex;align-items:center;justify-content:center;color:var(--text3)">Loading…</div>`,
    '🕒 Product History', 'xl');
  // ONE request for everything (summary + purchases + sales + timeline)
  // — previously this fired 4 separate requests via Promise.all, each of
  // which independently re-ran the opening-stock backfill migration
  // (PHP doesn't share its "already ran" flag across separate requests),
  // so a large catalog's first-ever History click did 4x the backfill
  // work at once and could sit on "Loading…" for a long time on this
  // app's XAMPP/Apache setup. One request = the backfill work happens
  // (at most) once per click.
  const r = await apiPost(HISTORY_API, { action: 'full', product_id: productId, po_page_size: 10, sale_page_size: 10, timeline_page_size: 15 }, 40000);
  const body = document.getElementById('hist-body');
  if (!body) return; // modal was closed before the request finished

  if (!r.success) { body.innerHTML = `<div style="color:var(--danger)">⚠ ${esc(r.error)}</div>`; return; }

  const p = r.product;
  const poList = r.purchases_list || [];
  const saleList = r.sales_list || [];
  const tl = r.timeline || { data: [] };
  body.outerHTML = `<div id="hist-body">
    <div style="font-weight:700;font-size:15px;margin-bottom:2px">${esc(p.name)} ${p.strength ? `<span style="font-weight:400;color:var(--text3);font-size:12px">${esc(p.strength)}</span>` : ''}</div>
    <div style="font-size:11px;color:var(--text3);margin-bottom:14px">${esc(p.form||'—')} · Barcode: ${esc(p.barcode||'—')}</div>

    <div style="display:grid;grid-template-columns:repeat(3,1fr);gap:10px;margin-bottom:16px">
      <div class="sc" style="border-top:3px solid var(--accent)">
        <div class="slabel">Total Purchased (Lifetime)</div>
        <div class="sval b">${r.purchase.qty} pcs</div>
        <div style="font-size:11px;color:var(--text3);margin-top:2px">${CUR}${fmtN(r.purchase.amount)}</div>
      </div>
      <div class="sc" style="border-top:3px solid var(--brand)">
        <div class="slabel">Total Sold (Lifetime)</div>
        <div class="sval b">${r.sales.qty} pcs</div>
        <div style="font-size:11px;color:var(--text3);margin-top:2px">${CUR}${fmtN(r.sales.amount)}</div>
      </div>
      <div class="sc" style="border-top:3px solid var(--warn)">
        <div class="slabel">Current Stock</div>
        <div class="sval b">${p.stock_pcs} pcs</div>
      </div>
    </div>

    <div style="display:grid;grid-template-columns:1fr 1fr;gap:14px;margin-bottom:16px">
      <div>
        <div style="font-weight:700;font-size:12.5px;margin-bottom:6px">📥 Purchase Orders <span style="color:var(--text3);font-weight:400">(latest first)</span></div>
        <div style="border:1px solid var(--border);border-radius:var(--rsm);max-height:220px;overflow-y:auto">
          ${!poList.length ? '<div class="empty-state" style="padding:14px">No purchases found.</div>' :
            poList.map(po => `
              <div style="display:flex;align-items:center;gap:6px;padding:7px 10px;border-bottom:1px solid var(--border);font-size:12px">
                <div style="flex:1;min-width:0">
                  <div style="font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(po.invoice)}</div>
                  <div style="color:var(--text3);font-size:10.5px">${esc(po.date)} · Qty: ${po.this_product_qty}</div>
                </div>
                <div class="mono" style="font-weight:700;color:var(--brand);white-space:nowrap">${CUR}${fmtN(po.final_price)}</div>
                <button class="btn btn-sm" onclick="viewPODetail(${po.id})">View</button>
              </div>`).join('')}
        </div>
      </div>
      <div>
        <div style="font-weight:700;font-size:12.5px;margin-bottom:6px">📤 Sale Invoices <span style="color:var(--text3);font-weight:400">(latest first)</span></div>
        <div style="border:1px solid var(--border);border-radius:var(--rsm);max-height:220px;overflow-y:auto">
          ${!saleList.length ? '<div class="empty-state" style="padding:14px">No sales found.</div>' :
            saleList.map(s => `
              <div style="display:flex;align-items:center;gap:6px;padding:7px 10px;border-bottom:1px solid var(--border);font-size:12px">
                <div style="flex:1;min-width:0">
                  <div style="font-weight:600;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${esc(s.invoice)}</div>
                  <div style="color:var(--text3);font-size:10.5px">${esc(s.date)} · Qty: ${s.this_product_qty}</div>
                </div>
                <div class="mono" style="font-weight:700;color:var(--brand);white-space:nowrap">${CUR}${fmtN(s.final_price)}</div>
                <button class="btn btn-sm" onclick="viewInvoiceDetail(${s.id})">View</button>
              </div>`).join('')}
        </div>
      </div>
    </div>

    <div style="font-weight:700;font-size:12.5px;margin-bottom:6px">📊 Stock Movement Timeline</div>
    <div style="border:1px solid var(--border);border-radius:var(--rsm);max-height:280px;overflow-y:auto">
      <table style="width:100%;border-collapse:collapse;font-size:11.5px">
        <thead style="position:sticky;top:0;background:var(--surface2)">
          <tr>
            <th style="padding:6px 8px;text-align:left">Date</th>
            <th style="padding:6px 8px;text-align:left">Type</th>
            <th style="padding:6px 8px;text-align:left">Reference</th>
            <th style="padding:6px 8px;text-align:right">In</th>
            <th style="padding:6px 8px;text-align:right">Out</th>
            <th style="padding:6px 8px;text-align:right">Balance</th>
            <th style="padding:6px 8px;text-align:left">User</th>
          </tr>
        </thead>
        <tbody>
          ${!tl.data.length ? '<tr><td colspan="7" class="empty-state">No stock movements recorded yet.</td></tr>' :
            tl.data.map(t => `
              <tr>
                <td style="padding:5px 8px;white-space:nowrap">${esc(t.created_at)}</td>
                <td style="padding:5px 8px">${txnTypeLabel(t.transaction_type)}</td>
                <td style="padding:5px 8px;color:var(--text3)">${esc(t.reference_no || '—')}</td>
                <td style="padding:5px 8px;text-align:right;color:var(--accent);font-weight:700">${t.qty_in > 0 ? '+' + t.qty_in : ''}</td>
                <td style="padding:5px 8px;text-align:right;color:var(--danger);font-weight:700">${t.qty_out > 0 ? '-' + t.qty_out : ''}</td>
                <td style="padding:5px 8px;text-align:right;font-weight:700">${t.balance_after}</td>
                <td style="padding:5px 8px;color:var(--text3)">${esc(t.user_name || 'System')}</td>
              </tr>`).join('')}
        </tbody>
      </table>
    </div>
  </div>`;
}

function txnTypeLabel(type) {
  const map = {
    purchase: '📥 Purchase', purchase_return: '↩️ Purchase Return',
    sale: '📤 Sale', sale_return: '↩️ Sale Return',
    stock_increase: '⬆️ Stock Increase', stock_decrease: '⬇️ Stock Decrease',
    opening_stock: '🏁 Opening Stock', transfer_in: '📦 Transfer In',
    transfer_out: '📦 Transfer Out', adjustment: '⚖️ Adjustment',
  };
  return map[type] || esc(type);
}

async function viewPODetail(id) {
  const r = await apiPost('../api/purchases.php', { action: 'detail', id });
  if (!r.success) { alert('⚠ ' + r.error); return; }
  const d = r.data, items = r.items || [];
  showModal(`
    <div style="margin-bottom:8px;font-size:12.5px;color:var(--text2)"><strong>${esc(d.invoice)}</strong> — ${esc(d.supplier_name||'—')} — ${esc(d.date)}</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;font-size:12px">
        <thead><tr style="background:var(--surface2)">
          <th style="padding:6px 8px;text-align:left">Product</th>
          <th style="padding:6px 8px;text-align:center">Boxes×Qty</th>
          <th style="padding:6px 8px;text-align:right">Rate</th>
          <th style="padding:6px 8px;text-align:right">Total</th>
        </tr></thead>
        <tbody>${items.map(it => `
          <tr>
            <td style="padding:6px 8px">${esc(it.product_name)}</td>
            <td style="padding:6px 8px;text-align:center">${it.boxes} × ${it.qty_in_box}</td>
            <td style="padding:6px 8px;text-align:right" class="mono">${CUR}${fmtN(it.btp_vat)}</td>
            <td style="padding:6px 8px;text-align:right;font-weight:700" class="mono">${CUR}${fmtN(it.total_price)}</td>
          </tr>`).join('') || '<tr><td colspan="4" class="empty-state">No items.</td></tr>'}</tbody>
      </table>
    </div>
    <div style="text-align:right;margin-top:10px;font-weight:800;font-size:15px;color:var(--brand)">${CUR}${fmtN(d.final_price)}</div>
  `, '📥 Purchase Order Detail', 'lg');
}

async function viewInvoiceDetail(id) {
  const r = await apiPost('../api/sales.php', { action: 'detail', id });
  if (!r.success) { alert('⚠ ' + r.error); return; }
  const d = r.data, items = r.items || [];
  showModal(`
    <div style="margin-bottom:8px;font-size:12.5px;color:var(--text2)"><strong>${esc(d.invoice)}</strong> — ${esc(d.customer_name||'Walk-in')} — ${esc(d.date)}</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;font-size:12px">
        <thead><tr style="background:var(--surface2)">
          <th style="padding:6px 8px;text-align:left">Product</th>
          <th style="padding:6px 8px;text-align:center">Qty</th>
          <th style="padding:6px 8px;text-align:right">Rate</th>
          <th style="padding:6px 8px;text-align:right">Total</th>
        </tr></thead>
        <tbody>${items.map(it => `
          <tr>
            <td style="padding:6px 8px">${esc(it.product_name)}</td>
            <td style="padding:6px 8px;text-align:center">${it.qty}</td>
            <td style="padding:6px 8px;text-align:right" class="mono">${CUR}${fmtN(it.price)}</td>
            <td style="padding:6px 8px;text-align:right;font-weight:700" class="mono">${CUR}${fmtN(it.total)}</td>
          </tr>`).join('') || '<tr><td colspan="4" class="empty-state">No items.</td></tr>'}</tbody>
      </table>
    </div>
    <div style="text-align:right;margin-top:10px;font-weight:800;font-size:15px;color:var(--brand)">${CUR}${fmtN(d.final_price)}</div>
  `, '📤 Sale Invoice Detail', 'lg');
}

// ── Stock Modification form ──────────────────────────────────
function openStockMod(id, name, stock) {
  const now = new Date().toLocaleString();
  showModal(`
    <div style="display:flex;flex-direction:column;gap:10px">
      <div id="sm-err" style="display:none;background:#fef2f2;border:1px solid #fecaca;color:#991b1b;padding:8px 12px;border-radius:var(--rsm);font-size:12.5px"></div>
      <div class="fg"><label>Product Name</label><input value="${esc(name)}" readonly style="background:var(--surface2)"></div>
      <div class="fg"><label>Current Stock</label><input value="${stock} pcs" readonly style="background:var(--surface2)"></div>
      <div class="fg">
        <label>Modification Type *</label>
        <div style="display:flex;gap:14px;margin-top:4px">
          <label style="display:flex;align-items:center;gap:5px;font-weight:500;font-size:13px;cursor:pointer">
            <input type="radio" name="sm-type" value="increase" checked onchange="smUpdateSign()"> ⬆️ Increase
          </label>
          <label style="display:flex;align-items:center;gap:5px;font-weight:500;font-size:13px;cursor:pointer">
            <input type="radio" name="sm-type" value="decrease" onchange="smUpdateSign()"> ⬇️ Decrease
          </label>
        </div>
      </div>
      <div class="fg"><label>Quantity *</label><input type="number" id="sm-qty" min="1" step="1" placeholder="e.g. 10"></div>
      <div class="fg"><label>Reason *</label>
        <select id="sm-reason" onchange="document.getElementById('sm-reason-other').style.display=this.value==='Other'?'block':'none'">
          <option value="">Select a reason…</option>
          <option>Physical Stock Count Correction</option>
          <option>Damaged / Expired Stock</option>
          <option>Theft / Loss</option>
          <option>Data Entry Correction</option>
          <option>Returned to Supplier (Non-Return Adjustment)</option>
          <option>Other</option>
        </select>
        <input type="text" id="sm-reason-other" placeholder="Specify reason…" style="display:none;margin-top:6px">
      </div>
      <div class="fg"><label>Remarks</label>
        <textarea id="sm-remarks" rows="2" style="padding:6px 9px;border:1px solid var(--border);border-radius:var(--rsm);font-family:inherit;font-size:12px;resize:vertical"></textarea>
      </div>
      <div style="display:flex;gap:20px;font-size:11.5px;color:var(--text3)">
        <div>Modified By: <strong style="color:var(--text2)">${esc(CURRENT_USER_NAME)}</strong></div>
        <div>Date: <strong style="color:var(--text2)">${esc(now)}</strong></div>
      </div>
      <button class="btn btn-primary" onclick="saveStockMod(${id})">💾 Save Stock Modification</button>
    </div>
  `, '🛠️ Stock Modification', 'md');
}

function smUpdateSign() { /* placeholder for future live preview if needed */ }

async function saveStockMod(productId) {
  const type    = document.querySelector('input[name="sm-type"]:checked')?.value;
  const qty     = parseInt(document.getElementById('sm-qty').value) || 0;
  const reasonSel = document.getElementById('sm-reason').value;
  const reason  = reasonSel === 'Other' ? document.getElementById('sm-reason-other').value.trim() : reasonSel;
  const remarks = document.getElementById('sm-remarks').value.trim();
  const errEl   = document.getElementById('sm-err');
  errEl.style.display = 'none';

  if (qty <= 0)   { errEl.textContent = '⚠ Quantity must be greater than zero.'; errEl.style.display = 'block'; return; }
  if (!reason)    { errEl.textContent = '⚠ Reason is required.'; errEl.style.display = 'block'; return; }

  const r = await apiPost(STOCKMOD_API, {
    action: 'save', product_id: productId, modification_type: type,
    quantity: qty, reason, remarks,
  });
  if (!r.success) { errEl.textContent = '⚠ ' + r.error; errEl.style.display = 'block'; return; }

  closeModal();
  loadProducts(document.getElementById('srch')?.value || '', curPage);
  if (stockModLoaded) loadStockModLog(1);
}

// ── Stock Modification Log ───────────────────────────────────
let smPage = 1, smSearchTimer = null, smProductTimer = null;

async function loadStockModUsers() {
  const r = await apiPost(STOCKMOD_API, { action: 'users' });
  if (!r.success) return;
  const sel = document.getElementById('sm-flt-user');
  sel.innerHTML = '<option value="">All Users</option>' +
    r.data.map(u => `<option value="${u.id}">${esc(u.name)}</option>`).join('');
}

function smProductFilterInput(q) {
  clearTimeout(smProductTimer);
  document.getElementById('sm-flt-product-id').value = '';
  const box = document.getElementById('sm-product-suggest');
  if (q.trim().length < 2) { box.style.display = 'none'; return; }
  smProductTimer = setTimeout(async () => {
    const r = await apiPost(API, { action: 'list', q, suggest: true, page_size: 8 });
    if (!r.success || !r.data.length) { box.style.display = 'none'; return; }
    box.innerHTML = r.data.map(p => `
      <div style="padding:7px 10px;cursor:pointer;font-size:12px;border-bottom:1px solid var(--border)"
           onmousedown="smPickProduct(${p.id},'${esc(p.name).replace(/'/g,"\\'")}')">${esc(p.name)} ${p.strength?`<span style="color:var(--text3)">${esc(p.strength)}</span>`:''}</div>
    `).join('');
    box.style.display = 'block';
  }, 250);
}
function smPickProduct(id, name) {
  document.getElementById('sm-flt-product-id').value = id;
  document.getElementById('sm-flt-product-name').value = name;
  document.getElementById('sm-product-suggest').style.display = 'none';
  loadStockModLog(1);
}
function smSearchInput(q) {
  clearTimeout(smSearchTimer);
  smSearchTimer = setTimeout(() => loadStockModLog(1), 280);
}
function clearStockModFilters() {
  document.getElementById('sm-flt-from').value = '';
  document.getElementById('sm-flt-to').value = '';
  document.getElementById('sm-flt-product-id').value = '';
  document.getElementById('sm-flt-product-name').value = '';
  document.getElementById('sm-flt-user').value = '';
  document.getElementById('sm-flt-type').value = '';
  document.getElementById('sm-search').value = '';
  loadStockModLog(1);
}

async function loadStockModLog(page = 1) {
  smPage = page;
  const tbody = document.getElementById('sm-body');
  tbody.innerHTML = '<tr><td colspan="9" class="empty-state">Loading…</td></tr>';
  const r = await apiPost(STOCKMOD_API, {
    action: 'list', page, page_size: 25,
    q: document.getElementById('sm-search')?.value || '',
    from: document.getElementById('sm-flt-from')?.value || '',
    to: document.getElementById('sm-flt-to')?.value || '',
    product_id: document.getElementById('sm-flt-product-id')?.value || '',
    user_id: document.getElementById('sm-flt-user')?.value || '',
    modification_type: document.getElementById('sm-flt-type')?.value || '',
  });
  if (!r.success) {
    tbody.innerHTML = `<tr><td colspan="9" class="empty-state" style="color:var(--danger)">Error: ${esc(r.error)}</td></tr>`;
    return;
  }
  renderStockModTable(r.data, r);
}

function renderStockModTable(rows, meta) {
  const tbody  = document.getElementById('sm-body');
  const footer = document.getElementById('sm-footer');
  if (!rows.length) {
    tbody.innerHTML = '<tr><td colspan="9" class="empty-state">No stock modification records found.</td></tr>';
    footer.innerHTML = '';
    return;
  }
  tbody.innerHTML = rows.map(r => `
    <tr>
      <td style="white-space:nowrap">${esc(r.created_at)}</td>
      <td>${esc(r.product_name)}</td>
      <td>${r.modification_type === 'increase' ? '<span style="color:var(--accent);font-weight:700">⬆️ Increase</span>' : '<span style="color:var(--danger);font-weight:700">⬇️ Decrease</span>'}</td>
      <td style="text-align:right" class="mono">${r.quantity}</td>
      <td style="text-align:right" class="mono">${r.stock_before}</td>
      <td style="text-align:right" class="mono;font-weight:700">${r.stock_after}</td>
      <td>${esc(r.reason)}</td>
      <td style="color:var(--text3)">${esc(r.remarks || '—')}</td>
      <td>${esc(r.user_name || 'System')}</td>
    </tr>`).join('');

  const total = meta.total ?? rows.length;
  const totalPages = meta.total_pages ?? 1;
  const page = meta.page ?? 1;
  footer.innerHTML = `
    <div style="display:flex;align-items:center;justify-content:space-between;gap:10px">
      <span>${total} record(s)</span>
      <div style="display:flex;align-items:center;gap:6px">
        <button class="btn btn-sm" ${page<=1?'disabled':''} onclick="loadStockModLog(1)">« First</button>
        <button class="btn btn-sm" ${page<=1?'disabled':''} onclick="loadStockModLog(${page-1})">‹ Prev</button>
        <span style="padding:0 6px">Page ${page} of ${totalPages}</span>
        <button class="btn btn-sm" ${page>=totalPages?'disabled':''} onclick="loadStockModLog(${page+1})">Next ›</button>
        <button class="btn btn-sm" ${page>=totalPages?'disabled':''} onclick="loadStockModLog(${totalPages})">Last »</button>
      </div>
    </div>`;
}

function exportStockModExcel() {
  const params = new URLSearchParams({
    action: 'export_csv',
    q: document.getElementById('sm-search')?.value || '',
    from: document.getElementById('sm-flt-from')?.value || '',
    to: document.getElementById('sm-flt-to')?.value || '',
    product_id: document.getElementById('sm-flt-product-id')?.value || '',
    user_id: document.getElementById('sm-flt-user')?.value || '',
    modification_type: document.getElementById('sm-flt-type')?.value || '',
  });
  window.open(STOCKMOD_API + '?' + params.toString(), '_blank');
}

function printStockModLog() {
  const table = document.getElementById('sm-table');
  const w = window.open('', '_blank', 'width=1000,height=700');
  w.document.write(`<html><head><title>Stock Modification Log</title>
    <style>
      body{font-family:Arial,sans-serif;padding:20px}
      table{width:100%;border-collapse:collapse;font-size:11px}
      th,td{border:1px solid #ccc;padding:5px 8px;text-align:left}
      th{background:#f0f0f0}
      h2{margin-bottom:4px}
    </style></head><body>
    <h2>Stock Modification Log</h2>
    <div style="color:#666;font-size:11px;margin-bottom:10px">Printed ${new Date().toLocaleString()}</div>
    ${table.outerHTML}
    </body></html>`);
  w.document.close();
  w.focus();
  setTimeout(() => w.print(), 300);
}

// jsPDF + autotable are loaded lazily (only when Export PDF is first
// used) so the page's normal load time isn't paying for a library
// most sessions never touch.
let pdfLibLoading = null;
function loadPdfLib() {
  if (window.jspdf && window.jspdf.jsPDF) return Promise.resolve();
  if (pdfLibLoading) return pdfLibLoading;
  pdfLibLoading = new Promise((resolve, reject) => {
    const s1 = document.createElement('script');
    s1.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js';
    s1.onload = () => {
      const s2 = document.createElement('script');
      s2.src = 'https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.8.2/jspdf.plugin.autotable.min.js';
      s2.onload = resolve;
      s2.onerror = reject;
      document.head.appendChild(s2);
    };
    s1.onerror = reject;
    document.head.appendChild(s1);
  });
  return pdfLibLoading;
}

async function exportStockModPDF() {
  try {
    await loadPdfLib();
  } catch (e) {
    alert('⚠ Could not load the PDF library — check your internet connection and try again.');
    return;
  }
  // Export the CURRENT filtered set across all pages (not just the
  // visible page) so "Export PDF" matches what a user filtered for.
  const r = await apiPost(STOCKMOD_API, {
    action: 'list', page: 1, page_size: 5000,
    q: document.getElementById('sm-search')?.value || '',
    from: document.getElementById('sm-flt-from')?.value || '',
    to: document.getElementById('sm-flt-to')?.value || '',
    product_id: document.getElementById('sm-flt-product-id')?.value || '',
    user_id: document.getElementById('sm-flt-user')?.value || '',
    modification_type: document.getElementById('sm-flt-type')?.value || '',
  });
  if (!r.success) { alert('⚠ ' + r.error); return; }

  const { jsPDF } = window.jspdf;
  const doc = new jsPDF({ orientation: 'landscape' });
  doc.setFontSize(14);
  doc.text('Stock Modification Log', 14, 15);
  doc.setFontSize(9);
  doc.setTextColor(120);
  doc.text('Generated ' + new Date().toLocaleString(), 14, 21);
  doc.autoTable({
    startY: 26,
    head: [['Date & Time', 'Product', 'Type', 'Qty', 'Before', 'After', 'Reason', 'Remarks', 'User']],
    body: r.data.map(row => [
      row.created_at, row.product_name, row.modification_type === 'increase' ? 'Increase' : 'Decrease',
      row.quantity, row.stock_before, row.stock_after, row.reason, row.remarks || '', row.user_name || 'System',
    ]),
    styles: { fontSize: 8 },
    headStyles: { fillColor: [45, 58, 140] },
  });
  doc.save('stock_modifications_' + new Date().toISOString().slice(0, 10) + '.pdf');
}

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

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