<?php

declare(strict_types=1);

$root = dirname(__DIR__);
$summaryPath = $root . '/app/Repositories/ExecutiveSummaryRepository.php';
$executivePath = $root . '/app/Repositories/ExecutiveRepository.php';
$viewPath = $root . '/app/Views/dashboard/index.php';
$cssPath = $root . '/public/assets/css/dashboard-executive-command.css';
$mainPath = $root . '/app/Views/layouts/main.php';
$reconcilePath = $root . '/tools/executive-kpi-reconcile.php';
$versionPath = $root . '/DDB_VERSION.json';

$summary = (string)@file_get_contents($summaryPath);
$executive = (string)@file_get_contents($executivePath);
$view = (string)@file_get_contents($viewPath);
$css = (string)@file_get_contents($cssPath);
$main = (string)@file_get_contents($mainPath);
$reconcile = (string)@file_get_contents($reconcilePath);
$version = json_decode((string)@file_get_contents($versionPath), true);

$gates = [];
$add = static function (string $name, bool $pass, string $detail = '') use (&$gates): void {
    $gates[] = ['name' => $name, 'status' => $pass ? 'PASS' : 'FAIL', 'detail' => $detail];
};

$add('R42 contract retained in release metadata', isset($version['r42_executive_dashboard_legacy_kpi_parity_live_reconciliation_closure']) && (($version['r42_executive_dashboard_legacy_kpi_parity_live_reconciliation_closure']['target'] ?? '') === '11.0.11.49 / H11.0-P11.1-R42'));
$add('Legacy KPI contract marker', str_contains($summary, "DDB_LEGACY_EXECUTIVE_KPI_PARITY_V1"));

$requiredCodes = ['opd','er','follow_up','first_visits','admissions','discharges','scheduled_operations','performed_operations'];
$definitionsOk = true;
foreach ($requiredCodes as $code) {
    if (!str_contains($summary, "'{$code}' => ['label'")) { $definitionsOk = false; break; }
}
$add('Eight Legacy KPI definitions registered', $definitionsOk, 'required=' . implode(',', $requiredCodes));

$summaryAddsOk = true;
foreach ($requiredCodes as $code) {
    if (!str_contains($summary, "\$this->add(\$items, '{$code}'")) { $summaryAddsOk = false; break; }
}
$add('Summary refresh persists all eight Legacy KPI values', $summaryAddsOk);

$legacyBlockStart = strpos($summary, 'private function opdVisitsToday(): ?float');
$legacyBlockEnd = strpos($summary, 'private function expectedDischargesToday(): ?float');
$legacyBlock = ($legacyBlockStart !== false && $legacyBlockEnd !== false && $legacyBlockEnd > $legacyBlockStart)
    ? substr($summary, $legacyBlockStart, $legacyBlockEnd - $legacyBlockStart)
    : '';

$add('Legacy KPI block does not use operationalRangeSql', $legacyBlock !== '' && !str_contains($legacyBlock, 'operationalRangeSql('));
$add('OPD exact Legacy predicates',
    str_contains($legacyBlock, "legacyVisitReservationColumnExpr('CLASSCODE')") &&
    str_contains($legacyBlock, 'WHERE {$classCodeExpr} = 2') &&
    str_contains($legacyBlock, "r.servstatus NOT IN ('C','F')") &&
    str_contains($legacyBlock, 'r.sched_serial IS NOT NULL') &&
    str_contains($legacyBlock, 'v.hosp_id = r.hosp_id')
);
$add('Legacy Today timestamp predicate preserved',
    substr_count($legacyBlock, "TO_TIMESTAMP(SYSDATE,'DD/MM/RR HH12:MI:SSXFF PM')") >= 6 &&
    str_contains($legacyBlock, 'TO_DATE(r.expecteddonedate) <= TO_TIMESTAMP(SYSDATE')
);
$add('ER exact Legacy classifier with unique unqualified-column resolution',
    str_contains($legacyBlock, 'legacyVisitReservationColumnExpr') &&
    str_contains($legacyBlock, "legacyVisitReservationColumnExpr('OUTPTA_EMERGENCY_TYPE')") &&
    str_contains($legacyBlock, "legacyVisitReservationColumnExpr('CLINIC_ID')") &&
    str_contains($legacyBlock, 'NVL({$emergencyTypeExpr},0) = 1 OR {$clinicExpr} IN (16, 297, 298)')
);
$add('Follow-up exact Legacy Res_Type contract', str_contains($legacyBlock, 'AND r.res_type = 1'));
$add('First visits exact Legacy visit_id contract', str_contains($legacyBlock, 'AND v.visit_id = 1'));
$add('Admissions exact Legacy join/date/bed contract',
    str_contains($legacyBlock, 'v.visit_start_date >= TO_TIMESTAMP(SYSDATE') &&
    str_contains($legacyBlock, 'NVL(e.baby_normal_bed,1) = 1')
);
$add('Discharges exact Legacy join/date/bed contract', str_contains($legacyBlock, 'v.visit_end_date >= TO_TIMESTAMP(SYSDATE'));
$add('Scheduled operations exact Legacy status/date contract',
    str_contains($legacyBlock, "r.servstatus NOT IN ('C')") &&
    str_contains($legacyBlock, 'r.expecteddonedate >= TO_DATE(SYSDATE)')
);
$add('Performed operations exact Legacy status contract', str_contains($legacyBlock, "r.servstatus = 'D'"));
$add('No zero-result fallback changes Legacy semantics', !str_contains($legacyBlock, 'fallbackSql') && !str_contains($legacyBlock, 'max($value'));

$add('Executive summary model maps restored Follow-up and First Visits',
    str_contains($executive, "'follow_up' => \$n('follow_up')") &&
    str_contains($executive, "'first_visits' => \$n('first_visits')")
);
$add('Fast/live fallback models include restored KPI keys', substr_count($executive, "'follow_up' => 0") >= 2 && substr_count($executive, "'first_visits' => 0") >= 2);
$add('Hospital Pulse restores Follow-up and First Visits cards',
    str_contains($view, "'Follow-up visits'") && str_contains($view, "'First visits'") &&
    str_contains($view, "\$followUp = \$get(\$flow, 'follow_up')") &&
    str_contains($view, "\$firstVisits = \$get(\$flow, 'first_visits')")
);
$add('Hospital Pulse has eight operational cards', substr_count($view, "'trusted' =>") >= 8);
$add('Desktop Hospital Pulse uses four columns', str_contains($css, '.command139-pulse-grid') && str_contains($css, 'grid-template-columns: repeat(4, minmax(0, 1fr));'));
$add('Responsive two-column and mobile single-column rules preserved',
    str_contains($css, 'body.page-dashboard .command139-pulse-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }') &&
    str_contains($css, '@media (max-width: 640px)') &&
    str_contains($css, 'grid-template-columns: 1fr;')
);

preg_match_all('/font-size\s*:\s*(\d+)px/i', $css, $matches);
$pixelSizes = array_map('intval', $matches[1] ?? []);
$belowFloor = array_values(array_filter($pixelSizes, static fn(int $v): bool => $v < 14));
$add('R41 readability floor remains >= 14px', count($belowFloor) === 0, 'below_floor=' . json_encode($belowFloor));

$add('Expected discharges keeps operational-day logic',
    str_contains($summary, "private function expectedDischargesToday(): ?float") &&
    str_contains(substr($summary, strpos($summary, 'private function expectedDischargesToday(): ?float')), "operationalRangeSql('e.expectdiscdate')")
);
$add('Read-only live reconciliation tool exists', is_file($reconcilePath) && str_contains($reconcile, 'reconcileLegacyKpisAgainstSummary()') && str_contains($reconcile, "'oracle_writes' => 'NONE'"));
$add('Reconciliation requires exact 8/8 per hospital', str_contains($reconcile, "'required_exact_parity' => '8/8 PER HOSPITAL'") && str_contains($reconcile, "(int)(\$reconciliation['pass_metrics'] ?? 0) === 8"));
$add('R42 cache key active', str_contains($main, 'r42-legacy-kpi-parity-live-reconciliation-closure'));
$add('R42 no DB schema change and no Oracle writes',
    ($version['database_changes'] ?? '') === 'NONE' &&
    ($version['oracle_write_operations'] ?? '') === 'NONE'
);
$add('R42 truthfully marks executive KPI calculation change', ($version['calculation_rules_changed'] ?? false) === true);
$add('Live acceptance is not pre-claimed',
    str_starts_with((string)($version['functional_baseline_status'] ?? ''), 'CANDIDATE_UNTIL_') &&
    (($version['r42_executive_dashboard_legacy_kpi_parity_live_reconciliation_closure']['official_baseline_claimed'] ?? true) === false) &&
    (($version['r42_1_legacy_kpi_column_ownership_hotfix']['official_baseline_claimed'] ?? true) === false)
);

$pass = count(array_filter($gates, static fn(array $g): bool => $g['status'] === 'PASS'));
$fail = count($gates) - $pass;
$out = [
    'patch' => 'H11.0-P11.1-R42',
    'target' => '11.0.11.49',
    'status' => $fail === 0 ? 'PASS' : 'FAIL',
    'pass_gates' => $pass,
    'fail_gates' => $fail,
    'total_gates' => count($gates),
    'required_live_numeric_gate' => '8/8 exact parity per accepted hospital',
    'gates' => $gates,
    'truth_boundary' => 'Static SQL/source parity is not Live Oracle numeric acceptance. Missing Evidence is not PASS.',
];

echo json_encode($out, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE), PHP_EOL;
exit($fail === 0 ? 0 : 1);
