<?php
/**
 * Slice 8Q · Tenders register pagination + project_type filter.
 *
 *  1) Tenders::countAll(filter) returns total matching count (no LIMIT applied)
 *  2) Tenders::listAll respects page + page_size (LIMIT + OFFSET)
 *  3) project_type filter narrows results
 *  4) countAll matches listAll size when page_size >= total
 *  5) Query preservation: type + stage + status filters combine correctly
 *  6) Controller: indexAction wires page + page_size + total + last_page + project_type
 *  7) View: type filter dropdown + pagination strip + type chip rendered
 *  8) Exports still work with type= filter (no pagination applied)
 */
define('DS', DIRECTORY_SEPARATOR);
define('ROOT', dirname(__DIR__));
require_once ROOT . DS . 'config' . DS . 'config.php';
require_once ROOT . DS . 'core' . DS . 'Application.php';
require_once ROOT . DS . 'core' . DS . 'Model.php';
require_once ROOT . DS . 'core' . DS . 'DB.php';
$_SESSION = $_SESSION ?? [];
require_once ROOT . DS . 'app' . DS . 'models' . DS . 'Tenders.php';

function h(string $t): void { echo "\n==== $t ====\n"; }
function p(string $t): void { echo "  $t\n"; }
function ok(bool $c, string $m): void { echo ($c ? "  ✓ " : "  ✗ ") . $m . "\n"; }
function boom(string $why): void { echo "\nFATAL: $why\n"; exit(1); }

$db = new DB();
$tm = new Tenders();

/* ── 1. countAll matches raw row count ─────────────────────────────── */
h('1. countAll matches raw row count');
$raw = (int)$db->mySelect("SELECT COUNT(*) n FROM tender_tracker WHERE deleted=0", [])[0]['n'];
$cnt = $tm->countAll([]);
ok($cnt === $raw, "countAll([]) = $cnt matches raw $raw");

/* ── 2. project_type filter narrows results ────────────────────────── */
h('2. project_type filter');
$goodsCntRaw = (int)$db->mySelect("SELECT COUNT(*) n FROM tender_tracker WHERE deleted=0 AND project_type='goods'", [])[0]['n'];
$goodsCnt    = $tm->countAll(['project_type' => 'goods']);
ok($goodsCnt === $goodsCntRaw, "goods count matches: $goodsCnt = $goodsCntRaw");
if ($goodsCnt > 0) {
    $goodsRows = $tm->listAll(['project_type' => 'goods']);
    $allGoods = array_reduce($goodsRows, fn($ok, $r) => $ok && $r['project_type'] === 'goods', true);
    ok($allGoods, 'every row in listAll(project_type=goods) has project_type=goods');
}

/* ── 3. Pagination · page 1 of size 5 ──────────────────────────────── */
h('3. LIMIT + OFFSET pagination');
$pageSize = 5;
$page1 = $tm->listAll(['page' => 1, 'page_size' => $pageSize]);
$page2 = $tm->listAll(['page' => 2, 'page_size' => $pageSize]);
ok(count($page1) <= $pageSize, "page 1 has ≤ $pageSize rows (got " . count($page1) . ")");
ok(count($page2) <= $pageSize, "page 2 has ≤ $pageSize rows (got " . count($page2) . ")");
if (count($page1) > 0 && count($page2) > 0) {
    $ids1 = array_column($page1, 'c0d');
    $ids2 = array_column($page2, 'c0d');
    $overlap = array_intersect($ids1, $ids2);
    ok(empty($overlap), 'page 1 and page 2 do not overlap');
}

/* ── 4. Big page_size returns ≤ total, no LIMIT skip ───────────────── */
h('4. page_size >= total returns everything');
$allInOnePage = $tm->listAll(['page' => 1, 'page_size' => 9999]);
ok(count($allInOnePage) === $cnt, "big page returns all $cnt rows");

/* ── 5. Filter + page combined ─────────────────────────────────────── */
h('5. filter + page combined');
$combined = $tm->countAll(['project_type' => 'goods', 'status' => 'OPEN']);
$comboRaw = (int)$db->mySelect("SELECT COUNT(*) n FROM tender_tracker WHERE deleted=0 AND project_type='goods' AND tender_status='OPEN'", [])[0]['n'];
ok($combined === $comboRaw, "goods+OPEN: $combined = $comboRaw");

/* ── 6. Controller wiring ──────────────────────────────────────────── */
h('6. Controller wiring');
$cBody = (string)file_get_contents(ROOT . DS . 'app' . DS . 'controllers' . DS . 'TendersController.php');
ok(strpos($cBody, "'project_type' => (string)(\$_GET['type']") !== false, 'controller reads ?type= into project_type filter');
ok(strpos($cBody, "'page'       => \$page")     !== false, 'controller passes page to view');
ok(strpos($cBody, "'total'      => \$total")    !== false, 'controller passes total to view');
ok(strpos($cBody, "'last_page'  => \$lastPage") !== false, 'controller passes last_page to view');
ok(strpos($cBody, "countAll(\$filter)") !== false, 'controller calls countAll for pagination');

/* ── 7. View surface ───────────────────────────────────────────────── */
h('7. View surface');
$vBody = (string)file_get_contents(ROOT . DS . 'app' . DS . 'views' . DS . 'Tenders' . DS . 'index.php');
ok(strpos($vBody, 'name="type"')       !== false, 'type filter select present');
ok(strpos($vBody, "\$f['project_type']")!== false, 'view reads project_type from filter');
ok(strpos($vBody, 'pagination')        !== false, 'pagination strip present');
ok(strpos($vBody, '$lastPage')         !== false, 'view uses last_page');
ok(strpos($vBody, '$firstIdx')         !== false, 'view computes visible range');
ok(strpos($vBody, '$typeChip')         !== false, 'type chip lookup present');
ok(strpos($vBody, '&type=')            !== false, 'type param preserved in export query string');
ok(strpos($vBody, 'Clear all filters') !== false, 'clear-filters link present');

echo "\n=== END ===\n";
