<?php

declare(strict_types=1);

// A/B developer MySQL, one synthetic user's balance-log check-only source.
$stage = 'default-deny';
set_exception_handler(static function (Throwable $e) use (&$stage): void {
    echo json_encode(['status' => 'redacted-error', 'stage' => $stage,
        'error_class' => get_class($e), 'error_code' => (string)$e->getCode()],
        JSON_UNESCAPED_SLASHES), PHP_EOL;
    exit(2);
});
$lane = (string)getenv('CDX_BALANCE_AB_LANE');
if (!in_array($lane, ['A', 'B'], true)
    || count($argv) !== 2 || ($argv[1] ?? '') !== '--preflight-only'
    || (string)getenv('CDX_BALANCE_AB_PREFLIGHT')
        !== 'ROOT_APPROVED_BALANCE_SELECT_V1'
    || (string)getenv('CDX_BALANCE_AB_PREFLIGHT_SHA')
        !== hash_file('sha256', __FILE__)) {
    echo json_encode(['status' => 'default-deny',
        'db_connected' => false, 'think_command_invoked' => false,
        'business_http_sent' => false, 'dml_sent' => false]), PHP_EOL;
    exit(78);
}
if (PHP_VERSION_ID < 70400 || PHP_VERSION_ID >= 70500
    || realpath(PHP_BINARY)
        !== '/opt/homebrew/Cellar/php@7.4/7.4.33_13/bin/php') {
    throw new RuntimeException('Approved local PHP7.4 CLI differs');
}
$targets = [
    'A' => ['/private/tmp/zhct-http-ai-macos.zjaU8h', 18105, 13109,
        'e7402b1ad50fcf4205326d93dc22e4f491c5052c60fcbe36214cd4e07b55343c'],
    'B' => ['/private/tmp/zhct-http-ai-macos.x2CCZX', 18101, 13127,
        '8c46a6d9cd277f2950818d09162f639a62f135a5eb6553e6c61ce2c19167fd02'],
];
[$root, $port, $pid, $databaseConfigSha] = $targets[$lane];
$stage = 'isolated-source';
if (is_link($root) || !is_file($root . '/think-poc')
    || hash_file('sha256', $root . '/think-poc')
        !== 'cbd8c0b81a4ffe64b68ed931bf1bbb08bbe5e7537f991d45c0be018d48e6e5cb'
    || hash_file('sha256', $root . '/config/database.php')
        !== $databaseConfigSha
    || hash_file('sha256', $root . '/app/command/InitBalanceLogFields.php')
        !== 'de886446344435f7a819105788ffc699c71a959119877b4b2e5de7dbe37f0b62'
    || strpos((string)file_get_contents($root . '/config/console.php'),
        'app\\command\\InitBalanceLogFields') === false) {
    throw new RuntimeException('Approved A/B balance CLI source changed');
}
$pids = array_values(array_filter(explode("\n", trim((string)shell_exec(
    '/usr/sbin/lsof -t -nP -iTCP:' . $port . ' -sTCP:LISTEN 2>/dev/null'))),
    static function (string $value): bool { return ctype_digit($value); }));
if (count($pids) !== 1 || (int)$pids[0] !== $pid) {
    throw new RuntimeException('A/B approved listener PID changed');
}
$process = (string)shell_exec('/bin/ps eww -p ' . $pid . ' -o command=');
if (strpos($process, '-S 127.0.0.1:' . $port) === false
    || (strpos($process, $root . '/public') === false
        && strpos($process,
            str_replace('/private/tmp/', '/tmp/', $root) . '/public') === false)
    || strpos($process, 'PHP_CLI_SERVER_WORKERS=') !== false) {
    throw new RuntimeException('A/B isolated process root changed');
}
$envFile = '/Users/wiscourper/Documents/workspace/zhct/zhctproject/ai_api/.env.local';
if (realpath($envFile) !== $envFile) {
    throw new RuntimeException('Code-local AI configuration unavailable');
}
$ini = parse_ini_file($envFile, true, INI_SCANNER_RAW);
if (!is_array($ini) || !isset($ini['DATABASE'], $ini['DATABASE_ZHCT'])) {
    throw new RuntimeException('Both code-local business sections required');
}
$config = [];
foreach (['DATABASE' => 'POC_AI_', 'DATABASE_ZHCT' => 'POC_']
    as $section => $prefix) {
    foreach (['HOSTNAME' => 'HOST', 'HOSTPORT' => 'PORT',
        'DATABASE' => 'SCHEMA', 'USERNAME' => 'USER',
        'PASSWORD' => 'PASSWORD'] as $field => $suffix) {
        $key = $prefix . $suffix;
        $value = (string)($ini[$section][$field] ?? '');
        if ($value === '' || preg_match('/(?:^|\s)'
            . preg_quote($key . '=' . $value, '/') . '(?=\s|$)/', $process) !== 1) {
            throw new RuntimeException('A/B runtime differs from code local');
        }
        $config[$key] = $value;
    }
}
$boundary = json_decode((string)file_get_contents(
    'http://127.0.0.1:' . $port . '/__poc_boundary', false,
    stream_context_create(['http' => ['timeout' => 8,
        'follow_location' => 0, 'ignore_errors' => true]])), true);
if (!is_array($boundary) || ($boundary['external_stubs'] ?? null) !== true) {
    throw new RuntimeException('A/B isolated external stub boundary absent');
}
$stage = 'approved-dev-ai-database';
$dsn = 'mysql:host=' . $config['POC_AI_HOST']
    . ';port=' . $config['POC_AI_PORT']
    . ';dbname=' . $config['POC_AI_SCHEMA'] . ';charset=utf8';
$db = new PDO($dsn, $config['POC_AI_USER'], $config['POC_AI_PASSWORD'], [
    PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
    PDO::ATTR_CASE => PDO::CASE_LOWER,
    PDO::ATTR_TIMEOUT => 5,
]);
$identity = (string)$db->query(
    "SELECT CONCAT(@@server_uuid,'|',@@hostname,'|',@@port,'|',DATABASE())")
    ->fetchColumn();
$approvedDbSha = '210f925e093f71e26a8c8923f0f626e3be0a15ccff3d386bba21feef695c8a5b';
if (!hash_equals($approvedDbSha, hash('sha256', $identity))) {
    throw new RuntimeException('Not approved developer AI MySQL database');
}
$stage = 'balance-schema-and-size';
$db->query('SELECT USER_ID,STORE_ID,NICK_NAME,BALANCE,SUBSIDY_BALANCE'
    . ' FROM yoshop_user WHERE 1=0');
$db->query('SELECT LOG_ID,USER_ID,STORE_ID,MONEY,BALANCE_AFTER'
    . ' FROM yoshop_user_balance_log WHERE 1=0');
$balanceTotal = (int)$db->query('SELECT COUNT(*) FROM yoshop_user_balance_log')
    ->fetchColumn();
if ($balanceTotal < 0 || $balanceTotal > 100000) {
    throw new RuntimeException('Balance log size exceeds approved cap');
}
// Diagnostic only: no subsidy row fetch/digest and no 10k subsidy gate here.
$subsidyTotal = (int)$db->query('SELECT COUNT(*) FROM yoshop_user_subsidy_log')
    ->fetchColumn();
$stage = 'unique-synthetic-user';
$users = $db->query('SELECT USER_ID,STORE_ID,NICK_NAME FROM yoshop_user'
    . " WHERE NICK_NAME LIKE 'CDXPAY%' AND IS_DELETE=0"
    . ' ORDER BY USER_ID DESC LIMIT 50')->fetchAll(PDO::FETCH_ASSOC);
$uniqueUser = $db->prepare('SELECT COUNT(*) FROM yoshop_user'
    . ' WHERE NICK_NAME=? AND STORE_ID=? AND IS_DELETE=0');
$logsStmt = $db->prepare('SELECT LOG_ID,USER_ID,STORE_ID,MONEY,BALANCE_AFTER'
    . ' FROM yoshop_user_balance_log WHERE USER_ID=? AND STORE_ID=?'
    . ' ORDER BY LOG_ID DESC LIMIT 201');
$digestRows = static function (array $rows): string {
    $hash = hash_init('sha256');
    foreach ($rows as $row) {
        hash_update($hash, json_encode(array_map(static function ($v): string {
            return is_resource($v) ? (string)stream_get_contents($v) : (string)$v;
        }, $row), JSON_UNESCAPED_UNICODE | JSON_INVALID_UTF8_SUBSTITUTE) . "\n");
    }
    return hash_final($hash);
};
$selected = null;
foreach ($users as $user) {
    $marker = (string)$user['nick_name'];
    $userId = (int)$user['user_id'];
    $storeId = (int)$user['store_id'];
    if (!preg_match('/^CDXPAY[A-F0-9]{8}$/D', $marker)
        || $userId <= 0 || $storeId !== 10001) continue;
    $uniqueUser->execute([$marker, $storeId]);
    if ((int)$uniqueUser->fetchColumn() !== 1) continue;
    $logsStmt->execute([$userId, $storeId]);
    $logs = $logsStmt->fetchAll(PDO::FETCH_ASSOC);
    if (!$logs || count($logs) > 200) continue;
    $cutoffId = (int)$logs[0]['log_id'];
    if ($cutoffId <= 0) continue;
    $missingStmt = $db->prepare('SELECT COUNT(*)'
        . ' FROM yoshop_user_balance_log WHERE BALANCE_AFTER IS NULL'
        . ' AND STORE_ID=? AND USER_ID=? AND LOG_ID<=?');
    $missingStmt->execute([$storeId, $userId, $cutoffId]);
    $userStmt = $db->prepare('SELECT USER_ID,STORE_ID,NICK_NAME,BALANCE,'
        . ' SUBSIDY_BALANCE FROM yoshop_user WHERE USER_ID=? AND STORE_ID=?');
    $userStmt->execute([$userId, $storeId]);
    $userRows = $userStmt->fetchAll(PDO::FETCH_ASSOC);
    if (count($userRows) !== 1) continue;
    $selected = [
        'marker' => $marker, 'store_id' => $storeId, 'user_id' => $userId,
        'cutoff_log_id' => $cutoffId, 'owned_log_rows' => count($logs),
        'expected_missing_count' => (int)$missingStmt->fetchColumn(),
        'user_digest' => hash('sha256', serialize($userRows[0])),
        'balance_log_digest' => $digestRows($logs),
    ];
    break;
}
if ($selected === null) {
    throw new RuntimeException('No unique bounded synthetic balance source');
}
echo json_encode(['status' => 'balance-only-preflight', 'lane' => $lane,
    'developer_ai_db_fingerprint' => $approvedDbSha,
    'balance_log_total_rows' => $balanceTotal,
    'subsidy_log_total_count_only' => $subsidyTotal,
    'approved_synthetic_source' => $selected,
    'think_command_invoked' => false, 'business_http_sent' => false,
    'dml_sent' => false], JSON_UNESCAPED_SLASHES), PHP_EOL;
