<?php
/**
 * Slice 6 end-to-end verify — per-project-type starter checklists.
 *
 *   1) Walk Slice 5 tender-to-project pipeline for a GOODS tender → confirm 7 tasks seeded,
 *      PM-owned rows pre-assigned to the PM.
 *   2) Repeat for a SERVICES tender → confirm 6 tasks.
 *   3) Repeat for a WORKS tender → confirm 7 tasks.
 *   4) On the Goods project:
 *        - Assign PC (Task 1 owner) then complete ASSIGN_PC → confirm c_projects.pc_id is hydrated
 *          and a c_projects_role_changes row is written.
 *        - Complete one PC task (with PC as owner) and skip another → status transitions verified.
 *
 * Uses the same real users seeded in the DB (uid 5 PM, uid 4 Accountant, uid 8 Procurement).
 */
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'] 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();
$ps = new ProjectStarter();

$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';
// Extra user for PC assignment (any active user works; grab first PC-group user)
$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 Projects Coordinator user in DB — seed one first.');
p("PC candidate: $pcNm (id $pcId)");

function drivePipeline(DB $db, ApprovalChain $ac, Tenders $tm, string $projectType, string $stamp, array $seats): int {
    global $uidCrm, $unmCrm, $ugCrm, $uidFm, $unmFm, $ugFm, $uidExec, $unmExec, $ugExec;
    $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.', :pt,
             CURDATE(), 20000000, 'TZS', 'PREP', 'OPEN', :u, :un, NOW())",
        [':tn' => "T-E2E6-{$projectType}-{$stamp}", ':tt' => "SLICE 6 · $projectType · $stamp", ':pt' => $projectType, ':u' => $uidCrm, ':un' => $unmCrm]
    );
    $tid = (int)($fb['last_id'] ?? 0); if ($tid <= 0) boom("$projectType tender insert failed");
    $tm->advanceStage($tid, 'SUBMISSION', 's', $uidCrm, $unmCrm);
    $tm->advanceStage($tid, 'FOLLOW_UP',  's', $uidCrm, $unmCrm);
    $tm->advanceStage($tid, 'OPENING',    's', $uidCrm, $unmCrm);
    $tm->recordResult($tid, 'WON', date('Y-m-d'), '', 20000000, '', '', $uidCrm, $unmCrm);
    $tm->advanceStage($tid, 'AWARDED', 'a', $uidCrm, $unmCrm);
    $tm->saveContractTerms($tid, ['contract_value' => 20000000, '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);
    $res = $ac->submit(['chain_key' => 'TENDER_CONTRACT_APPROVAL', 'entity_type' => 'TENDER_CONTRACT', 'entity_id' => $tid, 'ref_label' => "$projectType contract", 'return_url' => PROOT . 'Tenders/view/' . $tid, 'user_id' => $uidCrm, 'user_name' => $unmCrm, 'user_group' => $ugCrm]);
    if (!$res['ok']) boom("$projectType submit failed: " . $res['msg']);
    $aid = (int)$res['approval_id'];
    $ac->approve(['approval_id'=>$aid, 'user_id'=>$uidFm, 'user_name'=>$unmFm, 'user_group'=>$ugFm, 'comment'=>'', 'assignments'=>['ACCOUNTANT'=>$seats['ACCOUNTANT'],'PROCUREMENT'=>$seats['PROCUREMENT']]]);
    $ac->approve(['approval_id'=>$aid, 'user_id'=>$uidExec, 'user_name'=>$unmExec, 'user_group'=>$ugExec, 'comment'=>'', 'assignments'=>['PROJECT_MANAGER'=>$seats['PROJECT_MANAGER']]]);
    $a = $ac->approvalById($aid);
    $staged = $ac->decodePayload((string)($a['step_payload_json'] ?? ''));
    $tm->onContractApproved($tid, $uidExec, $unmExec, $staged['assignments'] ?? []);
    return $tid;
}

$stamp = date('YmdHis');
$seats = [
    'ACCOUNTANT'      => ['user_id' => $accountantId, 'user_name' => $accountantName],
    'PROCUREMENT'     => ['user_id' => $procId,       'user_name' => $procName],
    'PROJECT_MANAGER' => ['user_id' => $pmId,         'user_name' => $pmName],
];

/* 1 — Goods */
h('1. Goods pipeline → project → starter tasks');
$tidG = drivePipeline($db, $ac, $tm, 'goods', $stamp, $seats);
$tG   = $tm->find($tidG); $pidG = (int)$tG['linked_project_id'];
$tasksG = $ps->tasksFor($pidG);
p("Project $pidG · " . count($tasksG) . " tasks seeded");
ok(count($tasksG) === 7, 'Goods pack = 7 tasks');
$pmOwned = array_filter($tasksG, fn($t) => $t['owner_slot'] === 'PROJECT_MANAGER' && (int)$t['owner_user_id'] === $pmId);
ok(count($pmOwned) >= 1, 'PM-slot rows pre-assigned to PM (' . count($pmOwned) . ')');
foreach ($tasksG as $t) p("  [" . ($t['sequence']/10) . "] " . $t['task_key'] . " · owner_slot=" . $t['owner_slot'] . " · owner=" . ($t['owner_user_name'] ?: '—') . " · due " . $t['due_date']);

/* 2 — Services */
h('2. Services pipeline → project → starter tasks');
$tidS = drivePipeline($db, $ac, $tm, 'services', $stamp, $seats);
$pidS = (int)$tm->find($tidS)['linked_project_id'];
$tasksS = $ps->tasksFor($pidS);
p("Project $pidS · " . count($tasksS) . " tasks seeded");
ok(count($tasksS) === 6, 'Services pack = 6 tasks');

/* 3 — Works */
h('3. Works pipeline → project → starter tasks');
$tidW = drivePipeline($db, $ac, $tm, 'works', $stamp, $seats);
$pidW = (int)$tm->find($tidW)['linked_project_id'];
$tasksW = $ps->tasksFor($pidW);
p("Project $pidW · " . count($tasksW) . " tasks seeded");
ok(count($tasksW) === 7, 'Works pack = 7 tasks');

/* 4 — Interact with Goods project: assign PC → complete ASSIGN_PC → hydrate c_projects.pc_id */
h('4. Goods project: assign PC & complete ASSIGN_PC → hydrate c_projects.pc_id');
$assignPc = null; $reviewPo = null; $procSource = null;
foreach ($tasksG as $t) {
    if ($t['task_key'] === 'ASSIGN_PC')       $assignPc  = $t;
    if ($t['task_key'] === 'REVIEW_CLIENT_PO')$reviewPo  = $t;
    if ($t['task_key'] === 'PROC_SOURCE')     $procSource= $t;
}
if (!$assignPc) boom('ASSIGN_PC task not found');
$ps->assignOwner((int)$assignPc['c0d'], $pcId, $pcNm, $pmId);
$ps->complete((int)$assignPc['c0d'], $pmId, $pmName, 'Nominating PC ' . $pcNm);
$prow = $db->mySelect("SELECT pc_id, pc_name FROM c_projects WHERE c0d = :p", [':p' => $pidG]);
p("c_projects.pc_id = " . ($prow[0]['pc_id'] ?? 0) . " · pc_name = " . ($prow[0]['pc_name'] ?? '?'));
ok((int)$prow[0]['pc_id'] === $pcId, "PC seat hydrated on project");
$audit = $db->mySelect("SELECT COUNT(*) n FROM c_projects_role_changes WHERE project_id = :p AND role_slot = 'PROJECTS_COORDINATOR'", [':p' => $pidG]);
ok((int)$audit[0]['n'] === 1, "PROJECTS_COORDINATOR role-change audit row written");

/* 5 — Complete + skip other tasks */
h('5. Complete REVIEW_CLIENT_PO (PC owner) + Skip PROC_SOURCE');
if ($reviewPo) {
    $ps->assignOwner((int)$reviewPo['c0d'], $pcId, $pcNm, $pmId);
    $ps->complete((int)$reviewPo['c0d'], $pcId, $pcNm, 'PO reviewed, items match');
}
if ($procSource) {
    $ps->skip((int)$procSource['c0d'], $procId, $procName, 'Direct-award tender — no RFQ needed');
}
$after = $ps->tasksFor($pidG);
$counts = ['OPEN'=>0,'DONE'=>0,'SKIPPED'=>0];
foreach ($after as $t) $counts[$t['status']]++;
p("Task status counts: OPEN={$counts['OPEN']} · DONE={$counts['DONE']} · SKIPPED={$counts['SKIPPED']}");
ok($counts['DONE'] === 2 && $counts['SKIPPED'] === 1 && $counts['OPEN'] === 4, "Status transitions verified (2 DONE + 1 SKIPPED + 4 OPEN of 7)");

/* 6 — PM myQueue reflects remaining open PM-owned tasks */
h('6. PM myQueue');
$pmOpen = $ps->openForUser($pmId);
p("Open onboarding tasks assigned to PM (id $pmId) across all projects: " . count($pmOpen));
ok(count($pmOpen) >= 1, "PM has open onboarding tasks visible in queue");

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