<?php
/**
 * Slice 8a end-to-end verify — PC scrub of the auto-created client PO.
 *
 *   1) Drive the full tender→project pipeline for a Goods tender (uses Slices 4/5/6/7).
 *   2) Resolve /GoodsClientPos/forProject/{project_id} → confirm it maps to the seeded PO.
 *   3) Confirm the client PO exists with pricing lines snapshotted.
 *   4) markScrubbed WITHOUT a priced item → must fail (guard).
 *   5) saveItem on each line (adjust qty + cog to introduce a real margin) → verify rollup.
 *   6) markScrubbed → status DRAFT → CONFIRMED, scrubbed_at/by populated.
 *   7) unscrub → status CONFIRMED → DRAFT, scrubbed_at cleared.
 *   8) Then re-mark scrubbed and confirm the PC's ProjectStarter task PC_SCRUB_PO
 *      can be marked DONE (owner-nomination + complete() sequence).
 */
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','ProjectStarter.php',
    'Notifications.php','GoodsClientPos.php','GoodsScrub.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();
$pm = new TenderPricing();
$ps = new ProjectStarter();
$gs = new GoodsScrub();

$uidCrm = 100; $unmCrm = 'CRM Test User';       $ugCrm  = 'CRM';
$uidFm  = 3;   $unmFm  = 'Finance Manager Test'; $ugFm  = 'Finance Manager';
$uidExec= 2;   $unmExec= 'Executive Test';      $ugExec = 'Executive';
$accId  = 4;   $accNm  = 'Accountant Test';
$procId = 8;   $procNm = 'Procurement Test';
$pmId   = 5;   $pmNm   = 'Project Manager Test';
$pcRow  = $db->mySelect("SELECT c0d, full_name FROM users WHERE user_group_name='Projects Coordinator' AND status='ACTIVE' AND deleted=0 ORDER BY c0d LIMIT 1", []);
$pcId   = (int)($pcRow[0]['c0d'] ?? 0);
$pcNm   = (string)($pcRow[0]['full_name'] ?? '');
if ($pcId <= 0) boom('No active PC user');

/* 1 — Drive the pipeline */
h('1. Drive Goods pipeline to project provisioning (with priced tender)');
$stamp = date('YmdHis');
$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(), 20000000, 'TZS', 'PREP', 'OPEN', :u, :un, NOW())",
    [':tn' => 'T-E2E8A-' . $stamp, ':tt' => 'SLICE 8a scrub verify', ':u' => $uidCrm, ':un' => $unmCrm]
);
$tid = (int)($fb['last_id'] ?? 0);
$tm->advanceStage($tid, 'SUBMISSION', 's', $uidCrm, $unmCrm);
$tm->advanceStage($tid, 'FOLLOW_UP',  's', $uidCrm, $unmCrm);
$tm->advanceStage($tid, 'OPENING',    's', $uidCrm, $unmCrm);
// Build a pricing version so the snapshot has lines to copy
$vid = $pm->createVersion($tid, null, $uidCrm, $unmCrm);
$s1  = $pm->addSection($vid, $tid, 'Equipment', 'Kit for site', $uidCrm);
$pm->addLine($s1, ['item_ref'=>'EQ-A','description'=>'Widget A','qty'=>3,'uom'=>'ea','unit_cost'=>500000,'markup_pct'=>25], $uidCrm);
$pm->addLine($s1, ['item_ref'=>'EQ-B','description'=>'Widget B','qty'=>2,'uom'=>'ea','unit_cost'=>750000,'markup_pct'=>30], $uidCrm);
$res = $pm->submitForApproval($vid, $uidCrm, $unmCrm, $ugCrm);
$aidP = (int)$res['approval_id'];
$ac->approve(['approval_id'=>$aidP,'user_id'=>$pmId,  'user_name'=>$pmNm,  'user_group'=>'Project Manager','comment'=>'ok']);
$ac->approve(['approval_id'=>$aidP,'user_id'=>$uidFm, 'user_name'=>$unmFm, 'user_group'=>$ugFm,'comment'=>'ok']);
$ac->approve(['approval_id'=>$aidP,'user_id'=>$uidExec,'user_name'=>$unmExec,'user_group'=>$ugExec,'comment'=>'ok']);
$pm->onPricingDecision($vid, 'APPROVED');
$pm->applyToTender($vid);
// Result → Awarded → Contract terms → Contract chain
$tm->recordResult($tid, 'WON', date('Y-m-d'), '', 20000000, '', '', $uidCrm, $unmCrm);
$tm->advanceStage($tid, 'AWARDED', 'a', $uidCrm, $unmCrm);
$tm->saveContractTerms($tid, ['contract_value' => $pm->findVersion($vid)['sum_price'], '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')], $uidFm);
$rC = $ac->submit(['chain_key'=>'TENDER_CONTRACT_APPROVAL','entity_type'=>'TENDER_CONTRACT','entity_id'=>$tid,'ref_label'=>'S8a','return_url'=>PROOT.'Tenders/view/'.$tid,'user_id'=>$uidCrm,'user_name'=>$unmCrm,'user_group'=>$ugCrm]);
$aidC = (int)$rC['approval_id'];
$ac->approve(['approval_id'=>$aidC,'user_id'=>$uidFm,'user_name'=>$unmFm,'user_group'=>$ugFm,'comment'=>'','assignments'=>['ACCOUNTANT'=>['user_id'=>$accId,'user_name'=>$accNm],'PROCUREMENT'=>['user_id'=>$procId,'user_name'=>$procNm]]]);
$ac->approve(['approval_id'=>$aidC,'user_id'=>$uidExec,'user_name'=>$unmExec,'user_group'=>$ugExec,'comment'=>'','assignments'=>['PROJECT_MANAGER'=>['user_id'=>$pmId,'user_name'=>$pmNm]]]);
$aC = $ac->approvalById($aidC);
$stg = $ac->decodePayload((string)($aC['step_payload_json'] ?? ''));
$tm->onContractApproved($tid, $uidExec, $unmExec, $stg['assignments'] ?? []);
$t = $tm->find($tid); $pid = (int)$t['linked_project_id'];
$pm->snapshotToProject($tid, $pid, $uidExec, $unmExec);
p("Project provisioned: c0d=$pid");
ok($pid > 0, 'Project auto-created');

/* 2 — forProject alias should resolve to a single PO */
h('2. GoodsClientPos::forProject alias resolves to the seeded PO');
$posForProject = $db->mySelect("SELECT c0d, client_po_no, status FROM goods_client_pos WHERE project_id = :p AND deleted = 0", [':p' => $pid]);
p("Found " . count($posForProject) . " PO(s) for project $pid");
ok(count($posForProject) === 1, 'Exactly one PO — forProject will redirect to that PO');
$poId = (int)$posForProject[0]['c0d'];
p("PO: {$posForProject[0]['client_po_no']} (status={$posForProject[0]['status']})");

/* 3 — PO has snapshotted lines */
h('3. PO has snapshotted lines from pricing');
$items = $db->mySelect("SELECT c0d, item_no, description, qty, unit_price_sell, unit_price_cog, margin_pct FROM goods_client_po_items WHERE client_po_id = :p ORDER BY item_no", [':p' => $poId]);
p("Item count: " . count($items));
ok(count($items) === 2, '2 items snapshotted from pricing (matches sections/lines)');

/* 4 — markScrubbed must fail if any sell price is 0 */
h('4. markScrubbed guard — zero out first line sell → must fail');
$db->insertUpdate("UPDATE goods_client_po_items SET unit_price_sell = 0 WHERE c0d = :id", [':id' => (int)$items[0]['c0d']]);
try {
    $gs->markScrubbed($poId, $pcId, $pcNm);
    ok(false, 'guard failed — markScrubbed accepted a zero-priced line');
} catch (\Throwable $e) {
    ok(strpos($e->getMessage(), 'zero sell price') !== false, 'Guard rejected: ' . $e->getMessage());
}

/* 5 — saveItem on each line to set fresh pricing */
h('5. saveItem to set qty + sell + cog → margin computed, rollup updates');
foreach ($items as $i => $it) {
    $gs->saveItem((int)$it['c0d'], [
        'description'     => $it['description'],
        'qty'             => (float)$it['qty'],
        'unit'            => 'ea',
        'unit_price_sell' => $i === 0 ? 750000 : 1050000,
        'unit_price_cog'  => $i === 0 ? 500000 : 750000,
        'notes'           => 'PC scrub adjustment',
    ], $pcId);
}
$after = $db->mySelect("SELECT unit_price_sell, unit_price_cog, margin_pct, line_total_sell FROM goods_client_po_items WHERE client_po_id = :p ORDER BY item_no", [':p' => $poId]);
p("Line 1 margin: {$after[0]['margin_pct']}% · line_total {$after[0]['line_total_sell']}");
p("Line 2 margin: {$after[1]['margin_pct']}% · line_total {$after[1]['line_total_sell']}");
ok(abs((float)$after[0]['margin_pct'] - 33.33) < 0.1, 'Line 1 margin ≈ 33.33%');
ok(abs((float)$after[1]['margin_pct'] - 28.57) < 0.1, 'Line 2 margin ≈ 28.57%');
$poHdr = $db->mySelect("SELECT subtotal_sell, subtotal_cog, vat_amount, total FROM goods_client_pos WHERE c0d = :p", [':p' => $poId]);
$expSell = round(3*750000 + 2*1050000, 2);
$expTot  = round($expSell * 1.18, 2);
p("Header rolled: sell=" . number_format((float)$poHdr[0]['subtotal_sell']) . " total=" . number_format((float)$poHdr[0]['total']));
ok(abs((float)$poHdr[0]['subtotal_sell'] - $expSell) < 0.01, 'subtotal_sell matches rollup');
ok(abs((float)$poHdr[0]['total']         - $expTot)  < 0.01, 'total (incl VAT) matches rollup');

/* 6 — markScrubbed success */
h('6. markScrubbed → status DRAFT → CONFIRMED, timestamp + by populated');
$gs->markScrubbed($poId, $pcId, $pcNm, 'All lines verified against tender pricing');
$row = $db->mySelect("SELECT status, scrubbed_at, scrubbed_by_id, scrubbed_by_name, scrub_notes FROM goods_client_pos WHERE c0d = :p", [':p' => $poId])[0];
p("status={$row['status']} scrubbed_at={$row['scrubbed_at']} by={$row['scrubbed_by_name']}");
ok($row['status'] === 'CONFIRMED' && !empty($row['scrubbed_at']) && (int)$row['scrubbed_by_id'] === $pcId, 'PO scrubbed → CONFIRMED with correct actor');

/* 7 — unscrub (allowed because no RFQ yet) */
h('7. unscrub reverts to DRAFT (no RFQ yet)');
$gs->unscrub($poId, $pcId);
$row2 = $db->mySelect("SELECT status, scrubbed_at FROM goods_client_pos WHERE c0d = :p", [':p' => $poId])[0];
ok($row2['status'] === 'DRAFT' && empty($row2['scrubbed_at']), "Unscrubbed → status=DRAFT scrubbed_at NULL");
// Re-scrub for the checklist step
$gs->markScrubbed($poId, $pcId, $pcNm);

/* 8 — Mark the PC_SCRUB_PO starter task DONE */
h('8. ProjectStarter task PC_SCRUB_PO → assign PC → complete');
$tasks = $ps->tasksFor($pid);
$scrubTask = null; foreach ($tasks as $tt) if ($tt['task_key'] === 'PC_SCRUB_PO') $scrubTask = $tt;
if (!$scrubTask) boom('PC_SCRUB_PO task missing');
$ps->assignOwner((int)$scrubTask['c0d'], $pcId, $pcNm, $pmId);
$ps->complete((int)$scrubTask['c0d'], $pcId, $pcNm, 'PO scrubbed and confirmed');
$after = $ps->tasksFor($pid);
$scrubAfter = null; foreach ($after as $tt) if ($tt['task_key'] === 'PC_SCRUB_PO') $scrubAfter = $tt;
ok($scrubAfter && $scrubAfter['status'] === 'DONE', "PC_SCRUB_PO checklist task is DONE (completed_by={$scrubAfter['completed_by_name']})");

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