// ─── Savuki Drilling — Invoices Module ───────────────────────────────────
import { showToast, openModal, closeModal, navigate } from '../app.js';
import { richSelect, jobcardItems } from './rich-select.js';

const BASE = '/api/invoices';
const VAT  = 0.15;

function token() { return localStorage.getItem('sd_token') || ''; }

async function apiFetch(path, params = {}) {
  const qs  = new URLSearchParams({ ...params, token: token() });
  const res = await fetch(`${BASE}/${path}?${qs}`);
  const json = await res.json();
  if (!json.success) throw new Error(json.message);
  return json.data;
}

async function apiPost(path, data) {
  const body = new URLSearchParams({ ...data, token: token() });
  const res  = await fetch(`${BASE}/${path}`, {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body,
  });
  const json = await res.json();
  if (!json.success) throw new Error(json.message);
  return json.data;
}

// ═════════════════════════════════════════════════════════════════════════
// INVOICES LIST
// ═════════════════════════════════════════════════════════════════════════
export async function initInvoices(role) {
  const isAdmin = ['admin','dev','test','dispatch'].includes(role);
  const c = document.getElementById('view-content');
  c.innerHTML = `
  <div class="page-header">
    <div><h1>Invoices</h1></div>
    ${isAdmin ? `<button class="btn btn-accent" id="btn-new-invoice">+ New Invoice</button>` : ''}
  </div>
  <!-- Summary stat cards -->
  <div class="stat-grid" id="inv-stats" style="margin-bottom:1rem">
    <div class="stat-card navy">
      <div class="stat-label">Invoices <span id="inv-stat-this-label">This Month</span></div>
      <div class="stat-value" id="inv-stat-this">—</div>
    </div>
    <div class="stat-card blue">
      <div class="stat-label">Invoices <span id="inv-stat-last-label">Last Month</span></div>
      <div class="stat-value" id="inv-stat-last">—</div>
    </div>
    <div class="stat-card green">
      <div class="stat-label">Payments <span id="pay-stat-this-label">This Month</span></div>
      <div class="stat-value" id="pay-stat-this">—</div>
    </div>
    <div class="stat-card orange">
      <div class="stat-label">Payments <span id="pay-stat-last-label">Last Month</span></div>
      <div class="stat-value" id="pay-stat-last">—</div>
    </div>
  </div>
  <div class="toolbar" style="margin-bottom:1rem">
    <div class="toolbar-filters">
      <input class="form-control" id="inv-search" placeholder="Search invoice #, client…" style="width:220px" />
      <select class="form-control" id="inv-status" style="width:140px">
        <option value="">All</option>
        <option value="DRAFT">Draft</option>
        <option value="INVOICED">Invoiced</option>
        <option value="PAID">Paid</option>
      </select>
    </div>
  </div>
  <div class="card" id="inv-wrap">
    <div class="card-body" style="padding:0"><div class="page-loader"><div class="spinner"></div></div></div>
  </div>`;

  // Load stats async (non-blocking)
  apiFetch('stats.php').then(s => {
    const el = (id, v) => { const e = document.getElementById(id); if (e) e.textContent = v; };
    el('inv-stat-this-label', s.this_month_label);
    el('inv-stat-last-label', s.last_month_label);
    el('pay-stat-this-label', s.this_month_label);
    el('pay-stat-last-label', s.last_month_label);
    el('inv-stat-this',  s.invoices_this_month);
    el('inv-stat-last',  s.invoices_last_month);
    el('pay-stat-this',  `R ${(+s.payments_this_month||0).toLocaleString('en-ZA',{minimumFractionDigits:0})}`);
    el('pay-stat-last',  `R ${(+s.payments_last_month||0).toLocaleString('en-ZA',{minimumFractionDigits:0})}`);
  }).catch(() => {});

  const load = async () => {
    const wrap = document.getElementById('inv-wrap');
    if (!wrap) return;
    wrap.innerHTML = `<div class="card-body" style="padding:0"><div class="page-loader"><div class="spinner"></div></div></div>`;
    try {
      const rows = await apiFetch('list.php', {
        search: document.getElementById('inv-search')?.value || '',
        status: document.getElementById('inv-status')?.value || '',
      });
      wrap.innerHTML = `<div class="card-body" style="padding:0">${renderInvoiceTable(rows, isAdmin)}</div>`;
      wrap.querySelectorAll('.inv-view').forEach(b =>
        b.addEventListener('click', () => navigate('invoice-view', { id: +b.dataset.id }))
      );
    } catch (err) {
      wrap.innerHTML = `<div class="empty-state"><p>${err.message}</p></div>`;
    }
  };

  document.getElementById('inv-search')?.addEventListener('input', debounce(load, 350));
  document.getElementById('inv-status')?.addEventListener('change', load);
  document.getElementById('btn-new-invoice')?.addEventListener('click', () => navigate('invoice-create'));

  await load();
}

function renderInvoiceTable(rows, isAdmin) {
  if (!rows.length) return `<div class="empty-state" style="padding:2rem"><p>No invoices found.</p></div>`;

  const statusBadge = s => {
    const map = { DRAFT:'badge-pending', INVOICED:'badge-active', PAID:'badge-complete', UNPAID:'badge-active' };
    return `<span class="badge ${map[s]||'badge-navy'}">${s}</span>`;
  };

  const html = rows.map(r => {
    const subtotal = +r.items_subtotal || 0;
    const drilling = (+r.drilling_meters||0) * (+r.drilling_price||0);
    const casing   = (+r.casing_meters||0)   * (+r.casing_price||0);
    const nett     = subtotal + drilling + casing;
    const vat      = nett * VAT;
    const total    = nett + vat;
    const paid     = +r.total_paid || 0;
    const remaining= Math.max(0, total - paid);

    return `<tr>
      <td data-label="Invoice"><strong>${r.invoice_no}</strong>
        ${r.jc_no ? `<div style="font-size:.72rem;color:var(--text-muted)">JC #${r.jc_no}</div>` : ''}
      </td>
      <td data-label="Client">${esc(r.client_name||'—')}</td>
      <td data-label="Status">${statusBadge(r.status)}</td>
      <td data-label="Date" style="font-size:.82rem;color:var(--text-muted)">${(r.invoice_date||'').slice(0,10)}</td>
      <td data-label="Total" style="font-weight:700">R ${fmt(total)}</td>
      <td data-label="Paid" style="color:var(--success)">R ${fmt(paid)}</td>
      <td data-label="Remaining" style="color:${remaining>0?'var(--danger)':'var(--success)'}">
        R ${fmt(remaining)}
      </td>
      <td data-label=""><button class="btn btn-primary btn-sm inv-view" data-id="${r.record_id}">View</button></td>
    </tr>`;
  }).join('');

  return `<div class="table-wrap"><table>
    <thead><tr>
      <th>Invoice</th><th>Client</th><th>Status</th><th>Date</th>
      <th>Total</th><th>Paid</th><th>Remaining</th><th></th>
    </tr></thead>
    <tbody>${html}</tbody>
  </table></div>`;
}

// ═════════════════════════════════════════════════════════════════════════
// CREATE INVOICE PAGE
// ═════════════════════════════════════════════════════════════════════════
export async function initCreateInvoice(role, prefillJcId = null, pumpRecordId = null, pumpType = null) {
  const c = document.getElementById('view-content');
  c.innerHTML = `
  <div class="page-header">
    <div><h1>New Invoice</h1></div>
    <button class="btn btn-ghost" id="btn-back">← Back</button>
  </div>
  <div id="create-inv-body">
    <div class="page-loader"><div class="spinner"></div></div>
  </div>`;

  document.getElementById('btn-back')?.addEventListener('click', () => navigate('invoices'));

  let jcList   = [];
  let pumpData = null;
  let pumpInstalls = [];
  let pumpRepairs  = [];

  try {
    const [jcRes, pumpRes] = await Promise.all([
      fetch(`/api/jobcards/list.php?token=${token()}`).then(r=>r.json()),
      fetch(`/api/pumps/jobcard-list.php?token=${token()}`).then(r=>r.json()),
    ]);
    if (jcRes.success) jcList = jcRes.data;
    if (pumpRes.success) {
      pumpInstalls = pumpRes.data.installs || [];
      pumpRepairs  = pumpRes.data.repairs  || [];
    }
  } catch {}

  // Pre-fetch pump data if coming from pump install/repair
  if (pumpRecordId && pumpType) {
    try {
      const endpoint = pumpType === 'install' ? 'install-get.php' : 'repair-get.php';
      const res  = await fetch(`/api/pumps/${endpoint}?id=${pumpRecordId}&token=${token()}`);
      const json = await res.json();
      if (json.success) pumpData = json.data;
    } catch {}
  }

  renderInvoiceForm(null, null, jcList, prefillJcId, pumpData, pumpType, pumpInstalls, pumpRepairs);
}

// ═════════════════════════════════════════════════════════════════════════
// VIEW / EDIT INVOICE PAGE
// ═════════════════════════════════════════════════════════════════════════
export async function initInvoiceView(id, role) {
  const isAdmin = ['admin','dev','test','dispatch'].includes(role);
  const c = document.getElementById('view-content');
  c.innerHTML = `<div class="page-loader"><div class="spinner"></div></div>`;

  let data;
  try { data = await apiFetch('get.php', { id }); }
  catch (err) { c.innerHTML = `<div class="empty-state"><p>${err.message}</p></div>`; return; }

  if (isAdmin && data.invoice.status === 'DRAFT') {
    // Editable
    let jcList = [];
    try {
      const res  = await fetch(`/api/jobcards/list.php?token=${token()}`);
      const json = await res.json();
      if (json.success) jcList = json.data;
    } catch {}
    renderInvoiceForm(data, jcList, jcList);
  } else {
    renderInvoiceReadonly(data, isAdmin);
  }
}

// ═════════════════════════════════════════════════════════════════════════
// INVOICE FORM (create + edit draft)
// ═════════════════════════════════════════════════════════════════════════
function renderInvoiceForm(data, ignored, jcList, prefillJcId = null, pumpData = null, pumpType = null, pumpInstalls = [], pumpRepairs = []) {
  const inv   = data?.invoice || {};
  const items = data?.items   || [];
  const jc    = data?.jobcard || null;
  const isEdit  = !!data;
  const isPump  = !!pumpData;

  // Pre-fill client from pump data
  const prefillClient  = pumpData?.install?.client_name   || pumpData?.repair?.address       || '';
  const prefillPhone   = pumpData?.install?.contact_number || pumpData?.repair?.contact_number || '';
  const prefillAddress = pumpData?.install?.area           || pumpData?.repair?.address        || '';

  const body = document.getElementById(isEdit ? 'view-content' : 'create-inv-body');
  if (!body) return;

  if (isEdit) {
    body.innerHTML = `
    <div class="page-header">
      <div><h1>${inv.invoice_no}</h1><div class="sub">Edit Draft Invoice</div></div>
      <div style="display:flex;gap:.5rem">
        <button class="btn btn-primary" id="btn-save-draft">Save Draft</button>
        <button class="btn btn-accent"  id="btn-finalise">Mark as Invoiced</button>
        <button class="btn btn-ghost btn-sm" id="btn-pdf-draft">⬇ PDF</button>
        <button class="btn btn-ghost"   id="btn-back">← Back</button>
      </div>
    </div>`;
    document.getElementById('btn-back')?.addEventListener('click', () => navigate('invoices'));
    document.getElementById('btn-pdf-draft')?.addEventListener('click', () => {
      // Build current state for PDF
      const currentInv = { ...inv,
        client_name:    document.getElementById('inv-client-name')?.value    || inv.client_name,
        client_address: document.getElementById('inv-client-address')?.value || inv.client_address,
        client_phone:   document.getElementById('inv-client-phone')?.value   || inv.client_phone,
        client_email:   document.getElementById('inv-client-email')?.value   || inv.client_email,
        invoice_date:   document.getElementById('inv-date')?.value           || inv.invoice_date,
        due_date:       document.getElementById('inv-due')?.value            || inv.due_date,
        notes:          document.getElementById('inv-notes')?.value          || inv.notes,
        drilling_meters:parseFloat(document.getElementById('inv-drill-m')?.value || inv.drilling_meters),
        drilling_price: parseFloat(document.getElementById('inv-drill-p')?.value || inv.drilling_price),
        casing_meters:  parseFloat(document.getElementById('inv-case-m')?.value  || inv.casing_meters),
        casing_price:   parseFloat(document.getElementById('inv-case-p')?.value  || inv.casing_price),
      };
      const currentItems = collectLineItems();
      const ds = (currentInv.drilling_meters||0) * (currentInv.drilling_price||0);
      const cs = (currentInv.casing_meters||0)   * (currentInv.casing_price||0);
      const nett = currentItems.reduce((s,i)=>s+(+i.quantity * +i.unit_price),0) + ds + cs;
      const vat  = nett * VAT;
      generateInvoicePDF(currentInv, currentItems.map(i=>({...i, total: i.quantity*i.unit_price})),
        data?.jobcard, data?.payments || [], { nett, vat, total: nett+vat, paid: 0, remaining: nett+vat });
    });
  }

  const formEl = isEdit ? body : body;
  const formHtml = `
  <div id="inv-form-wrap">

    <!-- Pump banner (shown instead of JC selector when coming from pump) -->
    ${isPump ? `
    <div style="background:var(--info-bg);border:1px solid var(--info);border-radius:var(--radius);
                padding:.75rem 1rem;margin-bottom:1rem;font-size:.875rem">
      <strong>Pump ${pumpType === 'install' ? 'Installation' : 'Repair'} Invoice</strong>
      · ${esc(pumpData?.install?.client_name || pumpData?.repair?.address || '')}
      <span class="badge badge-navy" style="margin-left:.5rem;font-size:.7rem">${pumpType?.toUpperCase()}</span>
    </div>` : `
    <!-- Linked jobcard -->
    <div class="card" style="margin-bottom:1rem">
      <div class="card-header"><span class="card-title">Linked Drilling Jobcard</span></div>
      <div class="card-body">
        <div class="form-group">
          <label class="form-label">Select Jobcard (auto-fills client & meters)</label>
          <div id="inv-jc-rs"></div>
        </div>
        <div id="inv-jc-banner" style="display:none;background:var(--info-bg);border:1px solid var(--info);
             border-radius:var(--radius);padding:.75rem 1rem;margin-top:.75rem;font-size:.875rem"></div>
      </div>
    </div>`}

    <!-- Client details -->
    <div class="card" style="margin-bottom:1rem">
      <div class="card-header"><span class="card-title">Client Details</span></div>
      <div class="card-body">
        <div class="form-row">
          <div class="form-group">
            <label class="form-label">Client Name <span style="color:var(--danger)">*</span></label>
            <input class="form-control" id="inv-client-name" value="${esc(isPump ? prefillClient : inv.client_name)}" />
          </div>
          <div class="form-group">
            <label class="form-label">Phone</label>
            <input class="form-control" id="inv-client-phone" value="${esc(isPump ? prefillPhone : inv.client_phone)}" />
          </div>
        </div>
        <div class="form-row">
          <div class="form-group">
            <label class="form-label">Address</label>
            <input class="form-control" id="inv-client-address" value="${esc(isPump ? prefillAddress : inv.client_address)}" />
          </div>
          <div class="form-group">
            <label class="form-label">Email</label>
            <input class="form-control" id="inv-client-email" value="${esc(inv.client_email)}" type="email" />
          </div>
        </div>
      </div>
    </div>

    <!-- Invoice details -->
    <div class="card" style="margin-bottom:1rem">
      <div class="card-header"><span class="card-title">Invoice Details</span></div>
      <div class="card-body">
        <div class="form-row">
          <div class="form-group">
            <label class="form-label">Invoice Date</label>
            <input class="form-control" id="inv-date" type="date" value="${inv.invoice_date || today()}" />
          </div>
          <div class="form-group">
            <label class="form-label">Due Date</label>
            <input class="form-control" id="inv-due" type="date" value="${inv.due_date || ''}" />
          </div>
        </div>
      </div>
    </div>

    <!-- Drilling & Casing — hidden for pump invoices -->
    <div class="card" id="inv-drilling-card" style="margin-bottom:1rem;${isPump ? 'display:none' : ''}">
      <div class="card-header"><span class="card-title">Drilling & Casing</span></div>
      <div class="card-body">
        <div class="form-row">
          <div class="form-group">
            <label class="form-label">Drilling Meters</label>
            <input class="form-control" id="inv-drill-m" type="number" step="0.01" min="0"
                   value="${inv.drilling_meters || 0}" />
          </div>
          <div class="form-group">
            <label class="form-label">Price per Meter (R)</label>
            <input class="form-control" id="inv-drill-p" type="number" step="0.01" min="0"
                   value="${inv.drilling_price || ''}" placeholder="e.g. 350" />
          </div>
          <div class="form-group" style="align-self:flex-end">
            <label class="form-label">Subtotal</label>
            <div id="inv-drill-sub" style="padding:.6rem 0;font-weight:700">R 0.00</div>
          </div>
        </div>
        <div class="form-row">
          <div class="form-group">
            <label class="form-label">Casing Meters</label>
            <input class="form-control" id="inv-case-m" type="number" step="0.01" min="0"
                   value="${inv.casing_meters || 0}" />
          </div>
          <div class="form-group">
            <label class="form-label">Price per Meter (R)</label>
            <input class="form-control" id="inv-case-p" type="number" step="0.01" min="0"
                   value="${inv.casing_price || ''}" placeholder="e.g. 150" />
          </div>
          <div class="form-group" style="align-self:flex-end">
            <label class="form-label">Subtotal</label>
            <div id="inv-case-sub" style="padding:.6rem 0;font-weight:700">R 0.00</div>
          </div>
        </div>
      </div>
    </div>

    <!-- Line items -->
    <div class="card" style="margin-bottom:1rem">
      <div class="card-header">
        <span class="card-title">Additional Items</span>
        <button class="btn btn-ghost btn-sm" id="btn-add-line">+ Add Line</button>
      </div>
      <div class="card-body" style="padding:0">
        <div class="table-wrap">
          <table>
            <thead><tr><th style="width:50%">Description</th><th>Qty</th><th>Unit Price (R)</th><th>Total (R)</th><th></th></tr></thead>
            <tbody id="inv-lines">
              ${items.map(li => lineRow(li)).join('')}
              ${!items.length ? lineRow() : ''}
            </tbody>
          </table>
        </div>
      </div>
    </div>

    <!-- Totals & Payments -->
    <div style="display:grid;grid-template-columns:1fr 1fr;gap:1rem;margin-bottom:1rem">

      <!-- Payments -->
      <div class="card">
        <div class="card-header">
          <span class="card-title">${isPump ? 'Pump Payments' : 'Jobcard Payments'}</span>
          ${(isEdit && inv.jc_record_id) || isPump ? `<button class="btn btn-ghost btn-sm" id="btn-add-payment-inv" style="margin-left:auto">+ Add Payment</button>` : ''}
        </div>
        <div id="inv-payments-body" class="card-body" style="padding:0">
          <div class="empty-state" style="padding:1.5rem"><p style="font-size:.82rem">${isPump ? 'Loading payments…' : 'Link a jobcard to see payments'}</p></div>
        </div>
      </div>

      <!-- Totals -->
      <div class="card">
        <div class="card-header"><span class="card-title">Summary</span></div>
        <div class="card-body" id="inv-totals-body">
          ${renderTotalsHtml(0, 0, 0)}
        </div>
      </div>
    </div>

    <!-- Notes -->
    <div class="card" style="margin-bottom:1rem">
      <div class="card-header"><span class="card-title">Notes</span></div>
      <div class="card-body">
        <textarea class="form-control" id="inv-notes" rows="3">${esc(inv.notes)}</textarea>
      </div>
    </div>

    <div id="inv-err" class="login-error" style="margin-bottom:.75rem"></div>

    ${!isEdit ? `
    <div style="display:flex;gap:.75rem;margin-bottom:2rem">
      <button class="btn btn-ghost"   id="btn-save-draft">Save as Draft</button>
      <button class="btn btn-accent"  id="btn-finalise">Create Invoice</button>
    </div>` : ''}
  </div>`;

  if (isEdit) {
    body.insertAdjacentHTML('beforeend', formHtml);
  } else {
    body.innerHTML = formHtml;
  }

  // Init jobcard rich select
  let _jcData  = null;
  let invJcRs  = null;

  // If pump invoice — load pump payments immediately
  if (isPump && pumpData) {
    const pumpJobcardNo = pumpData?.install?.jobcard_no || pumpData?.repair?.jobcard_no || '';
    if (pumpJobcardNo) {
      (async () => {
        try {
          const res  = await fetch(`/api/invoices/pump-data.php?jobcard_no=${encodeURIComponent(pumpJobcardNo)}&type=${pumpType}&token=${token()}`);
          const json = await res.json();
          if (json.success) renderPaymentsPanel(json.data.payments || [], json.data.total_paid || 0);
        } catch {}
      })();
    }
    // Add payment button for pump invoices needs jobcard_no
    document.getElementById('btn-add-payment-inv')?.addEventListener('click', () => {
      const jcNo = pumpData?.install?.jobcard_no || pumpData?.repair?.jobcard_no;
      if (!jcNo) { showToast('No pump jobcard linked.', 'error'); return; }
      openAddPaymentInvoiceModal(jcNo, (payment) => {
        const paidEl = document.getElementById('inv-payments-paid');
        const curPaid = parseFloat(paidEl?.dataset.paid || 0);
        // Re-fetch to update panel
        fetch(`/api/invoices/pump-data.php?jobcard_no=${encodeURIComponent(jcNo)}&type=${pumpType}&token=${token()}`)
          .then(r=>r.json()).then(j => {
            if (j.success) renderPaymentsPanel(j.data.payments || [], j.data.total_paid || 0);
          });
      });
    });
  } else {
    invJcRs = richSelect('inv-jc-rs', {
    placeholder: 'Search jobcards, installs, repairs…',
    items: [
      { value: '', label: 'No jobcard', sub: '' },
      ...jobcardItems(jcList),
      ...pumpInstalls.map(p => ({
        value: `pump_install_${p.record_id}`,
        label: `${p.jobcard_no} — ${p.client_name||'—'}`,
        badge: 'Install',
        sub:   `${p.address||''} · ${p.jc_current_status||'—'}`,
      })),
      ...pumpRepairs.map(p => ({
        value: `pump_repair_${p.record_id}`,
        label: `${p.jobcard_no} — ${p.client_name||'—'}`,
        badge: 'Repair',
        sub:   `${p.address||''} · ${p.jc_current_status||'—'}`,
      })),
    ],
    onSelect: async (val) => {
      val = val == null ? '' : String(val);
      if (!val) {
        _jcData = null;
        const banner = document.getElementById('inv-jc-banner');
        if (banner) banner.style.display = 'none';
        // Restore drilling card if it was hidden
        const dc = document.getElementById('inv-drilling-card');
        if (dc) dc.style.display = '';
        renderPaymentsPanel([], 0);
        updateTotals();
        return;
      }

      const drillingCard = document.getElementById('inv-drilling-card');

      if (val.startsWith('pump_install_')) {
        const id = +val.replace('pump_install_', '');
        const pi = pumpInstalls.find(p => p.record_id == id);
        if (!pi) return;
        if (drillingCard) drillingCard.style.display = 'none';
        setIfEmpty('inv-client-name',    pi.client_name);
        setIfEmpty('inv-client-address', pi.address);
        setIfEmpty('inv-client-phone',   pi.contact_number);
        const banner = document.getElementById('inv-jc-banner');
        if (banner) {
          banner.style.display = '';
          banner.innerHTML = `<strong>${pi.jobcard_no}</strong> — Pump Installation
            &nbsp;·&nbsp; ${esc(pi.client_name||'—')}
            &nbsp;·&nbsp; <span class="badge badge-navy" style="font-size:.7rem">${pi.jc_current_status||'—'}</span>`;
        }
        _jcData = { _isPump: true, pump_record_id: pi.record_id, pump_type: 'install', jobcard_no: pi.jobcard_no };
        (async () => {
          try {
            const r = await fetch(`/api/invoices/pump-data.php?jobcard_no=${encodeURIComponent(pi.jobcard_no)}&type=install&token=${token()}`);
            const j = await r.json();
            if (j.success) renderPaymentsPanel(j.data.payments||[], j.data.total_paid||0);
          } catch {}
        })();
        updateTotals(); return;
      }

      if (val.startsWith('pump_repair_')) {
        const id = +val.replace('pump_repair_', '');
        const pr = pumpRepairs.find(p => p.record_id == id);
        if (!pr) return;
        if (drillingCard) drillingCard.style.display = 'none';
        setIfEmpty('inv-client-name',    pr.client_name);
        setIfEmpty('inv-client-address', pr.address);
        setIfEmpty('inv-client-phone',   pr.contact_number);
        const banner = document.getElementById('inv-jc-banner');
        if (banner) {
          banner.style.display = '';
          banner.innerHTML = `<strong>${pr.jobcard_no}</strong> — Pump Repair
            &nbsp;·&nbsp; ${esc(pr.client_name||'—')}
            &nbsp;·&nbsp; <span class="badge badge-orange" style="font-size:.7rem">${pr.jc_current_status||'—'}</span>`;
        }
        _jcData = { _isPump: true, pump_record_id: pr.record_id, pump_type: 'repair', jobcard_no: pr.jobcard_no };
        (async () => {
          try {
            const r = await fetch(`/api/invoices/pump-data.php?jobcard_no=${encodeURIComponent(pr.jobcard_no)}&type=repair&token=${token()}`);
            const j = await r.json();
            if (j.success) renderPaymentsPanel(j.data.payments||[], j.data.total_paid||0);
          } catch {}
        })();
        updateTotals(); return;
      }

      // Regular drilling jobcard
      if (drillingCard) drillingCard.style.display = '';
      const jcRow = jcList.find(j => String(j.jc_no) === String(val));
      if (!jcRow) return;
      try {
        _jcData = await apiFetch('jc-data.php', { jc_record_id: jcRow.record_id });
        setIfEmpty('inv-client-name',    _jcData.client_name);
        setIfEmpty('inv-client-address', _jcData.address);
        setIfEmpty('inv-client-phone',   _jcData.contact_number);
        setValue('inv-drill-m', _jcData.drilling_meters);
        setValue('inv-case-m',  _jcData.casing_meters);
        const banner = document.getElementById('inv-jc-banner');
        if (banner) {
          banner.style.display = '';
          banner.innerHTML = `<strong>JC #${_jcData.jc_no}</strong> — ${esc(_jcData.client_name||'—')}
            &nbsp;·&nbsp; Drilled: <strong>${_jcData.drilling_meters}m</strong>
            &nbsp;·&nbsp; Casing: <strong>${_jcData.casing_meters}m</strong>
            &nbsp;·&nbsp; Water: ${_jcData.water_flow||'—'}
            &nbsp;·&nbsp; <span class="badge badge-navy" style="font-size:.7rem">${_jcData.jc_current_status||'—'}</span>`;
        }
        renderPaymentsPanel(_jcData.payments || [], _jcData.total_paid || 0);
        updateTotals();
      } catch {}
    },
  });

  // Wire add-payment button — works for both JC and pump
  document.getElementById('btn-add-payment-inv')?.addEventListener('click', () => {
    const sourceId = _jcData?._isPump
      ? _jcData.jobcard_no
      : (_jcData?.record_id || inv.jc_record_id);
    if (!sourceId) { showToast('Link a jobcard or pump record first.', 'error'); return; }
    openAddPaymentInvoiceModal(sourceId, (payment) => {
      if (!_jcData) _jcData = { payments: [] };
      if (!_jcData.payments) _jcData.payments = [];
      _jcData.payments.push(payment);
      const total = _jcData.payments.reduce((s,p) => s + (+p.amount||0), 0);
      renderPaymentsPanel(_jcData.payments, total);
    });
  });

  // Pre-fill if coming from jobcard view
  if (prefillJcId) {
    const jcRow = jcList.find(j => String(j.record_id) === String(prefillJcId));
    if (jcRow) invJcRs?.setValue(jcRow.jc_no);
  } else if (inv.jc_no) {
    invJcRs?.setValue(inv.jc_no);
    if (data?.payments) renderPaymentsPanel(data.payments, 0);
  }
  } // end else (not isPump)
  document.getElementById('btn-add-line')?.addEventListener('click', () => {
    document.getElementById('inv-lines')?.insertAdjacentHTML('beforeend', lineRow());
    bindLineEvents();
    updateTotals();
  });

  bindLineEvents();
  updateDrillingSubtotals();
  updateTotals();

  // Wire drilling/casing inputs
  ['inv-drill-m','inv-drill-p','inv-case-m','inv-case-p'].forEach(id => {
    document.getElementById(id)?.addEventListener('input', () => {
      updateDrillingSubtotals(); updateTotals();
    });
  });

  // Wire save/finalise
  const saveBtn     = document.getElementById('btn-save-draft');
  const finaliseBtn = document.getElementById('btn-finalise');

  saveBtn?.addEventListener('click', () => submitInvoice('DRAFT', isEdit, inv, _jcData, pumpData, pumpType));
  finaliseBtn?.addEventListener('click', () => submitInvoice('INVOICED', isEdit, inv, _jcData, pumpData, pumpType));
}

// ── Add/Edit payment modal (invoice page) ────────────────────────────────
function openAddPaymentInvoiceModal(jobcardId, onAdded) {
  openPaymentInvoiceModal({ jobcardId, mode: 'add', onDone: (p) => onAdded(p) });
}

function openEditPaymentInvoiceModal(payment, onUpdated) {
  openPaymentInvoiceModal({ jobcardId: payment.jobcard_id, mode: 'edit', payment, onDone: onUpdated });
}

function openPaymentInvoiceModal({ jobcardId, mode, payment = null, onDone }) {
  const isEdit  = mode === 'edit';
  const hasFile = isEdit && payment?.file;

  openModal(isEdit ? 'Edit Payment' : 'Add Payment', `
    <div class="form-row">
      <div class="form-group">
        <label class="form-label">Payment Method <span style="color:var(--danger)">*</span></label>
        <select class="form-control" id="ipay-method">
          <option value="">— Select —</option>
          <option value="CASH"   ${payment?.payment_method==='CASH'  ?'selected':''}>Cash</option>
          <option value="EFT"    ${payment?.payment_method==='EFT'   ?'selected':''}>EFT</option>
          <option value="CARD"   ${payment?.payment_method==='CARD'  ?'selected':''}>Card</option>
          <option value="CHEQUE" ${payment?.payment_method==='CHEQUE'?'selected':''}>Cheque</option>
        </select>
      </div>
      <div class="form-group">
        <label class="form-label">Amount (R) <span style="color:var(--danger)">*</span></label>
        <input class="form-control" id="ipay-amount" type="number" min="0.01" step="0.01"
               value="${payment?.amount||''}" placeholder="0.00" />
      </div>
    </div>
    <div id="ipay-eft-wrap" style="display:${payment?.payment_method==='EFT'?'':'none'}" class="form-group">
      <label class="form-label">
        Proof of Payment (EFT) <span style="color:var(--danger)">*</span>
        ${hasFile ? `<span style="font-size:.75rem;color:var(--success);margin-left:.5rem">✓ File on record — upload new to replace</span>` : ''}
      </label>
      <input class="form-control" id="ipay-file" type="file" accept="image/*,.pdf" />
      ${hasFile ? `<div style="font-size:.75rem;color:var(--text-muted);margin-top:.25rem">Current: ${payment.file}</div>` : ''}
    </div>
    <div id="ipay-err" class="login-error"></div>
  `, async () => {
    const errEl     = document.getElementById('ipay-err');
    errEl.classList.remove('visible');
    const method    = document.getElementById('ipay-method')?.value;
    const amount    = parseFloat(document.getElementById('ipay-amount')?.value || 0);
    const fileInput = document.getElementById('ipay-file');
    const hasNewFile = fileInput?.files?.length > 0;

    if (!method)   { errEl.textContent = 'Payment method is required.'; errEl.classList.add('visible'); return; }
    if (amount<=0) { errEl.textContent = 'Amount must be greater than 0.'; errEl.classList.add('visible'); return; }
    if (method==='EFT' && !hasNewFile && !(isEdit && payment?.file)) {
      errEl.textContent = 'Proof of payment is required for EFT.'; errEl.classList.add('visible'); return;
    }

    try {
      const formData = new FormData();
      formData.append('token', token());
      formData.append('payment_method', method);
      formData.append('amount', amount);
      if (isEdit) formData.append('record_id', payment.record_id);
      else        formData.append('jobcard_id', jobcardId);
      if (hasNewFile) formData.append('file', fileInput.files[0]);

      const url  = isEdit ? '/api/jobcards/payment-update.php' : '/api/jobcards/payment-add.php';
      const res  = await fetch(url, { method: 'POST', body: formData });
      const json = await res.json();
      if (!json.success) throw new Error(json.message);
      closeModal();
      showToast(isEdit ? 'Payment updated.' : 'Payment added.', 'success');
      onDone(json.data, isEdit);
    } catch (err) { errEl.textContent = err.message; errEl.classList.add('visible'); }
  }, { confirmLabel: isEdit ? 'Save Payment' : 'Add Payment', confirmClass: 'btn-primary' });

  setTimeout(() => {
    document.getElementById('ipay-method')?.addEventListener('change', e => {
      document.getElementById('ipay-eft-wrap').style.display = e.target.value === 'EFT' ? '' : 'none';
    });
  }, 50);
}

// ── Submit invoice ────────────────────────────────────────────────────────
async function submitInvoice(status, isEdit, inv, jcData, pumpData = null, pumpType = null) {
  const errEl = document.getElementById('inv-err');
  if (errEl) { errEl.classList.remove('visible'); }

  const client_name = document.getElementById('inv-client-name')?.value.trim();
  if (!client_name) {
    if (errEl) { errEl.textContent = 'Client name is required.'; errEl.classList.add('visible'); }
    return;
  }

  const items = collectLineItems();

  const payload = {
    status,
    // JC drilling invoice
    jc_record_id:    jcData?._isPump ? 0 : (jcData?.record_id || inv.jc_record_id || 0),
    jc_no:           jcData?._isPump ? 0 : (jcData?.jc_no     || inv.jc_no        || 0),
    // Pump invoice — from selector (_jcData._isPump) or from direct pump navigate (pumpData)
    pump_record_id:  jcData?._isPump
      ? (jcData.pump_record_id || 0)
      : pumpData
        ? (pumpData?.install?.record_id || pumpData?.repair?.record_id || 0)
        : (inv.pump_record_id || 0),
    pump_type:       jcData?._isPump
      ? (jcData.pump_type || '')
      : (pumpType || inv.pump_type || ''),
    client_name,
    client_address:  document.getElementById('inv-client-address')?.value || '',
    client_phone:    document.getElementById('inv-client-phone')?.value   || '',
    client_email:    document.getElementById('inv-client-email')?.value   || '',
    invoice_date:    document.getElementById('inv-date')?.value           || today(),
    due_date:        document.getElementById('inv-due')?.value            || '',
    notes:           document.getElementById('inv-notes')?.value          || '',
    drilling_meters: document.getElementById('inv-drill-m')?.value        || 0,
    drilling_price:  document.getElementById('inv-drill-p')?.value        || 0,
    casing_meters:   document.getElementById('inv-case-m')?.value         || 0,
    casing_price:    document.getElementById('inv-case-p')?.value         || 0,
    items:           JSON.stringify(items),
  };

  try {
    let res;
    if (isEdit) {
      res = await apiPost('update.php', { id: inv.record_id, invoice_no: inv.invoice_no, ...payload });
      showToast('Invoice saved.', 'success');
    } else {
      res = await apiPost('create.php', payload);
      showToast(`${res.invoice_no} created.`, 'success');
    }
    navigate('invoice-view', { id: isEdit ? inv.record_id : res.record_id });
  } catch (err) {
    if (errEl) { errEl.textContent = err.message; errEl.classList.add('visible'); }
  }
}

// ═════════════════════════════════════════════════════════════════════════
// INVOICE READONLY VIEW
// ═════════════════════════════════════════════════════════════════════════
function renderInvoiceReadonly(data, isAdmin) {
  const { invoice: inv, items, jobcard: jc, pump, payments } = data;
  const c = document.getElementById('view-content');

  const subtotal_items = items.reduce((s, i) => s + (+i.total||0), 0);
  const drill_sub = (+inv.drilling_meters||0) * (+inv.drilling_price||0);
  const case_sub  = (+inv.casing_meters||0)   * (+inv.casing_price||0);
  const nett      = subtotal_items + drill_sub + case_sub;
  const vat       = nett * VAT;
  const total     = nett + vat;
  const paid      = payments.reduce((s, p) => s + (+p.amount||0), 0);
  const remaining = Math.max(0, total - paid);

  // Payment source ID — jc record id (int) or pump jobcard_no (string like PI_1)
  const paymentSourceId = jc?.record_id || pump?.jobcard_no || null;

  const statusBadge = s => {
    const map = { DRAFT:'badge-pending', INVOICED:'badge-active', PAID:'badge-complete', UNPAID:'badge-active' };
    return `<span class="badge ${map[s]||'badge-navy'}">${s}</span>`;
  };

  c.innerHTML = `
  <div class="page-header">
    <div>
      <h1>${inv.invoice_no}</h1>
      <div class="sub">${statusBadge(inv.status)}
        ${jc   ? `· JC #${jc.jc_no}` : ''}
        ${pump ? `· ${pump.pump_type?.toUpperCase()} ${pump.jobcard_no}` : ''}
      </div>
    </div>
    <div style="display:flex;gap:.5rem;flex-wrap:wrap">
      ${isAdmin && inv.status === 'DRAFT' ? `<button class="btn btn-primary" id="btn-edit-inv">Edit</button>` : ''}
      <button class="btn btn-accent btn-sm" id="btn-pdf">⬇ PDF</button>
      <button class="btn btn-ghost" id="btn-back">← Back</button>
    </div>
  </div>

  ${jc ? `
  <div style="background:var(--info-bg);border:1px solid var(--info);border-radius:var(--radius);
              padding:.75rem 1rem;margin-bottom:1rem;font-size:.875rem">
    <strong>Drilling Jobcard:</strong> #${jc.jc_no} — ${esc(jc.client_name||'—')}
    &nbsp;·&nbsp; ${jc.jc_current_status}
    &nbsp;·&nbsp; Drilled: ${jc.drilling_meters}m  Casing: ${jc.casing_meters}m
  </div>` : ''}

  ${pump ? `
  <div style="background:var(--surface-2);border:1px solid var(--border);border-radius:var(--radius);
              padding:.75rem 1rem;margin-bottom:1rem;font-size:.875rem">
    <strong>Pump ${pump.pump_type === 'install' ? 'Installation' : 'Repair'}:</strong>
    ${pump.jobcard_no} — ${esc(pump.client_name||'—')}
    &nbsp;·&nbsp; ${pump.jc_current_status||'—'}
  </div>` : ''}

  <div class="section-grid wide">
    <div style="display:flex;flex-direction:column;gap:1rem">

      <!-- Line items table -->
      <div class="card">
        <div class="card-header"><span class="card-title">Invoice Items</span></div>
        <div class="card-body" style="padding:0">
          <div class="table-wrap"><table>
            <thead><tr><th>Description</th><th>Qty</th><th>Unit Price</th><th>Total</th></tr></thead>
            <tbody>
              ${(+inv.drilling_meters > 0 && +inv.drilling_price > 0) ? `<tr>
                <td>Drilling (${inv.drilling_meters}m @ R${fmt(inv.drilling_price)}/m)</td>
                <td>1</td><td>R ${fmt(drill_sub)}</td><td>R ${fmt(drill_sub)}</td>
              </tr>` : ''}
              ${(+inv.casing_meters > 0 && +inv.casing_price > 0) ? `<tr>
                <td>Casing (${inv.casing_meters}m @ R${fmt(inv.casing_price)}/m)</td>
                <td>1</td><td>R ${fmt(case_sub)}</td><td>R ${fmt(case_sub)}</td>
              </tr>` : ''}
              ${items.map(li => `<tr>
                <td>${esc(li.description)}</td>
                <td>${li.quantity}</td>
                <td>R ${fmt(li.unit_price)}</td>
                <td><strong>R ${fmt(li.total)}</strong></td>
              </tr>`).join('')}
            </tbody>
          </table></div>
        </div>
      </div>

      <!-- Payments -->
      <div class="card">
        <div class="card-header">
          <span class="card-title">Payments Received</span>
          ${isAdmin && paymentSourceId && inv.status !== 'PAID'
            ? `<button class="btn btn-ghost btn-sm" id="btn-add-payment-view" style="margin-left:auto">+ Add Payment</button>`
            : ''}
        </div>
        <div id="view-payments-body" class="card-body" style="padding:0">
          ${payments.length ? `<div class="table-wrap"><table>
            <thead><tr><th>Date</th><th>Method</th><th>User</th><th>Amount</th><th>Proof</th></tr></thead>
            <tbody id="view-payments-rows">
              ${payments.map(p => `<tr>
                <td style="font-size:.8rem;color:var(--text-muted)">${(p.date_time||'').slice(0,10)}</td>
                <td>${p.payment_method||'—'}</td>
                <td>${p.username||'—'}</td>
                <td><strong style="color:var(--success)">R ${fmt(p.amount)}</strong></td>
                <td>${p.file
                  ? `<a href="/uploads/payments/${p.file}" target="_blank"
                       style="font-size:.78rem;color:var(--primary);font-weight:600">View ↗</a>`
                  : '<span style="color:var(--text-muted);font-size:.78rem">—</span>'}</td>
              </tr>`).join('')}
            </tbody>
          </table></div>` : `<div class="empty-state" style="padding:1.5rem"><p>No payments recorded</p></div>`}
        </div>
      </div>

    </div>

    <div style="display:flex;flex-direction:column;gap:1rem">

      <!-- Client -->
      <div class="card">
        <div class="card-header"><span class="card-title">Bill To</span></div>
        <div class="card-body">
          <div style="font-weight:700;margin-bottom:.35rem">${esc(inv.client_name)}</div>
          ${inv.client_address ? `<div style="font-size:.875rem">${esc(inv.client_address)}</div>` : ''}
          ${inv.client_phone   ? `<div style="font-size:.875rem">${esc(inv.client_phone)}</div>`   : ''}
          ${inv.client_email   ? `<div style="font-size:.875rem">${esc(inv.client_email)}</div>`   : ''}
        </div>
      </div>

      <!-- Totals -->
      <div class="card">
        <div class="card-header"><span class="card-title">Totals</span></div>
        <div class="card-body">
          ${totalRow('Subtotal',        nett,      '')}
          ${totalRow('VAT (15%)',        vat,       '')}
          ${totalRow('TOTAL',            total,     'font-weight:800;font-size:1.1rem;border-top:2px solid var(--border)')}
          ${totalRow('Payments Received',paid,      'color:var(--success)')}
          ${totalRow('Balance Due',      remaining, `font-weight:800;color:${remaining>0?'var(--danger)':'var(--success)'};font-size:1.05rem`)}
        </div>
      </div>

      ${inv.notes ? `
      <div class="card">
        <div class="card-header"><span class="card-title">Notes</span></div>
        <div class="card-body" style="font-size:.875rem">${esc(inv.notes)}</div>
      </div>` : ''}

    </div>
  </div>`;

  document.getElementById('btn-back')?.addEventListener('click', () => navigate('invoices'));
  document.getElementById('btn-edit-inv')?.addEventListener('click', () => {
    let jcList = [];
    fetch(`/api/jobcards/list.php?token=${token()}`).then(r=>r.json()).then(j => {
      if (j.success) jcList = j.data;
      renderInvoiceForm(data, null, jcList);
    });
  });
  // Add payment from readonly view
  let _livePayments = [...payments];
  document.getElementById('btn-add-payment-view')?.addEventListener('click', () => {
    if (!paymentSourceId) { showToast('No linked jobcard or pump record.', 'error'); return; }
    openAddPaymentInvoiceModal(paymentSourceId, (payment) => {
      _livePayments.push(payment);
      const tbody = document.getElementById('view-payments-rows');
      const body  = document.getElementById('view-payments-body');
      if (tbody) {
        tbody.insertAdjacentHTML('beforeend', `<tr>
          <td style="font-size:.8rem;color:var(--text-muted)">${(payment.date_time||'').slice(0,10)}</td>
          <td>${payment.payment_method||'—'}</td>
          <td>${payment.username||'—'}</td>
          <td><strong style="color:var(--success)">R ${fmt(payment.amount)}</strong></td>
          <td>${payment.file ? `<a href="/uploads/payments/${payment.file}" target="_blank" style="font-size:.78rem;color:var(--primary);font-weight:600">View ↗</a>` : '<span style="color:var(--text-muted);font-size:.78rem">—</span>'}</td>
        </tr>`);
      } else if (body) {
        body.innerHTML = `<div class="table-wrap"><table>
          <thead><tr><th>Date</th><th>Method</th><th>User</th><th>Amount</th><th>Proof</th></tr></thead>
          <tbody id="view-payments-rows">
            ${_livePayments.map(p => `<tr>
              <td style="font-size:.8rem;color:var(--text-muted)">${(p.date_time||'').slice(0,10)}</td>
              <td>${p.payment_method||'—'}</td><td>${p.username||'—'}</td>
              <td><strong style="color:var(--success)">R ${fmt(p.amount)}</strong></td>
              <td>${p.file ? `<a href="/uploads/payments/${p.file}" target="_blank" style="font-size:.78rem;color:var(--primary);font-weight:600">View ↗</a>` : '<span style="color:var(--text-muted);font-size:.78rem">—</span>'}</td>
            </tr>`).join('')}
          </tbody></table></div>`;
      }
    });
  });

  document.getElementById('btn-pdf')?.addEventListener('click', () =>
    generateInvoicePDF(inv, items, jc, _livePayments, { nett, vat, total, paid, remaining }, pump)
  );
}

// ═════════════════════════════════════════════════════════════════════════
// PDF GENERATION (jsPDF)
// ═════════════════════════════════════════════════════════════════════════
async function generateInvoicePDF(inv, items, jc, payments, totals, pump = null) {
  await loadScript('https://cdnjs.cloudflare.com/ajax/libs/jspdf/2.5.1/jspdf.umd.min.js', 'jspdf');
  await loadScript('https://cdnjs.cloudflare.com/ajax/libs/jspdf-autotable/3.8.2/jspdf.plugin.autotable.min.js', null);

  const jsPDFCtor = window.jspdf?.jsPDF || window.jsPDF;
  if (!jsPDFCtor) { showToast('PDF library failed to load.', 'error'); return; }
  const doc = new jsPDFCtor({ unit: 'mm', format: 'a4' });
  if (typeof doc.autoTable !== 'function') { showToast('autoTable not loaded.', 'error'); return; }

  const NAVY   = [40, 60, 140];
  const ORANGE = [240, 120, 40];
  const LIGHT  = [245, 247, 250];
  const W = 210, M = 15;

  // ── Header bar (taller to accommodate logo)
  doc.setFillColor(...NAVY);
  doc.rect(0, 0, W, 36, 'F');

  // Try loading logo — strictly capped at 55mm wide, 26mm tall
  try {
    await new Promise((resolve) => {
      const img = new Image();
      img.crossOrigin = 'anonymous';
      img.onload = () => {
        try {
          const MAX_W = 55, MAX_H = 26;
          const canvas = document.createElement('canvas');
          canvas.width  = img.naturalWidth  || img.width  || 400;
          canvas.height = img.naturalHeight || img.height || 200;
          canvas.getContext('2d').drawImage(img, 0, 0, canvas.width, canvas.height);
          const aspect = canvas.width / canvas.height;
          let lw = MAX_W, lh = lw / aspect;
          if (lh > MAX_H) { lh = MAX_H; lw = lh * aspect; }
          const lx = W - M - lw;
          const ly = (36 - lh) / 2;
          doc.addImage(canvas.toDataURL('image/png'), 'PNG', lx, ly, lw, lh);
        } catch {}
        resolve();
      };
      img.onerror = resolve;
      img.src = '/assets/img/logo.png';
    });
  } catch {}

  // "INVOICE" label — bottom-left of header bar
  doc.setTextColor(255, 255, 255);
  doc.setFont('helvetica', 'bold'); doc.setFontSize(20);
  doc.text('INVOICE', M, 24);
  doc.setFont('helvetica', 'normal'); doc.setFontSize(8);
  doc.text('Savuki Drilling (Pty) Ltd', M, 30);

  let y = 44;

  // ── Orange accent line below header
  doc.setDrawColor(...ORANGE); doc.setLineWidth(0.8);
  doc.line(M, 37, W - M, 37);

  // ── Invoice ref + dates row
  doc.setTextColor(60, 60, 60); doc.setFont('helvetica', 'bold'); doc.setFontSize(9);
  doc.text(inv.invoice_no, M, y);
  doc.setFont('helvetica', 'normal');
  doc.text(`Date: ${inv.invoice_date || ''}`, 90, y);
  if (inv.due_date) doc.text(`Due: ${inv.due_date}`, W - M, y, { align: 'right' });
  y += 8;

  // ── Two-column: Bill To | Invoice Details
  doc.setFillColor(...LIGHT);
  doc.rect(M, y, W - M*2, 6, 'F');
  doc.setFont('helvetica','bold'); doc.setFontSize(8); doc.setTextColor(40,40,40);
  doc.text('BILL TO', M + 2, y + 4);
  doc.text('INVOICE DETAILS', 120, y + 4);
  y += 8;

  const clientStart = y;
  doc.setFont('helvetica','bold'); doc.setFontSize(10); doc.setTextColor(20,20,20);
  doc.text(inv.client_name || '—', M, y);
  y += 5;
  doc.setFont('helvetica','normal'); doc.setFontSize(9);
  if (inv.client_address) { doc.text(inv.client_address, M, y); y += 4; }
  if (inv.client_phone)   { doc.text(inv.client_phone,   M, y); y += 4; }
  if (inv.client_email)   { doc.text(inv.client_email,   M, y); y += 4; }

  // Meta on right (aligned to client start)
  let ym = clientStart;
  const meta = [
    ['Status:', inv.status],
    ...(jc   ? [['JC #:', String(jc.jc_no)]] : []),
    ...(pump  ? [['Pump Ref:', `${(pump.pump_type||'').toUpperCase()} ${pump.jobcard_no}`]] : []),
  ];
  meta.forEach(([label, val]) => {
    doc.setFont('helvetica','bold');   doc.setFontSize(9); doc.setTextColor(80,80,80);
    doc.text(label, 120, ym);
    doc.setFont('helvetica','normal'); doc.setTextColor(20,20,20);
    doc.text(val||'—', 145, ym);
    ym += 5;
  });

  y = Math.max(y, ym) + 4;

  // ── Drilling/Casing summary bar (if applicable)
  const drillSub = (+inv.drilling_meters||0) * (+inv.drilling_price||0);
  const caseSub  = (+inv.casing_meters||0)   * (+inv.casing_price||0);
  if (drillSub > 0 || caseSub > 0) {
    doc.setFillColor(...LIGHT);
    doc.rect(M, y, W - M*2, 10, 'F');
    doc.setFont('helvetica','normal'); doc.setFontSize(8.5); doc.setTextColor(40,40,40);
    let bx = M + 3;
    if (drillSub > 0) {
      doc.text(`Drilling: ${inv.drilling_meters}m × R${fmt(inv.drilling_price)} = R${fmt(drillSub)}`, bx, y + 6);
      bx += 80;
    }
    if (caseSub > 0) {
      doc.text(`Casing: ${inv.casing_meters}m × R${fmt(inv.casing_price)} = R${fmt(caseSub)}`, bx, y + 6);
    }
    y += 14;
  }

  // ── Line items table
  const allLines = [
    ...(drillSub > 0 ? [{ description: `Drilling (${inv.drilling_meters}m)`, quantity: 1, unit_price: drillSub, total: drillSub }] : []),
    ...(caseSub  > 0 ? [{ description: `Casing (${inv.casing_meters}m)`,     quantity: 1, unit_price: caseSub,  total: caseSub  }] : []),
    ...items,
  ];

  doc.autoTable({
    startY: y,
    head: [['Description', 'Qty', 'Unit Price (R)', 'Total (R)']],
    body: allLines.map(li => [
      li.description,
      li.quantity,
      'R ' + fmt(li.unit_price),
      'R ' + fmt(li.total),
    ]),
    theme: 'grid',
    headStyles: { fillColor: NAVY, textColor: [255,255,255], fontStyle:'bold', fontSize:9 },
    styles: { fontSize: 9, cellPadding: 2.5 },
    alternateRowStyles: { fillColor: LIGHT },
    columnStyles: { 1:{halign:'center'}, 2:{halign:'right'}, 3:{halign:'right'} },
  });

  y = doc.lastAutoTable.finalY + 6;

  // ── Totals block (right aligned)
  const totalsData = [
    ['Subtotal (excl. VAT)', totals.nett],
    ['VAT (15%)',             totals.vat],
    ['TOTAL',                 totals.total],
    ...(payments.length ? [['Payments Received', -totals.paid]] : []),
    ...(totals.remaining > 0 ? [['BALANCE DUE', totals.remaining]] : []),
  ];

  totalsData.forEach(([label, val], i) => {
    const isTotal    = label === 'TOTAL' || label === 'BALANCE DUE';
    const isPaid     = label === 'Payments Received';
    const rowH = 8;
    if (isTotal) { doc.setFillColor(...NAVY); doc.rect(M + 90, y, W - M - 90, rowH, 'F'); }
    else if (isPaid) { doc.setFillColor(220,250,220); doc.rect(M + 90, y, W - M - 90, rowH, 'F'); }
    doc.setFont('helvetica', isTotal ? 'bold' : 'normal');
    doc.setFontSize(isTotal ? 10 : 9);
    doc.setTextColor(isTotal ? 255 : isPaid ? 22 : 40, isTotal ? 255 : isPaid ? 163 : 40, isTotal ? 255 : isPaid ? 74 : 40);
    doc.text(label, M + 92, y + 5.5);
    doc.text('R ' + fmt(Math.abs(val)), W - M - 2, y + 5.5, { align:'right' });
    y += rowH;
  });

  // ── Payments table (if any)
  if (payments.length) {
    y += 6;
    doc.setFont('helvetica','bold'); doc.setFontSize(9); doc.setTextColor(...NAVY);
    doc.text('PAYMENTS RECEIVED', M, y); y += 4;
    doc.autoTable({
      startY: y,
      head: [['Date', 'Method', 'By', 'Amount (R)']],
      body: payments.map(p => [(p.date_time||'').slice(0,10), p.payment_method||'—', p.username||'—', 'R '+fmt(p.amount)]),
      theme: 'grid',
      headStyles: { fillColor: [22,163,74], textColor:[255,255,255], fontStyle:'bold', fontSize:8 },
      styles: { fontSize: 8, cellPadding: 2 },
      columnStyles: { 3:{halign:'right'} },
    });
    y = doc.lastAutoTable.finalY + 4;
  }

  // ── Notes
  if (inv.notes) {
    y += 4;
    doc.setFont('helvetica','normal'); doc.setFontSize(8.5); doc.setTextColor(80,80,80);
    doc.setFont('helvetica','bold'); doc.text('Notes:', M, y); y += 4;
    doc.setFont('helvetica','normal');
    doc.text(inv.notes, M, y, { maxWidth: W - M*2 });
  }

  // ── Footer
  const pageH = doc.internal.pageSize.getHeight();
  doc.setFontSize(7); doc.setTextColor(150,150,150);
  doc.text('Savuki Drilling  |  Generated: ' + new Date().toLocaleString('en-ZA'), W/2, pageH - 8, { align:'center' });

  doc.save(`Savuki_${inv.invoice_no}.pdf`);
  showToast('PDF downloaded.', 'success');
}

// ═════════════════════════════════════════════════════════════════════════
// HELPERS
// ═════════════════════════════════════════════════════════════════════════
function lineRow(li = {}) {
  return `<tr class="inv-line-row">
    <td data-label="Description">
      <input class="form-control inv-desc" value="${esc(li.description||'')}" placeholder="Description…" />
    </td>
    <td data-label="Qty">
      <input class="form-control inv-qty" type="number" step="0.01" min="0"
             value="${li.quantity||1}" style="width:70px" />
    </td>
    <td data-label="Unit Price">
      <input class="form-control inv-price" type="number" step="0.01" min="0"
             value="${li.unit_price||''}" style="width:100px" />
    </td>
    <td data-label="Total">
      <strong class="inv-line-total">R ${fmt((+li.quantity||0) * (+li.unit_price||0))}</strong>
    </td>
    <td>
      <button class="btn btn-ghost btn-sm inv-del-line" style="color:var(--danger)" type="button">✕</button>
    </td>
  </tr>`;
}

function bindLineEvents() {
  document.querySelectorAll('.inv-line-row').forEach(row => {
    if (row._bound) return;
    row._bound = true;
    const calc = () => {
      const qty   = parseFloat(row.querySelector('.inv-qty')?.value   || 0);
      const price = parseFloat(row.querySelector('.inv-price')?.value || 0);
      const total = row.querySelector('.inv-line-total');
      if (total) total.textContent = 'R ' + fmt(qty * price);
      updateTotals();
    };
    row.querySelector('.inv-qty')?.addEventListener('input', calc);
    row.querySelector('.inv-price')?.addEventListener('input', calc);
    row.querySelector('.inv-del-line')?.addEventListener('click', () => { row.remove(); updateTotals(); });
  });
}

function collectLineItems() {
  const rows = document.querySelectorAll('.inv-line-row');
  const items = [];
  rows.forEach(row => {
    const desc  = row.querySelector('.inv-desc')?.value.trim()    || '';
    const qty   = parseFloat(row.querySelector('.inv-qty')?.value  || 0);
    const price = parseFloat(row.querySelector('.inv-price')?.value || 0);
    if (desc) items.push({ description: desc, quantity: qty, unit_price: price });
  });
  return items;
}

function updateDrillingSubtotals() {
  const dm = parseFloat(document.getElementById('inv-drill-m')?.value || 0);
  const dp = parseFloat(document.getElementById('inv-drill-p')?.value || 0);
  const cm = parseFloat(document.getElementById('inv-case-m')?.value  || 0);
  const cp = parseFloat(document.getElementById('inv-case-p')?.value  || 0);
  const ds = document.getElementById('inv-drill-sub');
  const cs = document.getElementById('inv-case-sub');
  if (ds) ds.textContent = `R ${fmt(dm * dp)}`;
  if (cs) cs.textContent = `R ${fmt(cm * cp)}`;
}

function updateTotals() {
  const dm = parseFloat(document.getElementById('inv-drill-m')?.value || 0);
  const dp = parseFloat(document.getElementById('inv-drill-p')?.value || 0);
  const cm = parseFloat(document.getElementById('inv-case-m')?.value  || 0);
  const cp = parseFloat(document.getElementById('inv-case-p')?.value  || 0);

  let itemsTotal = 0;
  document.querySelectorAll('.inv-line-row').forEach(row => {
    const qty   = parseFloat(row.querySelector('.inv-qty')?.value   || 0);
    const price = parseFloat(row.querySelector('.inv-price')?.value || 0);
    itemsTotal += qty * price;
  });

  const nett      = (dm * dp) + (cm * cp) + itemsTotal;
  const vat       = nett * VAT;
  const total     = nett + vat;
  const paidEl    = document.getElementById('inv-payments-paid');
  const paid      = paidEl ? parseFloat(paidEl.dataset.paid || 0) : 0;
  const remaining = Math.max(0, total - paid);

  const tb = document.getElementById('inv-totals-body');
  if (tb) tb.innerHTML = renderTotalsHtml(nett, vat, total, paid, remaining);
}

function renderTotalsHtml(nett, vat, total, paid = 0, remaining = null) {
  const rem = remaining !== null ? remaining : Math.max(0, total - paid);
  return `
  <div id="inv-payments-paid" data-paid="${paid}" style="display:none"></div>
  ${totalRow('Subtotal (excl. VAT)', nett, '')}
  ${totalRow('VAT (15%)', vat, 'color:var(--text-muted)')}
  ${totalRow('TOTAL', total, 'font-weight:800;font-size:1.1rem;border-top:2px solid var(--border);margin-top:.25rem;padding-top:.5rem')}
  ${paid > 0 ? totalRow('Payments Received', paid, 'color:var(--success)') : ''}
  ${paid > 0 ? totalRow('Balance Due', rem, `font-weight:800;color:${rem>0?'var(--danger)':'var(--success)'}`) : ''}`;
}

function totalRow(label, val, style = '') {
  return `<div style="display:flex;justify-content:space-between;padding:.35rem 0;${style}">
    <span>${label}</span>
    <strong>R ${fmt(val)}</strong>
  </div>`;
}

function renderPaymentsPanel(payments, totalPaid) {
  const body = document.getElementById('inv-payments-body');
  if (!body) return;
  const paidEl = document.getElementById('inv-payments-paid');
  if (paidEl) paidEl.dataset.paid = totalPaid;
  if (!payments.length) {
    body.innerHTML = `<div class="empty-state" style="padding:1.5rem"><p style="font-size:.82rem">No payments recorded</p></div>`;
    return;
  }
  body.innerHTML = `<div class="table-wrap"><table>
    <thead><tr><th>Date</th><th>Method</th><th>Amount</th><th>Proof</th><th></th></tr></thead>
    <tbody>
      ${payments.map(p => `<tr>
        <td style="font-size:.78rem;color:var(--text-muted)">${(p.date_time||'').slice(0,10)}</td>
        <td>${p.payment_method||'—'}</td>
        <td><strong style="color:var(--success)">R ${fmt(p.amount)}</strong></td>
        <td>${p.file
          ? `<a href="/uploads/payments/${p.file}" target="_blank" style="font-size:.75rem;color:var(--primary)">View ↗</a>`
          : '<span style="color:var(--text-muted);font-size:.75rem">—</span>'}</td>
        <td><button class="btn btn-ghost btn-sm ipay-edit-btn" style="font-size:.72rem"
            data-payment="${encodeURIComponent(JSON.stringify(p))}">Edit</button></td>
      </tr>`).join('')}
      <tr style="border-top:2px solid var(--border)">
        <td colspan="3"><strong>Total Paid</strong></td>
        <td colspan="2"><strong style="color:var(--success)">R ${fmt(totalPaid)}</strong></td>
      </tr>
    </tbody>
  </table></div>`;

  // Bind edit buttons — need access to the payments array ref via closure
  body.querySelectorAll('.ipay-edit-btn').forEach(btn => {
    btn.addEventListener('click', () => {
      const p = JSON.parse(decodeURIComponent(btn.dataset.payment));
      openEditPaymentInvoiceModal(p, (updated, isEdit) => {
        const idx = payments.findIndex(x => x.record_id == updated.record_id);
        if (idx > -1) payments[idx] = updated;
        const total = payments.reduce((s, x) => s + (+x.amount||0), 0);
        renderPaymentsPanel(payments, total);
        updateTotals();
      });
    });
  });

  updateTotals();
}

function today() { return new Date().toISOString().slice(0,10); }
function fmt(v) { return (+v||0).toLocaleString('en-ZA', {minimumFractionDigits:2, maximumFractionDigits:2}); }
function setIfEmpty(id, val) { const el = document.getElementById(id); if (el && !el.value) el.value = val||''; }
function setValue(id, val)   { const el = document.getElementById(id); if (el) el.value = val||0; }
function esc(v) { return (v||'').toString().replace(/"/g,'&quot;').replace(/</g,'&lt;'); }
function debounce(fn, ms) { let t; return (...a) => { clearTimeout(t); t = setTimeout(()=>fn(...a), ms); }; }

function loadScript(src, globalCheck) {
  return new Promise(resolve => {
    if (globalCheck && window[globalCheck]) { resolve(); return; }
    if (document.querySelector(`[src="${src}"]`)) {
      if (!globalCheck) { resolve(); return; }
      let n = 0; const p = setInterval(()=>{ if (window[globalCheck]||++n>40){ clearInterval(p); resolve(); }}, 100);
      return;
    }
    const s = document.createElement('script'); s.src = src;
    s.onload = () => {
      if (!globalCheck) { resolve(); return; }
      let n = 0; const p = setInterval(()=>{ if (window[globalCheck]||++n>40){ clearInterval(p); resolve(); }}, 100);
    };
    s.onerror = resolve;
    document.head.appendChild(s);
  });
}