<?php

require_once __DIR__ . '/../core/config.php';
require_once __DIR__ . '/../services/billing/BillingRuntimeGate.php';

function diag_bool($value)
{
    return $value ? 'true' : 'false';
}

function read_file_or_fail($path, array &$failures)
{
    if (!is_file($path)) {
        $failures[] = 'missing file: ' . $path;
        return '';
    }
    $content = file_get_contents($path);
    if ($content === false) {
        $failures[] = 'unreadable file: ' . $path;
        return '';
    }
    return $content;
}

function method_body($php, $methodName)
{
    $tokens = token_get_all($php);
    $count = count($tokens);
    for ($i = 0; $i < $count; $i++) {
        if (!is_array($tokens[$i]) || $tokens[$i][0] !== T_FUNCTION) {
            continue;
        }
        $j = $i + 1;
        while ($j < $count && (is_array($tokens[$j]) && in_array($tokens[$j][0], array(T_WHITESPACE, T_AMPERSAND_NOT_FOLLOWED_BY_VAR_OR_VARARG, T_AMPERSAND_FOLLOWED_BY_VAR_OR_VARARG), true))) {
            $j++;
        }
        if ($j >= $count || !is_array($tokens[$j]) || $tokens[$j][0] !== T_STRING || $tokens[$j][1] !== $methodName) {
            continue;
        }
        while ($j < $count && $tokens[$j] !== '{') {
            $j++;
        }
        if ($j >= $count) {
            return null;
        }
        $depth = 0;
        $body = '';
        for (; $j < $count; $j++) {
            $token = $tokens[$j];
            $text = is_array($token) ? $token[1] : $token;
            if ($text === '{') {
                $depth++;
                if ($depth === 1) {
                    continue;
                }
            } elseif ($text === '}') {
                $depth--;
                if ($depth === 0) {
                    return $body;
                }
            }
            if ($depth >= 1) {
                $body .= $text;
            }
        }
    }
    return null;
}

$appRoot = realpath(__DIR__ . '/..');
$failures = array();
$checks = array();

$servicePath = $appRoot . '/services/billing/BillingPayablesService.php';
$endpointPath = $appRoot . '/modules/billing/payable_update_ajax.php';
$uiPath = $appRoot . '/modules/billing/payables.php';

$service = read_file_or_fail($servicePath, $failures);
$endpoint = read_file_or_fail($endpointPath, $failures);
$ui = read_file_or_fail($uiPath, $failures);
$updateBody = $service !== '' ? method_body($service, 'updateDocument') : null;
$linkBody = $service !== '' ? method_body($service, 'autoLinkSupplier') : null;
$paymentBody = $service !== '' ? method_body($service, 'registerPayment') : null;
$bulkBody = $service !== '' ? method_body($service, 'registerBulkPayment') : null;

$checks['updateDocument method found'] = $updateBody !== null;
$checks['materialize flag parsed'] = $updateBody !== null && strpos($updateBody, "!empty(\$data['materialize_collection'])") !== false;
$checks['collection creation conditional'] = $updateBody !== null && strpos($updateBody, 'if ($existingCollection || $materializeCollection)') !== false;
$checks['supplier required for new CxP'] = $updateBody !== null && strpos($updateBody, 'Vincula un proveedor antes de crear la cuenta por pagar.') !== false;
$checks['center required for new CxP'] = $updateBody !== null && strpos($updateBody, 'Selecciona un centro de costo antes de crear la cuenta por pagar.') !== false;
$checks['payment terms required for new CxP'] = $updateBody !== null && strpos($updateBody, 'Define una condición de pago para el proveedor antes de crear la cuenta por pagar.') !== false;
$checks['identity guard present'] = $updateBody !== null && strpos($updateBody, 'expected_inbox_document_id') !== false;
$checks['supplier linking does not create CxP'] = $linkBody !== null && strpos($linkBody, 'ensurePayableCollection') === false;
$checks['single payment requires existing CxP'] = $paymentBody !== null && strpos($paymentBody, 'getPayableCollectionByInboxId') !== false && strpos($paymentBody, 'Crea primero la cuenta por pagar antes de registrar un pago.') !== false;
$checks['bulk payment requires existing CxP'] = $bulkBody !== null && strpos($bulkBody, 'getPayableCollectionByInboxId') !== false && strpos($bulkBody, 'deben tener una cuenta por pagar creada previamente') !== false;
$checks['endpoint forwards explicit flag'] = strpos($endpoint, "'materialize_collection' => !empty(\$_POST['materialize_collection'])") !== false;
$checks['endpoint forwards identity guard'] = strpos($endpoint, "'expected_inbox_document_id'") !== false;
$checks['UI explicit CTA'] = strpos($ui, 'Crear cuenta por pagar') !== false;
$checks['UI confirmation'] = strpos($ui, 'Esta acción no registra pagos ni acepta el documento en FACTO.') !== false;
$checks['UI sends explicit flag'] = strpos($ui, "fd.append('materialize_collection', materialize ? '1' : '0')") !== false;
$checks['UI sends inbox identity'] = strpos($ui, "fd.append('expected_inbox_document_id'") !== false;
$checks['payment action hidden before collection'] = strpos($ui, 'hasCollection && balance > 0.5') !== false;
$checks['bulk selection requires collection'] = strpos($ui, 'Number(r.collection_id || 0) > 0 && balanceOf(r) > 0.5') !== false;

foreach ($checks as $label => $ok) {
    if (!$ok) $failures[] = $label;
}

$environment = defined('ICYLAB_FACTO_ENVIRONMENT') ? ICYLAB_FACTO_ENVIRONMENT : 'undefined';
$dbName = defined('DB_NAME') ? DB_NAME : (defined('ICYLAB_DB_NAME') ? ICYLAB_DB_NAME : 'undefined');

$gateRead = null;
$gateFinance = null;
$gateIssue = null;
try { billing_assert_runtime_operation('read', 'production', 'diagnostic v6.14'); $gateRead = true; } catch (Throwable $e) { $gateRead = false; }
try { billing_assert_runtime_operation('finance', 'production', 'diagnostic v6.14'); $gateFinance = true; } catch (Throwable $e) { $gateFinance = false; }
try { billing_assert_runtime_operation('issue', 'production', 'diagnostic v6.14'); $gateIssue = true; } catch (Throwable $e) { $gateIssue = false; }

if ($environment !== 'production') $failures[] = 'default FACTO environment is not production';
if ($dbName !== 'icylab_app') $failures[] = 'database is not icylab_app';
if ($gateRead !== true) $failures[] = 'production/read must be allowed';
if ($gateFinance !== false) $failures[] = 'production/finance must remain blocked before pilot';
if ($gateIssue !== false) $failures[] = 'production/issue must remain blocked';

echo "ICYLAB BILLING EXPLICIT CxP MATERIALIZATION DIAGNOSTIC v6.14\n";
echo 'Generated at UTC: ' . gmdate('Y-m-d H:i:s') . "\n";
echo 'PHP: ' . PHP_VERSION . "\n";
echo 'App root: ' . $appRoot . "\n";
echo 'DB name: ' . $dbName . "\n";
echo 'Default FACTO environment: ' . $environment . "\n\n";
foreach ($checks as $label => $ok) echo $label . ': ' . ($ok ? 'PASS' : 'FAIL') . "\n";
echo "\nRUNTIME GATES\n";
echo 'production/read: ' . ($gateRead ? 'ALLOWED' : 'BLOCKED') . " expected=ALLOWED\n";
echo 'production/finance: ' . ($gateFinance ? 'ALLOWED' : 'BLOCKED') . " expected=BLOCKED\n";
echo 'production/issue: ' . ($gateIssue ? 'ALLOWED' : 'BLOCKED') . " expected=BLOCKED\n";
echo "\nRESULT: " . (empty($failures) ? 'PASS' : 'FAIL') . "\n";
if ($failures) {
    foreach ($failures as $failure) echo '- ' . $failure . "\n";
    exit(1);
}
