<?php
/**
 * Slice 5 end-to-end verify — assignments-during-approval → auto-provisioned project seats.
 *
 *   1) Fresh WON+AWARDED tender
 *   2) Contract terms
 *   3) CRM submits TENDER_CONTRACT_APPROVAL
 *   4) FM_VERIFY step: try to approve WITHOUT assignments → must fail; then approve WITH
 *      assignments (Accountant + Procurement) → step advances, payload accumulates.
 *   5) EXEC_APPROVE step: try WITHOUT PM → fail; then approve WITH PM → chain APPROVED.
 *   6) Confirm: c_projects row created with all 3 seats hydrated (Accountant/Procurement/PM),
 *      c_projects_role_changes rows written for each.
 */
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 ?? [];
foreach (['ApprovalChain.php','Tenders.php','TenderPricing.php'] as $m) require_once ROOT . DS . 'app' . DS . 'models' . DS . $m;

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) { echo "\nFATAL: $why\n"; exit(1); }

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

// Real users seeded in DB
$uidCrm = 100; $unmCrm = 'CRM Test User';   $ugCrm  = 'CRM';
$uidFm  = 3;   $unmFm  = 'Finance Manager Test'; $ugFm = 'Finance Manager';
$uidExec= 2;   $unmExec= 'Executive Test';  $ugExec = 'Executive';
$accountantId = 4;  $accountantName = 'Accountant Test';
$procId       = 8;  $procName       = 'Procurement Test';
$pmId         = 5;  $pmName         = 'Project Manager Test';

$stamp = date('YmdHis');
$title = "SLICE 5 E2E · Assignments on approval · $stamp";

/* 1 — Fresh tender at AWARDED/WON */
h('1. Fresh tender @ AWARDED/WON');
$fb = $db->insertUpdate(
    "INSERT INTO tender_tracker
        (tender_no, tender_title, source_type, buyer_name, project_type,
         date_received, estimated_value, currency, tender_stage, tender_status,
         created_by, created_by_name, created_on)
     VALUES (:tn, :tt, 'PRIVATE_CLIENT', 'ACME Ltd.', 'goods',
         CURDATE(), 30000000, 'TZS', 'PREP', 'OPEN', :u, :un, NOW())",
    [':tn' => 'T-E2E5-' . $stamp, ':tt' => $title, ':u' => $uidCrm, ':un' => $unmCrm]
);
$tid = (int)($fb['last_id'] ?? 0); if ($tid <= 0) boom('tender insert failed');
p("Tender c0d=$tid");
$tm->advanceStage($tid, 'SUBMISSION', 'stage', $uidCrm, $unmCrm);
$tm->advanceStage($tid, 'FOLLOW_UP',  'stage', $uidCrm, $unmCrm);
$tm->advanceStage($tid, 'OPENING',    'stage', $uidCrm, $unmCrm);
$tm->recordResult($tid, 'WON', date('Y-m-d'), '', 30000000, '', '', $uidCrm, $unmCrm);
$tm->advanceStage($tid, 'AWARDED', 'awarded', $uidCrm, $unmCrm);

/* 2 — Contract terms */
h('2. FM saves contract terms');
$tm->saveContractTerms($tid, [
    'contract_value' => 30000000, 'vat_rate' => 18, 'currency' => 'TZS',
    'payment_terms' => 'Net 30', 'advance_pct' => 20, 'retention_pct' => 5,
    'contract_duration_days' => 60, 'expected_start_date' => date('Y-m-d'),
    'notes' => 'Slice 5 verify',
], $uidFm);

/* 3 — Submit chain */
h('3. CRM submits contract chain');
$res = $ac->submit([
    'chain_key' => 'TENDER_CONTRACT_APPROVAL', 'entity_type' => 'TENDER_CONTRACT',
    'entity_id' => $tid, 'ref_label' => 'Contract test',
    'return_url' => PROOT . 'Tenders/view/' . $tid,
    'user_id' => $uidCrm, 'user_name' => $unmCrm, 'user_group' => $ugCrm,
]);
if (!$res['ok']) boom('submit failed: ' . $res['msg']);
$aid = (int)$res['approval_id'];
p("Approval c0d=$aid");

/* 4 — FM step: bad path then good path */
h('4a. FM approves WITHOUT assignments → must fail');
$r = $ac->approve(['approval_id'=>$aid, 'user_id'=>$uidFm, 'user_name'=>$unmFm, 'user_group'=>$ugFm, 'comment'=>'no assignments', 'assignments'=>[]]);
ok(!$r['ok'] && strpos($r['msg'], 'Assign:') !== false, "FM approve blocked with: " . $r['msg']);

h('4b. FM approves WITH Accountant + Procurement → advances');
$r = $ac->approve([
    'approval_id'=>$aid, 'user_id'=>$uidFm, 'user_name'=>$unmFm, 'user_group'=>$ugFm,
    'comment'=>'Assigning ops seats',
    'assignments' => [
        'ACCOUNTANT'  => ['user_id'=>$accountantId, 'user_name'=>$accountantName],
        'PROCUREMENT' => ['user_id'=>$procId,       'user_name'=>$procName],
    ],
]);
ok(!empty($r['ok']), "FM approve OK: " . $r['msg']);

/* 5 — Exec step: bad then good */
h('5a. Exec approves WITHOUT PM → must fail');
$r = $ac->approve(['approval_id'=>$aid, 'user_id'=>$uidExec, 'user_name'=>$unmExec, 'user_group'=>$ugExec, 'comment'=>'', 'assignments'=>[]]);
ok(!$r['ok'] && strpos($r['msg'], 'Assign:') !== false, "Exec approve blocked with: " . $r['msg']);

h('5b. Exec approves WITH Project Manager → chain APPROVED');
$r = $ac->approve([
    'approval_id'=>$aid, 'user_id'=>$uidExec, 'user_name'=>$unmExec, 'user_group'=>$ugExec,
    'comment'=>'Assigning PM & authorising',
    'assignments' => [ 'PROJECT_MANAGER' => ['user_id'=>$pmId, 'user_name'=>$pmName] ],
]);
ok(!empty($r['ok']) && !empty($r['final']), "Exec approve OK terminal: " . $r['msg']);

// Simulate FinChainsController::postDecisionHook (CLI bypasses it)
$a = $ac->approvalById($aid);
$staged = $ac->decodePayload((string)($a['step_payload_json'] ?? ''));
$tm->onContractApproved($tid, $uidExec, $unmExec, $staged['assignments'] ?? []);

/* 6 — Verify project seats + role-change audit */
h('6. Verify auto-project seats + role-changes audit');
$t = $tm->find($tid);
$pid = (int)$t['linked_project_id'];
ok($pid > 0, "linked_project_id = $pid");

$row = $db->mySelect("SELECT project_no, accountant_id, accountant_name, procurement_id, procurement_name, project_manager_id, project_manager_name, crm_status FROM c_projects WHERE c0d = :p", [':p' => $pid]);
$p2 = $row[0] ?? [];
p("Project = " . ($p2['project_no'] ?? '?'));
p("  Accountant       = " . ($p2['accountant_name'] ?? '') . " (id " . ($p2['accountant_id'] ?? 0) . ")");
p("  Procurement      = " . ($p2['procurement_name'] ?? '') . " (id " . ($p2['procurement_id'] ?? 0) . ")");
p("  Project Manager  = " . ($p2['project_manager_name'] ?? '') . " (id " . ($p2['project_manager_id'] ?? 0) . ")");
ok((int)$p2['accountant_id']      === $accountantId, "Accountant seat matches");
ok((int)$p2['procurement_id']     === $procId,       "Procurement seat matches");
ok((int)$p2['project_manager_id'] === $pmId,         "Project Manager seat matches");

$chg = $db->mySelect("SELECT role_slot, new_user_id, new_user_name FROM c_projects_role_changes WHERE project_id = :p ORDER BY role_slot", [':p' => $pid]);
$slots = array_column($chg, 'role_slot');
p("role_changes rows: " . implode(', ', $slots));
ok(count($chg) === 3 && in_array('ACCOUNTANT',$slots,true) && in_array('PROCUREMENT',$slots,true) && in_array('PROJECT_MANAGER',$slots,true),
   'All 3 seat assignments logged to c_projects_role_changes');

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