<?php
/**
 * Slice 8Z-s smoke test — Kickoff brief RBAC + upsert + budget-ceiling helper.
 * Runs against tender 400 (Vodacom Tower PM sample).
 */
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';
foreach (['Tenders.php','TenderExecKickoffBrief.php'] as $m) require_once ROOT . DS . 'app' . DS . 'models' . DS . $m;

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

$db  = new DB();
$tid = 400;
$m   = new TenderExecKickoffBrief();

/* Clean any prior smoke rows */
$db->insertUpdate("DELETE FROM tender_exec_kickoff_brief WHERE tender_id = :t", [':t' => $tid]);

/* ── 1 · RBAC — allowed sections per role ─────────────────────────── */
h('1. editableSectionsFor per role');
ok(in_array('fm_gates',   $m->editableSectionsFor('Finance Manager'), true),  'FM can edit fm_gates');
ok(in_array('fm_risks',   $m->editableSectionsFor('Finance Manager'), true),  'FM can edit fm_risks');
ok(!in_array('kickoff',   $m->editableSectionsFor('Finance Manager'), true),  'FM cannot edit kickoff');
ok(in_array('kickoff',    $m->editableSectionsFor('Executive'), true),        'Exec can edit kickoff');
ok(in_array('exec_gates', $m->editableSectionsFor('Executive'), true),        'Exec can edit exec_gates');
ok(!in_array('fm_gates',  $m->editableSectionsFor('Executive'), true),        'Exec cannot edit fm_gates');
ok(in_array('fm_gates',   $m->editableSectionsFor('Administrator'), true),    'Admin can edit fm_gates');
ok(in_array('kickoff',    $m->editableSectionsFor('Administrator'), true),    'Admin can edit kickoff');
ok(empty($m->editableSectionsFor('CRM')),                                     'CRM has no editable sections');

/* ── 2 · FM save writes only FM fields ────────────────────────────── */
h('2. FM save writes only FM half');
$fmPayload = [
    'gate_insurance_activated' => 1,
    'gate_advance_requested'   => 1,
    'gate_tra_efiscal_setup'   => 0,
    'gate_wcf_nssf_verified'   => 1,
    'risk_client_payment'      => 1,
    'risk_fx_exposure'         => 0,
    'risk_wcf_nssf_compliance' => 1,
    // These should be silently dropped (not owned by FM)
    'priority'                 => 'HIGH',
    'target_kickoff_date'      => '2026-09-15',
    'instructions_to_pm'       => 'FM cannot write this — should be dropped',
    'margin_floor_pct'         => 15.50,
];
$res = $m->save($tid, $fmPayload, 42, 'Test FM User', 'Finance Manager');
ok($res['ok'] === true, 'FM save returned ok');
$row = $m->forTender($tid);
ok((int)$row['gate_insurance_activated'] === 1, 'gate_insurance_activated persisted');
ok((int)$row['gate_wcf_nssf_verified'] === 1,   'gate_wcf_nssf_verified persisted');
ok((int)$row['gate_tra_efiscal_setup'] === 0,   'gate_tra_efiscal_setup persisted (0)');
ok((int)$row['risk_client_payment'] === 1,      'risk_client_payment persisted');
ok((string)$row['priority'] === 'NORMAL',       'priority not touched by FM (default NORMAL)');
ok($row['target_kickoff_date'] === null,        'target_kickoff_date not touched by FM');
ok($row['instructions_to_pm'] === null,         'instructions_to_pm not touched by FM');
ok($row['margin_floor_pct'] === null,           'margin_floor_pct not touched by FM');
ok(!empty($row['fm_edited_on']),                'fm_edited_on stamped');
ok(empty($row['exec_edited_on']),               'exec_edited_on NOT stamped');

/* ── 3 · Exec save writes only Exec fields ────────────────────────── */
h('3. Exec save writes only Exec half');
$execPayload = [
    'priority'                     => 'HIGH',
    'target_kickoff_date'          => '2026-09-15',
    'instructions_to_pm'           => 'Kick off with a site visit before any spend.',
    'margin_floor_pct'             => 15.50,
    'change_order_threshold_pct'   => 5.00,
    'gate_site_visit'              => 1,
    'gate_mobilization'            => 1,
    'gate_long_lead'               => 1,
    'gate_permits'                 => 0,
    'gate_osha_safety_plan'        => 1,
    'risk_tight_schedule'          => 1,
    'risk_site_access'             => 0,
    'risk_scope_ambiguity'         => 1,
    'risk_ppra_reporting'          => 0,
    'reporting_cadence'            => 'WEEKLY',
    'escalation_contact_name'      => 'MD Vodacom Account',
    // Should be silently dropped
    'gate_insurance_activated'     => 0,   // FM had it as 1 — must survive
    'risk_client_payment'          => 0,   // FM had it as 1 — must survive
];
$res = $m->save($tid, $execPayload, 99, 'Test Exec User', 'Executive');
ok($res['ok'] === true, 'Exec save returned ok');
$row = $m->forTender($tid);
ok((string)$row['priority'] === 'HIGH',                     'priority = HIGH');
ok((string)$row['target_kickoff_date'] === '2026-09-15',    'target_kickoff_date persisted');
ok(strpos((string)$row['instructions_to_pm'], 'site visit') !== false, 'instructions_to_pm persisted');
ok((float)$row['margin_floor_pct'] === 15.50,               'margin_floor_pct = 15.50');
ok((int)$row['gate_site_visit'] === 1,                       'gate_site_visit persisted');
ok((int)$row['gate_osha_safety_plan'] === 1,                 'gate_osha_safety_plan persisted');
ok((int)$row['risk_tight_schedule'] === 1,                   'risk_tight_schedule persisted');
ok((string)$row['reporting_cadence'] === 'WEEKLY',           'reporting_cadence persisted');
ok((int)$row['gate_insurance_activated'] === 1,              'FM gate_insurance_activated PRESERVED (Exec did not overwrite)');
ok((int)$row['risk_client_payment'] === 1,                   'FM risk_client_payment PRESERVED (Exec did not overwrite)');
ok(!empty($row['exec_edited_on']),                           'exec_edited_on stamped');

/* ── 4 · Budget-ceiling helper ───────────────────────────────────── */
h('4. budgetCeiling helper');
$ceiling = $m->budgetCeiling($tid);
$cvRow = $db->mySelect("SELECT contract_value FROM tender_contract_terms WHERE tender_id = :t", [':t' => $tid]);
if (!empty($cvRow) && (float)$cvRow[0]['contract_value'] > 0) {
    $expected = round((float)$cvRow[0]['contract_value'] * (1 - 15.50 / 100), 2);
    ok(abs(($ceiling ?? 0) - $expected) < 0.01, "budget_ceiling = contract_value × (1 − 15.5%) = " . number_format((float)$expected));
} else {
    ok($ceiling === null, 'budget_ceiling null when no contract terms — expected for sample tender');
}

/* ── 5 · Unauthorised group ───────────────────────────────────────── */
h('5. Unauthorised group refused');
$refused = $m->save($tid, ['priority' => 'HIGH'], 5, 'Test CRM', 'CRM');
ok($refused['ok'] === false, 'CRM save refused (ok=false)');

/* ── 6 · Cleanup ──────────────────────────────────────────────────── */
$db->insertUpdate("DELETE FROM tender_exec_kickoff_brief WHERE tender_id = :t", [':t' => $tid]);
echo "\n=== DONE (row cleaned up) ===\n";
