<?php
/**
 * Slice 8P-TenderDetailsEdit · Details tab inline edit for CRM/Admin/Exec.
 *
 *  1) Tenders::saveDetails updates only the whitelist fields
 *  2) project_type, buyer_name, customer_id are NOT changed (locked at intake)
 *  3) Invalid source_type falls back to previous value
 *  4) Partial payload keeps unchanged fields intact (uses find() defaults)
 *  5) Round-trip: save → find returns saved fields
 *  6) Controller: saveDetailsAjAction defined
 *  7) Perms: CRM + Administrator + Executive have saveDetailsAj; PC/PM/FM do NOT
 *  8) View: Edit-details button + jquery-confirm modal wired
 *  9) View: POSTs to saveDetailsAj
 * 10) View: locked-fields caption present
 */
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();
$uid = 155;

/* ── 0. Anchor + snapshot ──────────────────────────────────────────── */
h('0. Anchor tender + snapshot');
$row = $db->mySelect(
    "SELECT c0d, tender_title, tender_description, tender_industry, region, district,
            source_type, source_ref, source_url, submission_method,
            project_type, buyer_name, customer_id
       FROM tender_tracker WHERE deleted=0 ORDER BY c0d ASC LIMIT 1", []);
if (empty($row)) boom('no tender');
$tid = (int)$row[0]['c0d'];
$snap = $row[0];
p("anchor tender #$tid · type={$snap['project_type']} · buyer='{$snap['buyer_name']}'");

/* ── 1. saveDetails updates whitelisted fields ─────────────────────── */
h('1. saveDetails updates whitelist');
$tm->saveDetails($tid, [
    'tender_title'       => '8P-VERIFY updated title',
    'tender_description' => '8P-VERIFY: multi-line\ndescription',
    'tender_industry'    => 'Telecom',
    'region'             => 'Dar es Salaam',
    'district'           => 'Ilala',
    'source_type'        => 'PRIVATE_CLIENT',
    'source_ref'         => 'REF-8P-01',
    'source_url'         => 'https://example.tld/tender/8p',
    'submission_method'  => 'Hand-delivered, sealed envelope',
], $uid);
$r = $tm->find($tid);
ok($r['tender_title']       === '8P-VERIFY updated title',                  'tender_title saved');
ok($r['tender_industry']    === 'Telecom',                                  'tender_industry saved');
ok($r['region']             === 'Dar es Salaam',                            'region saved');
ok($r['district']           === 'Ilala',                                    'district saved');
ok($r['source_type']        === 'PRIVATE_CLIENT',                           'source_type saved');
ok($r['source_ref']         === 'REF-8P-01',                                'source_ref saved');
ok($r['source_url']         === 'https://example.tld/tender/8p',            'source_url saved');
ok($r['submission_method']  === 'Hand-delivered, sealed envelope',          'submission_method saved');

/* ── 2. Locked fields NOT touched by saveDetails ───────────────────── */
h('2. Locked fields untouched');
ok((string)$r['project_type'] === (string)$snap['project_type'], 'project_type unchanged (locked)');
ok((string)$r['buyer_name']   === (string)$snap['buyer_name'],   'buyer_name unchanged (locked)');
ok((int)$r['customer_id']     === (int)$snap['customer_id'],     'customer_id unchanged (locked)');

/* ── 3. Invalid source_type falls back to previous ─────────────────── */
h('3. Invalid source_type coerced to previous');
$tm->saveDetails($tid, ['source_type' => 'GARBAGE'], $uid);
$r3 = $tm->find($tid);
ok((string)$r3['source_type'] === 'PRIVATE_CLIENT', 'invalid source_type ignored; kept PRIVATE_CLIENT');

/* ── 4. Partial payload keeps unspecified fields intact ────────────── */
h('4. Partial payload preserves untouched fields');
$tm->saveDetails($tid, ['region' => 'Arusha'], $uid);
$r4 = $tm->find($tid);
ok((string)$r4['region']          === 'Arusha',                          'region overridden');
ok((string)$r4['district']        === 'Ilala',                           'district untouched');
ok((string)$r4['tender_industry'] === 'Telecom',                         'tender_industry untouched');
ok((string)$r4['source_ref']      === 'REF-8P-01',                       'source_ref untouched');

/* ── 5. Round-trip fidelity ────────────────────────────────────────── */
h('5. Round-trip fidelity');
$tm->saveDetails($tid, [
    'tender_title'       => 'Round-trip title',
    'tender_description' => "Line 1\nLine 2\nLine 3",
    'source_type'        => 'INVITATION',
], $uid);
$r5 = $tm->find($tid);
ok($r5['tender_title'] === 'Round-trip title', 'title round-trip');
ok($r5['tender_description'] === "Line 1\nLine 2\nLine 3", 'multiline description round-trip');
ok($r5['source_type'] === 'INVITATION', 'source_type round-trip');

/* ── 6. Controller endpoint ────────────────────────────────────────── */
h('6. Controller endpoint');
$cBody = (string)file_get_contents(ROOT . DS . 'app' . DS . 'controllers' . DS . 'TendersController.php');
ok(strpos($cBody, 'function saveDetailsAjAction') !== false, 'saveDetailsAjAction defined');
ok(strpos($cBody, "'ok' => true") !== false && strpos($cBody, 'saveDetails(') !== false, 'delegates to Tenders::saveDetails');

/* ── 7. Perms surface ──────────────────────────────────────────────── */
h('7. Perms');
$granted = array_map(fn($r) => $r['user_group_name'], $db->mySelect(
    "SELECT g.user_group_name FROM user_groups_perms p JOIN user_groups g ON g.c0d=p.user_group_id
      WHERE p.controller_allowed='Tenders' AND p.action_allowed='saveDetailsAj'", []) ?: []);
ok(in_array('CRM',           $granted, true), 'CRM has saveDetailsAj');
ok(in_array('Administrator', $granted, true), 'Admin has saveDetailsAj');
ok(in_array('Executive',     $granted, true), 'Executive has saveDetailsAj');
ok(!in_array('Projects Coordinator', $granted, true), 'PC does NOT have saveDetailsAj');
ok(!in_array('Project Manager',      $granted, true), 'PM does NOT have saveDetailsAj');
ok(!in_array('Finance Manager',      $granted, true), 'FM does NOT have saveDetailsAj');

/* ── 8. View surface ───────────────────────────────────────────────── */
h('8. View surface');
$vBody = (string)file_get_contents(ROOT . DS . 'app' . DS . 'views' . DS . 'Tenders' . DS . 'view.php');
ok(strpos($vBody, "id=\"btn_edit_details\"")     !== false, 'Edit-details button present');
ok(strpos($vBody, "UserGroups::can('Tenders','saveDetailsAj')") !== false, 'button gated by saveDetailsAj perm');
ok(strpos($vBody, "Slice 8P")                    !== false, 'Slice 8P marker present in JS block');
ok(strpos($vBody, 'saveDetailsAj')               !== false, 'view posts to saveDetailsAj');
ok(strpos($vBody, 'ed_title')                    !== false, 'ed_title modal field wired');
ok(strpos($vBody, 'ed_desc')                     !== false, 'ed_desc modal field wired');
ok(strpos($vBody, 'ed_source')                   !== false, 'ed_source select wired');
ok(strpos($vBody, 'locked · set at intake')      !== false, 'locked-fields caption present in details display');

/* ── Z. Restore anchor ─────────────────────────────────────────────── */
h('Z. Restore');
$db->insertUpdate(
    "UPDATE tender_tracker SET
        tender_title       = :tt, tender_description = :td, tender_industry = :ti,
        region             = :rg, district           = :ds,
        source_type        = :src, source_ref         = :sr, source_url = :su,
        submission_method  = :sm
      WHERE c0d = :id",
    [
        ':tt'  => (string)$snap['tender_title'],
        ':td'  => (string)$snap['tender_description'],
        ':ti'  => (string)$snap['tender_industry'],
        ':rg'  => (string)$snap['region'],
        ':ds'  => (string)$snap['district'],
        ':src' => (string)$snap['source_type'],
        ':sr'  => (string)$snap['source_ref'],
        ':su'  => (string)$snap['source_url'],
        ':sm'  => (string)$snap['submission_method'],
        ':id'  => $tid,
    ]
);
p('anchor restored to pre-verify state');

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