<?php
declare(strict_types=1);

/**
 * modules/accounts/ledger.php
 * General Ledger browser for `transactions`.
 */

require_once dirname(__DIR__, 2) . '/config/functions.php';
require_role(['MD','Accounts','Admin','Management']);

$user = current_user();
$cid  = (int)($user['company_id'] ?? 0);
if ($cid <= 0) redirect(url_public('login.php'));

$errors = [];
$notice = null;

/* ---------------------------------
   Helpers
----------------------------------*/
function tbl_exists(string $t): bool {
  try { db()->query("SELECT 1 FROM `{$t}` LIMIT 1"); return true; }
  catch (Throwable) { return false; }
}

/** Build CASE expression for "signed amount" based on type */
function sql_signed_amount(string $colAmount='t.amount', string $colType='t.type'): string {
  return "CASE
    WHEN {$colType}='income' THEN {$colAmount}
    WHEN {$colType}='expense' THEN -{$colAmount}
    WHEN {$colType}='adjustment' THEN {$colAmount}
    ELSE 0
  END";
}

/** Safe IN (...) helper */
function sql_in_clause(array $vals, string $prefix): array {
  $placeholders = [];
  $args = [];
  $i = 0;
  foreach ($vals as $v) {
    $ph = ":" . $prefix . $i++;
    $placeholders[] = $ph;
    $args[$ph] = $v;
  }
  return [implode(',', $placeholders), $args];
}

if (!tbl_exists('transactions')) {
  $errors[] = 'The `transactions` table is missing.';
}

/* ---------------------------------
   Filters & Inputs
----------------------------------*/
$today      = new DateTimeImmutable('today');
$monthStart = $today->modify('first day of this month')->format('Y-m-d');
$monthEnd   = $today->modify('last day of this month')->format('Y-m-d');

$from = (string)($_GET['from'] ?? $monthStart);
$to   = (string)($_GET['to']   ?? $monthEnd);
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/',$from)) $from = $monthStart;
if (!preg_match('/^\d{4}-\d{2}-\d{2}$/',$to))   $to   = $monthEnd;

$accSel = $_GET['account_code'] ?? [];
$segSel = $_GET['segment']      ?? [];
$typeSel= $_GET['type']         ?? [];
$amountMin = trim((string)($_GET['amount_min'] ?? ''));
$amountMax = trim((string)($_GET['amount_max'] ?? ''));
$hasAttach = (string)($_GET['has_attachment'] ?? '');
$q         = trim((string)($_GET['q'] ?? ''));
$bookRefQ  = trim((string)($_GET['booking_ref'] ?? ''));

$export    = (string)($_GET['export'] ?? '');
$sort      = (string)($_GET['sort'] ?? 'date_desc');
$page      = max(1, (int)($_GET['page'] ?? 1));
$perPage   = min(200, max(10, (int)($_GET['per_page'] ?? 50)));

$allowedTypes = ['income','expense','transfer','adjustment'];

$accSel = is_array($accSel) ? array_values(array_filter(array_map('strval',$accSel))) : [];
$segSel = is_array($segSel) ? array_values(array_filter(array_map('strval',$segSel))) : [];
$typeSel= is_array($typeSel)? array_values(array_filter(array_map('strval',$typeSel), fn($t)=>in_array($t,$allowedTypes,true))) : [];

$singleAccount = (count($accSel) === 1);
if ($singleAccount) $sort = 'date_asc';

/* ---------------------------------
   Dropdown data
----------------------------------*/
$accountCodes = [];
$segments = [];
if (!$errors) {
  try {
    $q1 = db()->prepare("SELECT DISTINCT account_code FROM transactions WHERE company_id=:cid AND account_code IS NOT NULL AND account_code!='' ORDER BY account_code");
    $q1->execute([':cid'=>$cid]); $accountCodes = array_map(fn($r)=>$r['account_code'], $q1->fetchAll());
  } catch (Throwable) {}
  try {
    $q2 = db()->prepare("SELECT DISTINCT segment FROM transactions WHERE company_id=:cid AND segment IS NOT NULL AND segment!='' ORDER BY segment");
    $q2->execute([':cid'=>$cid]); $segments = array_map(fn($r)=>$r['segment'], $q2->fetchAll());
  } catch (Throwable) {}
}

/* ---------------------------------
   WHERE builder
----------------------------------*/
$where = ["t.company_id = :cid", "t.date BETWEEN :from AND :to"];
$args  = [':cid'=>$cid, ':from'=>$from, ':to'=>$to];

if ($accSel) {
  [$in, $a] = sql_in_clause($accSel, 'acc');
  $where[] = "t.account_code IN ($in)";
  $args = array_merge($args, $a);
}
if ($segSel) {
  [$in, $a] = sql_in_clause($segSel, 'seg');
  $where[] = "t.segment IN ($in)";
  $args = array_merge($args, $a);
}
if ($typeSel) {
  [$in, $a] = sql_in_clause($typeSel, 'typ');
  $where[] = "t.type IN ($in)";
  $args = array_merge($args, $a);
}
if ($amountMin !== '' && is_numeric($amountMin)) {
  $where[] = "t.amount >= :amin";
  $args[':amin'] = (float)$amountMin;
}
if ($amountMax !== '' && is_numeric($amountMax)) {
  $where[] = "t.amount <= :amax";
  $args[':amax'] = (float)$amountMax;
}
if ($hasAttach === '1') {
  $where[] = "t.attachment_url IS NOT NULL AND t.attachment_url <> ''";
} elseif ($hasAttach === '0') {
  $where[] = "(t.attachment_url IS NULL OR t.attachment_url = '')";
}
if ($q !== '') {
  $where[] = "(t.reference LIKE :qq OR t.notes LIKE :qq)";
  $args[':qq'] = '%'.$q.'%';
}

$joinBookings = false;
if ($bookRefQ !== '' && tbl_exists('bookings')) {
  $joinBookings = true;
  $where[] = "(b.booking_ref LIKE :brq)";
  $args[':brq'] = '%'.$bookRefQ.'%';
}

/* ---------------------------------
   KPIs
----------------------------------*/
$kpi = ['opening'=>0.0,'debits'=>0.0,'credits'=>0.0,'net'=>0.0,'closing'=>0.0,'count'=>0];

if (!$errors) {
  try {
    // Opening: same filters except date -> strictly before :from
    $wOpen = $where;
    foreach ($wOpen as $i=>$w) {
      if (strpos($w, 't.date BETWEEN') !== false) $wOpen[$i] = "t.date < :from";
    }
    $sqlOpen = "SELECT COALESCE(SUM(" . sql_signed_amount() . "),0) AS open_sum
                FROM transactions t
                ".($joinBookings ? "LEFT JOIN bookings b ON b.id=t.booking_id AND b.company_id=t.company_id" : "")."
                WHERE ".implode(' AND ', $wOpen);
    $st = db()->prepare($sqlOpen);
    $argsOpen = $args;            // <-- FIX: remove :to for this query
    unset($argsOpen[':to']);
    $st->execute($argsOpen);
    $kpi['opening'] = (float)$st->fetchColumn();

    // In-range debits & credits & count
    $sqlIn = "SELECT
                COALESCE(SUM(CASE WHEN t.type='expense' THEN t.amount ELSE 0 END),0) AS debits,
                COALESCE(SUM(CASE WHEN t.type='income'  THEN t.amount ELSE 0 END),0) AS credits,
                COUNT(*) AS cnt
              FROM transactions t
              ".($joinBookings ? "LEFT JOIN bookings b ON b.id=t.booking_id AND b.company_id=t.company_id" : "")."
              WHERE ".implode(' AND ', $where);
    $st2 = db()->prepare($sqlIn); $st2->execute($args);
    $row = $st2->fetch() ?: ['debits'=>0,'credits'=>0,'cnt'=>0];
    $kpi['debits']  = (float)$row['debits'];
    $kpi['credits'] = (float)$row['credits'];
    $kpi['count']   = (int)$row['cnt'];

    $kpi['net']     = $kpi['credits'] - $kpi['debits'];
    $kpi['closing'] = $kpi['opening'] + $kpi['net'];
  } catch (Throwable $e) {
    $errors[] = (defined('APP_ENV') && APP_ENV==='dev') ? $e->getMessage() : 'Unable to compute KPIs.';
  }
}

/* ---------------------------------
   Query rows (with pagination)
----------------------------------*/
$totalRows = 0;
$rows = [];
$order = ($sort === 'date_asc') ? "t.date ASC, t.id ASC" : "t.date DESC, t.id DESC";
$offset = ($page - 1) * $perPage;

if (!$errors) {
  try {
    $sqlCnt = "SELECT COUNT(*) FROM transactions t
               ".($joinBookings ? "LEFT JOIN bookings b ON b.id=t.booking_id AND b.company_id=t.company_id" : "")."
               WHERE ".implode(' AND ', $where);
    $sc = db()->prepare($sqlCnt); $sc->execute($args);
    $totalRows = (int)$sc->fetchColumn();

    $sql = "SELECT
              t.id, t.date, t.account_code, t.segment, t.type, t.amount,
              t.booking_id, t.rental_id, t.invoice_id,
              t.reference, t.milestone, t.notes, t.attachment_url, t.created_at
              ".($joinBookings ? ", b.booking_ref" : "")."
            FROM transactions t
            ".($joinBookings ? "LEFT JOIN bookings b ON b.id=t.booking_id AND b.company_id=t.company_id" : "")."
            WHERE ".implode(' AND ', $where)."
            ORDER BY {$order}
            LIMIT :lim OFFSET :off";
    $st = db()->prepare($sql);
    foreach ($args as $k=>$v) $st->bindValue($k, $v);
    $st->bindValue(':lim', $perPage, PDO::PARAM_INT);
    $st->bindValue(':off', $offset, PDO::PARAM_INT);
    $st->execute();
    $rows = $st->fetchAll() ?: [];
  } catch (Throwable $e) {
    $errors[] = (defined('APP_ENV') && APP_ENV==='dev') ? $e->getMessage() : 'Unable to load transactions.';
  }
}

/* ---------------------------------
   Running balance (single account)
----------------------------------*/
$runningBalances = [];
if (!$errors && $singleAccount && $rows) {
  try {
    $acc = $accSel[0] ?? '';
    $argsRB = $args;
    $argsRB[':acc_rb'] = $acc;
    // replace BETWEEN with < :from and drop :to to avoid HY093
    $wRB = $where;
    foreach ($wRB as $i=>$w) {
      if (strpos($w, 't.date BETWEEN') !== false) $wRB[$i] = "t.date < :from";
    }
    $sqlRBOpen = "SELECT COALESCE(SUM(" . sql_signed_amount() . "),0) FROM transactions t
                  ".($joinBookings ? "LEFT JOIN bookings b ON b.id=t.booking_id AND b.company_id=t.company_id" : "")."
                  WHERE ".implode(' AND ', $wRB)." AND t.account_code = :acc_rb";
    $stRB = db()->prepare($sqlRBOpen);
    unset($argsRB[':to']);               // <-- FIX: remove :to
    $stRB->execute($argsRB);
    $balance = (float)$stRB->fetchColumn();

    foreach ($rows as $r) {
      $signed = 0.0;
      if ($r['type'] === 'income') $signed = (float)$r['amount'];
      elseif ($r['type'] === 'expense') $signed = - (float)$r['amount'];
      elseif ($r['type'] === 'adjustment') $signed = (float)$r['amount'];
      else $signed = 0.0;
      $balance += $signed;
      $runningBalances[(int)$r['id']] = $balance;
    }
  } catch (Throwable $e) {
    // omit running balances if failed
  }
}

/* ---------------------------------
   CSV Export
----------------------------------*/
if (!$errors && $export === 'csv') {
  try {
    header('Content-Type: text/csv; charset=utf-8');
    header('Content-Disposition: attachment; filename=ledger_'.date('Ymd_His').'.csv');

    $out = fopen('php://output', 'w');
    fputcsv($out, [
      'Date','Account Code','Segment','Type',
      'Debit','Credit','Amount','Running Balance',
      'Booking ID','Booking Ref','Invoice ID','Rental ID',
      'Reference','Notes','Attachment','Created At'
    ]);

    $sqlAll = "SELECT
                t.*, ".($joinBookings ? "b.booking_ref" : "NULL AS booking_ref")."
              FROM transactions t
              ".($joinBookings ? "LEFT JOIN bookings b ON b.id=t.booking_id AND b.company_id=t.company_id" : "")."
              WHERE ".implode(' AND ', $where)."
              ORDER BY ".(($sort==='date_asc') ? "t.date ASC, t.id ASC" : "t.date DESC, t.id DESC");
    $stA = db()->prepare($sqlAll);
    foreach ($args as $k=>$v) $stA->bindValue($k, $v);
    $stA->execute();

    $bal = null;
    if ($singleAccount) {
      // overall opening for CSV running balance
      $acc = $accSel[0] ?? '';
      $argsOpen2 = $args;
      $argsOpen2[':acc_csv'] = $acc;
      $wOpen2 = $where;
      foreach ($wOpen2 as $i=>$w) if (strpos($w, 't.date BETWEEN') !== false) $wOpen2[$i] = "t.date < :from";
      $sqlOpen2 = "SELECT COALESCE(SUM(" . sql_signed_amount() . "),0) FROM transactions t
                   ".($joinBookings ? "LEFT JOIN bookings b ON b.id=t.booking_id AND b.company_id=t.company_id" : "")."
                   WHERE ".implode(' AND ', $wOpen2)." AND t.account_code = :acc_csv";
      $so = db()->prepare($sqlOpen2);
      unset($argsOpen2[':to']);          // <-- FIX: remove :to
      $so->execute($argsOpen2);
      $bal = (float)$so->fetchColumn();
    }

    while ($r = $stA->fetch(PDO::FETCH_ASSOC)) {
      $debit  = $r['type']==='expense'  ? (float)$r['amount'] : 0.0;
      $credit = $r['type']==='income'   ? (float)$r['amount'] : 0.0;
      $rb     = '';
      if ($singleAccount && $bal !== null) {
        $signed = ($r['type']==='income') ? (float)$r['amount']
                : ($r['type']==='expense' ? -(float)$r['amount']
                : ($r['type']==='adjustment' ? (float)$r['amount'] : 0.0));
        $bal += $signed;
        $rb = number_format($bal, 2, '.', '');
      }
      fputcsv($out, [
        $r['date'], $r['account_code'], $r['segment'], $r['type'],
        number_format($debit, 2, '.', ''), number_format($credit, 2, '.', ''), number_format((float)$r['amount'], 2, '.', ''),
        $rb,
        (int)$r['booking_id'], (string)($r['booking_ref'] ?? ''), (int)$r['invoice_id'], (int)$r['rental_id'],
        (string)($r['reference'] ?? ''), (string)($r['notes'] ?? ''), (string)($r['attachment_url'] ?? ''), (string)($r['created_at'] ?? '')
      ]);
    }
    fclose($out);
    exit;
  } catch (Throwable $e) {
    $errors[] = 'Export failed.';
  }
}

/* ---------------------------------
   Pagination helper
----------------------------------*/
$totalPages = max(1, (int)ceil($totalRows / $perPage));
$qsBase = $_GET; unset($qsBase['page']); unset($qsBase['export']);
$baseUrl = 'ledger.php?'.http_build_query($qsBase);

/* ---------------------------------
   Render
----------------------------------*/
include dirname(__DIR__, 2) . '/includes/header.php';
?>
<style>
  @media print {
    .no-print { display:none !important; }
    .card, .shadow-sm { box-shadow:none !important; border:0 !important; }
    body { background:#fff !important; }
  }
  .table-sm td, .table-sm th { padding: .5rem .6rem; }
  .badge-type { text-transform: capitalize; }
</style>

<div class="d-flex justify-content-between align-items-center mb-3">
  <div>
    <h1 class="h4 mb-0">General Ledger</h1>
    <div class="text-muted">Filter and export transactions. <?= $singleAccount ? 'Showing running balance for selected account.' : '' ?></div>
  </div>
  <div class="d-flex flex-wrap gap-2 no-print">
    <a class="btn btn-outline-secondary" href="<?= e(url_modules('accounts/index.php')) ?>">← Accounts Dashboard</a>
    <a class="btn btn-outline-primary" href="<?= e('ledger.php?'.http_build_query(array_merge($_GET,['export'=>'csv']))) ?>">⬇️ Export CSV</a>
    <button class="btn btn-outline-secondary" onclick="window.print()">🖨️ Print</button>
  </div>
</div>

<?php if ($notice): ?><div class="alert alert-success"><?= e($notice) ?></div><?php endif; ?>
<?php if ($errors): ?>
  <div class="alert alert-danger"><ul class="mb-0"><?php foreach ($errors as $er) echo '<li>'.e($er).'</li>'; ?></ul></div>
<?php endif; ?>

<div class="row g-3 mb-3">
  <div class="col-12 col-md-2">
    <div class="card shadow-sm h-100 border-secondary"><div class="card-body">
      <div class="small text-muted">Opening B/F</div>
      <div class="h5 mb-0">£<?= number_format($kpi['opening'], 2) ?></div>
    </div></div>
  </div>
  <div class="col-12 col-md-2">
    <div class="card shadow-sm h-100 border-danger"><div class="card-body">
      <div class="small text-muted">Debits (Expense)</div>
      <div class="h5 mb-0">£<?= number_format($kpi['debits'], 2) ?></div>
    </div></div>
  </div>
  <div class="col-12 col-md-2">
    <div class="card shadow-sm h-100 border-success"><div class="card-body">
      <div class="small text-muted">Credits (Income)</div>
      <div class="h5 mb-0">£<?= number_format($kpi['credits'], 2) ?></div>
    </div></div>
  </div>
  <div class="col-12 col-md-2">
    <div class="card shadow-sm h-100 border-primary"><div class="card-body">
      <div class="small text-muted">Net Movement</div>
      <div class="h5 mb-0">£<?= number_format($kpi['net'], 2) ?></div>
    </div></div>
  </div>
  <div class="col-12 col-md-2">
    <div class="card shadow-sm h-100 border-dark"><div class="card-body">
      <div class="small text-muted">Closing C/F</div>
      <div class="h5 mb-0">£<?= number_format($kpi['closing'], 2) ?></div>
    </div></div>
  </div>
  <div class="col-12 col-md-2">
    <div class="card shadow-sm h-100 border-light"><div class="card-body">
      <div class="small text-muted">Transactions</div>
      <div class="h5 mb-0"><?= number_format($kpi['count']) ?></div>
    </div></div>
  </div>
</div>

<div class="card shadow-sm mb-3 no-print">
  <div class="card-body">
    <form class="row g-2 align-items-end" method="get">
      <div class="col-sm-6 col-md-2">
        <label class="form-label">From</label>
        <input type="date" class="form-control" name="from" value="<?= e($from) ?>">
      </div>
      <div class="col-sm-6 col-md-2">
        <label class="form-label">To</label>
        <input type="date" class="form-control" name="to" value="<?= e($to) ?>">
      </div>

      <div class="col-md-3">
        <label class="form-label">Account Code</label>
        <select class="form-select" name="account_code[]" multiple size="3">
          <?php foreach ($accountCodes as $ac): ?>
            <option value="<?= e($ac) ?>" <?= in_array($ac,$accSel,true)?'selected':'' ?>><?= e($ac) ?></option>
          <?php endforeach; ?>
        </select>
        <div class="form-text">Hold Ctrl/Cmd to multi-select.</div>
      </div>

      <div class="col-md-2">
        <label class="form-label">Segment</label>
        <select class="form-select" name="segment[]" multiple size="3">
          <?php foreach ($segments as $sg): ?>
            <option value="<?= e($sg) ?>" <?= in_array($sg,$segSel,true)?'selected':'' ?>><?= e($sg) ?></option>
          <?php endforeach; ?>
        </select>
      </div>

      <div class="col-md-2">
        <label class="form-label">Type</label>
        <select class="form-select" name="type[]" multiple size="4">
          <?php foreach ($allowedTypes as $tp): ?>
            <option value="<?= e($tp) ?>" <?= in_array($tp,$typeSel,true)?'selected':'' ?>><?= e(ucfirst($tp)) ?></option>
          <?php endforeach; ?>
        </select>
      </div>

      <div class="col-md-3">
        <label class="form-label">Search</label>
        <input class="form-control" name="q" placeholder="Reference / Notes" value="<?= e($q) ?>">
      </div>

      <div class="col-md-2">
        <label class="form-label">Booking Ref</label>
        <input class="form-control" name="booking_ref" value="<?= e($bookRefQ) ?>" placeholder="Optional">
      </div>

      <div class="col-md-2">
        <label class="form-label">Amount ≥</label>
        <input class="form-control" name="amount_min" type="number" step="0.01" value="<?= e($amountMin) ?>">
      </div>
      <div class="col-md-2">
        <label class="form-label">Amount ≤</label>
        <input class="form-control" name="amount_max" type="number" step="0.01" value="<?= e($amountMax) ?>">
      </div>

      <div class="col-md-2">
        <label class="form-label">Attachment</label>
        <select class="form-select" name="has_attachment">
          <option value=""  <?= $hasAttach===''?'selected':'' ?>>— Any —</option>
          <option value="1" <?= $hasAttach==='1'?'selected':'' ?>>Has attachment</option>
          <option value="0" <?= $hasAttach==='0'?'selected':'' ?>>No attachment</option>
        </select>
      </div>

      <div class="col-md-2">
        <label class="form-label">Sort</label>
        <select class="form-select" name="sort">
          <option value="date_desc" <?= $sort==='date_desc'?'selected':'' ?>>Date ↓ (Newest)</option>
          <option value="date_asc"  <?= $sort==='date_asc'?'selected':''  ?>>Date ↑ (Oldest)</option>
        </select>
      </div>

      <div class="col-md-1">
        <label class="form-label">Per Page</label>
        <input class="form-control" type="number" min="10" max="200" name="per_page" value="<?= (int)$perPage ?>">
      </div>

      <div class="col-md-2 d-grid">
        <button class="btn btn-primary">Apply Filters</button>
      </div>
    </form>
  </div>
</div>

<div class="card shadow-sm">
  <div class="table-responsive">
    <table class="table table-sm align-middle mb-0">
      <thead class="table-light">
        <tr>
          <th style="width:110px;">Date</th>
          <th>Account Code</th>
          <th>Segment</th>
          <th>Type</th>
          <th class="text-end">£ Debit</th>
          <th class="text-end">£ Credit</th>
          <th class="text-end">£ Amount</th>
          <?php if ($singleAccount): ?><th class="text-end">£ Balance</th><?php endif; ?>
          <th>Ref</th>
          <th>Links</th>
          <th>Notes</th>
          <th>Attachment</th>
          <th style="width:120px;">Created</th>
        </tr>
      </thead>
      <tbody>
        <?php if ($rows): foreach ($rows as $r):
          $debit  = $r['type']==='expense' ? (float)$r['amount'] : 0.0;
          $credit = $r['type']==='income'  ? (float)$r['amount'] : 0.0;
          $badgeT = $r['type']==='income' ? 'success' : ($r['type']==='expense' ? 'danger' : 'secondary');
          $links = [];
          if (!empty($r['booking_id'])) {
            $refDisp = isset($r['booking_ref']) && $r['booking_ref']!==null && $r['booking_ref']!=='' ? $r['booking_ref'] : '#'.$r['booking_id'];
            $links[] = '<a href="'.e(url_modules('bookings/view.php')).'?id='.(int)$r['booking_id'].'" target="_blank" rel="noopener">Booking '.e($refDisp).'</a>';
          }
          if (!empty($r['invoice_id'])) {
            $links[] = '<a href="'.e(url_modules('invoices/view.php')).'?id='.(int)$r['invoice_id'].'" target="_blank" rel="noopener">Invoice #'.(int)$r['invoice_id'].'</a>';
          }
          if (!empty($r['rental_id'])) {
            $links[] = '<a href="'.e(url_modules('rentals/view.php')).'?id='.(int)$r['rental_id'].'" target="_blank" rel="noopener">Rental #'.(int)$r['rental_id'].'</a>';
          }
        ?>
        <tr>
          <td><?= e($r['date'] ?? '') ?></td>
          <td><span class="fw-semibold"><?= e($r['account_code'] ?? '') ?></span></td>
          <td><span class="badge text-bg-light"><?= e($r['segment'] ?? '—') ?></span></td>
          <td><span class="badge text-bg-<?= e($badgeT) ?> badge-type"><?= e($r['type'] ?? '') ?></span></td>
          <td class="text-end"><?= $debit>0 ? '£'.number_format($debit,2) : '—' ?></td>
          <td class="text-end"><?= $credit>0 ? '£'.number_format($credit,2) : '—' ?></td>
          <td class="text-end">£<?= number_format((float)$r['amount'],2) ?></td>
          <?php if ($singleAccount): ?>
            <td class="text-end"><?= isset($runningBalances[(int)$r['id']]) ? '£'.number_format($runningBalances[(int)$r['id']],2) : '—' ?></td>
          <?php endif; ?>
          <td><?= e($r['reference'] ?? '—') ?></td>
          <td><?= $links ? implode(' · ', $links) : '<span class="text-muted">—</span>' ?></td>
          <td class="text-truncate" style="max-width:260px;"><?= e($r['notes'] ?? '') ?></td>
          <td>
            <?php if (!empty($r['attachment_url'])): ?>
              <a href="<?= e($r['attachment_url']) ?>" target="_blank" rel="noopener">Open</a>
            <?php else: ?>
              <span class="text-muted">—</span>
            <?php endif; ?>
          </td>
          <td><?= e($r['created_at'] ?? '') ?></td>
        </tr>
        <?php endforeach; else: ?>
          <tr><td colspan="<?= $singleAccount?13:12 ?>" class="text-center text-muted py-4">No transactions found for the selected filters.</td></tr>
        <?php endif; ?>
      </tbody>
    </table>
  </div>

  <div class="card-footer d-flex justify-content-between align-items-center">
    <div class="small text-muted">Showing <?= count($rows) ?> of <?= number_format($totalRows) ?> transactions.</div>
    <nav aria-label="Page navigation" class="no-print">
      <ul class="pagination mb-0">
        <?php
          $mk = function(int $p, string $label=null, bool $disabled=false, bool $active=false) use ($baseUrl){
            $u = $baseUrl.'&page='.$p;
            $label = $label ?? (string)$p;
            $cls = 'page-item'.($disabled?' disabled':'').($active?' active':'');
            echo '<li class="'.$cls.'"><a class="page-link" href="'.e($disabled?'#':$u).'">'.e($label).'</a></li>';
          };
          $mk(max(1,$page-1), '«', $page<=1);
          $mk(1, '1', false, $page===1);
          if ($page-1>1) $mk($page-1, (string)($page-1));
          if ($page>1 && $page<$totalPages) $mk($page, (string)$page, false, true);
          if ($page+1<$totalPages) $mk($page+1, (string)($page+1));
          if ($totalPages>1) $mk($totalPages, (string)$totalPages, false, $page===$totalPages);
          $mk(min($totalPages,$page+1), '»', $page>=$totalPages);
        ?>
      </ul>
    </nav>
  </div>
</div>

<?php include dirname(__DIR__, 2) . '/includes/footer.php';
