<?php
declare(strict_types=1);

// Bounded AI-only read smoke. No business DML, no external URL, no redirects.
$mode = (string)($argv[1] ?? '');
$lane = (string)getenv('CDX_BATCH_LANE');
$tail = in_array($mode, ['--preflight-tail', '--run-tail'], true);
$head = in_array($mode, ['--preflight-head', '--run-head'], true);
$crossHead = in_array($mode, ['--preflight-cross-head', '--run-cross-head'], true);
$crossTail = in_array($mode, ['--preflight-cross-tail', '--run-cross-tail'], true);
$infoValue = in_array($mode, ['--preflight-info-value', '--run-info-value',
    '--token-zero-info-value'], true);
$infoFixed = in_array($mode, ['--preflight-user-info-fixed',
    '--run-user-info-fixed', '--token-zero-user-info-fixed'], true);
$dishesFixed = in_array($mode, ['--preflight-dishes-fixed',
    '--run-dishes-fixed', '--token-zero-dishes-fixed'], true);
$nextNatural = in_array($mode, ['--preflight-next-natural',
    '--run-next-natural', '--token-zero-next-natural'], true);
$nextOwned = in_array($mode, ['--preflight-next-owned',
    '--run-next-owned', '--token-zero-next-owned'], true);
$next = $nextNatural || $nextOwned;
$cross = in_array($mode, ['--preflight-cross', '--run-cross',
    '--preflight-cross-head', '--run-cross-head',
    '--preflight-cross-tail', '--run-cross-tail', '--token-zero-cross',
    '--preflight-info-value', '--run-info-value', '--token-zero-info-value',
    '--preflight-user-info-fixed', '--run-user-info-fixed',
    '--token-zero-user-info-fixed'], true);
if (!in_array($mode, ['--preflight', '--preflight-tail', '--preflight-head',
        '--preflight-cross', '--preflight-cross-head', '--preflight-cross-tail',
        '--run-pure', '--run-tail', '--run-head', '--run-cross',
        '--run-cross-head', '--run-cross-tail', '--token-zero',
        '--token-zero-cross', '--preflight-info-value', '--run-info-value',
        '--token-zero-info-value', '--preflight-user-info-fixed',
        '--run-user-info-fixed', '--token-zero-user-info-fixed',
        '--preflight-dishes-fixed', '--run-dishes-fixed',
        '--token-zero-dishes-fixed', '--preflight-next-natural',
        '--run-next-natural', '--token-zero-next-natural',
        '--preflight-next-owned', '--run-next-owned',
        '--token-zero-next-owned'], true)
    || !in_array($lane, ['A', 'B', 'C'], true)
    || ($mode !== '--preflight' && $mode !== '--preflight-tail'
        && $mode !== '--preflight-head' && $mode !== '--preflight-cross'
        && $mode !== '--preflight-cross-head'
        && $mode !== '--preflight-cross-tail'
        && $mode !== '--preflight-info-value'
        && $mode !== '--preflight-user-info-fixed'
        && $mode !== '--preflight-dishes-fixed'
        && $mode !== '--preflight-next-natural'
        && $mode !== '--preflight-next-owned'
        && ((string)getenv('CDX_BATCH_APPROVED') !== ($mode === '--run-tail'
                ? 'ROOT_APPROVED_AI_PURE_TAIL_V1'
                : ($mode === '--run-head' ? 'ROOT_APPROVED_AI_PURE_HEAD_V1'
                    : ($crossHead ? 'ROOT_APPROVED_AI_CROSS_HEAD3_V1'
                        : ($crossTail ? 'ROOT_APPROVED_AI_CROSS_TAIL4_V1'
                            : ($infoValue ? 'ROOT_APPROVED_C_USER_INFO_VALUES_V1'
                                : ($infoFixed ? 'ROOT_APPROVED_USER_INFO_FIXED2_V1'
                                    : ($dishesFixed ? 'ROOT_APPROVED_DISHES_DECIMAL_RETEST_V1'
                                        : ($next ? 'ROOT_APPROVED_AI_NEXT_READ_V1'
                                            : ($cross ? 'ROOT_APPROVED_AI_CROSS_BATCH_V1'
                                                : 'ROOT_APPROVED_AI_PURE_BATCH_V1')))))))))
            || (string)getenv('CDX_BATCH_SHA') !== hash_file('sha256', __FILE__)))
    || ($tail && (string)getenv('CDX_BATCH_UNKNOWN_ROUTE')
        !== 'GET /api/health.Metric/trend')
    || ($head && (string)getenv('CDX_BATCH_HEAD_BOUNDARY')
        !== 'BEFORE_UNKNOWN_TREND_V1')
    || ($cross && (string)getenv('CDX_BATCH_CROSS_GATE')
        !== 'AFTER_USER_DATA_ZERO_V1')
    || (($crossHead || $crossTail)
        && (string)getenv('CDX_BATCH_CROSS_SKIP')
            !== 'POST /api/User/zhctInfo')
    || ($infoValue && ($lane !== 'C'
        || (string)getenv('CDX_BATCH_INFO_VALUE_GATE')
            !== 'DM_NEW_DECIMAL_COLUMNS_V1'))
    || ($infoFixed && (string)getenv('CDX_BATCH_INFO_FIXED_GATE')
        !== 'USER_SEVEN_FIXED2_V1')
    || ($dishesFixed && (string)getenv('CDX_BATCH_DISHES_FIXED_GATE')
        !== 'DISHES_SIX_FIELDS_FIXED2_V1')
    || ($next && (string)getenv('CDX_BATCH_NEXT_GATE')
        !== 'NEXT_ELEVEN_SAFE_READ_V1')) {
    echo json_encode(['status' => 'default_deny', 'http' => 0,
        'business_dml' => 0]), PHP_EOL;
    exit(78);
}
require_once __DIR__ . '/batch_plan.php';
$branchFile = __DIR__ . '/batch_branch.php';
if (!is_file($branchFile) || hash_file('sha256', $branchFile)
    !== 'a3ccd89e5d077627328aa5a3faa07a99f13f70d34806ebddb15104dcacd0da51') {
    throw new RuntimeException('Nested response branch policy drift');
}
require_once $branchFile;
$routes = array_values(array_filter(batch_smoke_routes(), static function (array $r): bool {
    return preg_match('#^/api/(UserMotion/user(?:Day|Week)Steps|Sport/all|sport\.(Cate|Item)/|health\.Metric/|Dishes/list|dishes\.Dishes/cate|Activity(Type)?/list|drill\.)#i',
        $r['path']) === 1;
}));
if (count($routes) !== 20) throw new RuntimeException('AI pure allowlist count drift');
if ($tail) {
    if ($routes[10]['path'] !== '/api/health.Metric/trend'
        || $routes[11]['path'] !== '/api/health.Metric/records') {
        throw new RuntimeException('Unknown-route skip boundary drift');
    }
    $routes = array_slice($routes, 11);
    if (count($routes) !== 9) throw new RuntimeException('Tail allowlist count drift');
}
if ($head) {
    if ($routes[9]['path'] !== '/api/health.Metric/overview'
        || $routes[10]['path'] !== '/api/health.Metric/trend') {
        throw new RuntimeException('Head/unknown boundary drift');
    }
    $routes = array_slice($routes, 0, 10);
    if (count($routes) !== 10) throw new RuntimeException('Head allowlist count drift');
}
if ($cross) {
    $crossPaths = ['/api/Setting/aiface', '/api/User/info',
        '/api/User/assets', '/api/User/zhctInfo', '/api/user.Disease/info',
        '/api/Restaurant/list', '/api/Restaurant/areaList', '/api/Cashout/list'];
    $routes = array_values(array_filter(batch_smoke_routes(),
        static function (array $r) use ($crossPaths): bool {
            return in_array($r['path'], $crossPaths, true);
        }));
    if (count($routes) !== 8) throw new RuntimeException('Cross allowlist count drift');
    if ($crossHead || $crossTail) {
        if ($routes[2]['path'] !== '/api/User/assets'
            || $routes[3]['path'] !== '/api/User/zhctInfo'
            || $routes[4]['path'] !== '/api/user.Disease/info') {
            throw new RuntimeException('Cross unknown-route boundary drift');
        }
        $routes = $crossHead ? array_slice($routes, 0, 3)
            : array_slice($routes, 4);
        if (count($routes) !== ($crossHead ? 3 : 4)) {
            throw new RuntimeException('Cross resumed allowlist count drift');
        }
    }
    if ($infoValue || $infoFixed) {
        $routes = array_values(array_filter($routes,
            static function (array $r): bool {
                return $r['path'] === '/api/User/info';
            }));
        if (count($routes) !== 1) throw new RuntimeException('Info value route drift');
    }
}
if ($dishesFixed) {
    $routes = array_values(array_filter($routes,
        static function (array $r): bool {
            return $r['path'] === '/api/Dishes/list';
        }));
    if (count($routes) !== 1) throw new RuntimeException('Dishes retest route drift');
}
if ($next) {
    $nextPlanFile = $lane === 'C'
        ? '/tmp/zhct-ai-next-read-20261004/next_plan.php'
        : __DIR__ . '/../parallel-ai-next-read-smoke-20261004/next_plan.php';
    if (!is_file($nextPlanFile)) throw new RuntimeException('Next plan missing');
    require_once $nextPlanFile;
    $allNext = next_read_routes();
    if (count($allNext) !== 11) throw new RuntimeException('Next route count drift');
    $routes = $nextNatural ? array_slice($allNext, 0, 3)
        : array_slice($allNext, 3);
    if (count($routes) !== ($nextNatural ? 3 : 8)) {
        throw new RuntimeException('Next group boundary drift');
    }
}
$identity = $lane === 'C'
    ? '/tmp/zhct-ai-read2-VgKO4CDY/read2_schema_preflight.php'
    : __DIR__ . '/../parallel-ai-next-read-safe4/read2_schema_preflight.php';
if (!is_file($identity) || hash_file('sha256', $identity)
    !== '8641ee015ad537d300de9c00f2c882e654b739d7c2194dd13ef4635ca9df1af0') {
    throw new RuntimeException('Identity source drift');
}
putenv('CDX_READ2_TARGET=' . $lane);
putenv('CDX_READ2_SCHEMA_PREFLIGHT=1');
require $identity;
$stage = 'batch_runtime';
$redactedError = static function (Throwable $e) use (&$stage): void {
    echo json_encode(['status' => 'redacted_stop', 'stage' => $stage,
        'class' => get_class($e), 'code' => (string)$e->getCode(),
        'reason_sha256' => hash('sha256', $e->getMessage()),
        'business_dml' => 0]), PHP_EOL;
    exit(2);
};
set_exception_handler($redactedError);
$root = ['A' => '/private/tmp/zhct-http-ai-macos.zjaU8h',
    'B' => '/private/tmp/zhct-http-ai-macos.x2CCZX',
    'C' => '/tmp/zhct-ai-http-20260929.3XDj3w'][$lane];
$source = ['A' => '/Users/wiscourper/Documents/workspace/local/dm8-full-acceptance.fF5yTy/baseline-a-ai-api-V4.0.40',
    'B' => '/Users/wiscourper/Documents/workspace/local/dm8-full-acceptance.fF5yTy/ai_api',
    'C' => '/tmp/zhct-dm-poc-20260928.fcpiiN/ai_api'][$lane];
$cache = realpath($root . '/runtime/cache');
if (realpath($root) !== $root || $cache !== $root . '/runtime/cache'
    || (((int)fileperms($root) & 0077) !== 0)
    || hash_file('sha256', $root . '/config/cache.php')
        !== 'ccf1c56eb4178f3eaad8775e26a27742b2cacf662b46fb058cbb91d23783b8c6'
    || is_file($root . '/.env') || is_file($root . '/.env.poc')
    || getenv('CACHE_DRIVER') !== false || getenv('cache.driver') !== false) {
    throw new RuntimeException('Private File cache boundary drift');
}
$files = ['app/api/controller/UserMotion.php', 'app/api/controller/Sport.php',
    'app/api/controller/sport/Cate.php', 'app/api/controller/sport/Item.php',
    'app/api/controller/health/Metric.php', 'app/api/controller/Dishes.php',
    'app/api/controller/dishes/Dishes.php', 'app/api/controller/Activity.php',
    'app/api/controller/ActivityType.php', 'app/api/controller/drill/Course.php',
    'app/api/controller/drill/CourseType.php',
    'app/api/service/sport/Item.php', 'app/api/service/sport/Record.php',
    'app/api/service/health/Metric.php', 'app/api/service/Activity.php',
    'app/api/service/drill/Course.php'];
if ($cross) {
    $files = array_merge($files, [
        'app/api/controller/Setting.php', 'app/api/controller/User.php',
        'app/api/controller/user/Disease.php',
        'app/api/controller/Restaurant.php', 'app/api/controller/Cashout.php',
        'app/api/service/Setting.php', 'app/api/service/User.php',
        'app/api/service/Cashout.php', 'app/api/model/Restaurant.php',
        'app/api/model/User.php',
    ]);
}
if ($dishesFixed) {
    $files = array_merge($files, ['app/api/service/dishes/Dishes.php',
        'app/api/model/User.php']);
    $expectedDishesSha = $lane === 'A'
        ? '74a89733eceead09e59149f941a8e6a15eed9bea831a6fb4eae50c19ce112c15'
        : '9edc09427ee4ea18b50ad83b7b21c5ab08e1115c98433ca18aae9b59b5c9b39d';
    if (hash_file('sha256', $source . '/app/api/service/dishes/Dishes.php')
        !== $expectedDishesSha) {
        throw new RuntimeException('Dishes actual Composer source SHA drift');
    }
}
if ($next) {
    $files = array_merge($files, [
        'app/api/controller/Cashout.php',
        'app/api/controller/user/UserCourse.php',
        'app/api/controller/WellandScale.php',
        'app/api/controller/meal/Meal.php',
        'app/api/controller/wolland/Rope.php',
        'app/api/service/Cashout.php',
        'app/api/service/user/UserCourse.php',
        'app/api/service/WellandScale.php',
        'app/api/service/meal/Meal.php',
        'app/api/service/WellandRope.php',
        'app/api/model/User.php',
    ]);
}
$hashes = [];
foreach ($files as $relative) {
    $file = $source . '/' . $relative;
    if (!is_file($file)) throw new RuntimeException('Selected source missing');
    $hashes[] = hash_file('sha256', $file);
}
$sourceSha = hash('sha256', implode('|', $hashes));
$planSha = hash_file('sha256', $next ? $nextPlanFile
    : __DIR__ . '/batch_plan.php');
if (!in_array($mode, ['--preflight', '--preflight-tail', '--preflight-head',
        '--preflight-cross', '--preflight-cross-head',
        '--preflight-cross-tail', '--preflight-info-value',
        '--preflight-user-info-fixed', '--preflight-dishes-fixed',
        '--preflight-next-natural', '--preflight-next-owned'], true)
    && ((string)getenv('CDX_BATCH_SOURCE_SHA') !== $sourceSha
        || (string)getenv('CDX_BATCH_PLAN_SHA') !== $planSha)) {
    throw new RuntimeException('Plan or source changed since preflight');
}
foreach ($config as $key => $value) {
    if (strpos($key, 'POC_') === 0) putenv($key . '=' . $value);
}
define('TP_ENV', 'poc');
define('BUSINESS', 'aizhct');
define('ENTERPRISE_NO', '10001');
require $root . '/vendor/autoload.php';
(new \think\App($root))->setEnvName('poc')->initialize();
set_exception_handler($redactedError);
if (strtolower((string)\think\facade\Config::get('cache.default')) !== 'file'
    || realpath((string)\think\facade\Config::get('cache.stores.file.path')) !== $cache
    || (string)\think\facade\Config::get('cache.stores.file.prefix') !== '') {
    throw new RuntimeException('CLI File cache differs from HTTP');
}
$ai->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
$store->setAttribute(PDO::ATTR_CASE, PDO::CASE_LOWER);
$select = static function (PDO $pdo, string $sql, array $bind = [], int $cap = 101): array {
    $stmt = $pdo->prepare($sql);
    $stmt->execute($bind);
    $rows = $stmt->fetchAll(PDO::FETCH_ASSOC);
    if (count($rows) > $cap) throw new RuntimeException('Owned row cap exceeded');
    foreach ($rows as &$row) {
        $row = array_change_key_case($row, CASE_LOWER);
        foreach ($row as &$value) if (is_resource($value)) $value = stream_get_contents($value);
        unset($value);
        ksort($row, SORT_STRING);
    }
    unset($row);
    return $rows;
};
$table = static function (string $side, string $name) use ($dialect): string {
    $prefix = $side === 'ai' ? 'YOSHOP_' : 'YDY_';
    return $dialect === 'dm' ? $prefix . strtoupper($name)
        : strtolower($prefix . $name);
};
$name = $lane === 'C' ? 'CDXPARMOB9411C2CB' : 'CDXPARMOB426CA4B0';
$actor = static function () use ($ai, $store, $select, $table, $name): array {
    $users = $select($ai, 'SELECT * FROM ' . $table('ai', 'user')
        . ' WHERE NICK_NAME=? AND STORE_ID=10001 AND IS_DELETE=0', [$name], 1);
    if (count($users) !== 1 || (int)$users[0]['user_id'] <= 0) {
        throw new RuntimeException('Synthetic AI actor not unique');
    }
    $id = (int)$users[0]['user_id'];
    $stores = $select($store, 'SELECT * FROM ' . $table('store', 'user')
        . ' WHERE AI_USER_ID=? AND IS_DELETE=0', [$id], 1);
    if (count($stores) !== 1 || !isset($stores[0]['password_version'])) {
        throw new RuntimeException('Synthetic Store mapping/version not unique');
    }
    return ['id' => $id, 'version' => (int)$stores[0]['password_version'],
        'sha256' => hash('sha256', serialize([$users, $stores]))];
};
$owner = $actor();
$probe = static function (array $route) use ($ai, $store, $select, $table, $owner,
    $dialect): array {
    $pdo = $route['source_side'] === 'ai' ? $ai : $store;
    $name = $table($route['source_side'], $route['source_table']);
    $column = $route['owner_column'] ?? (($route['owned'] ?? false)
        ? ($route['source_side'] === 'store' && $route['source_table'] === 'user'
            ? 'AI_USER_ID' : 'USER_ID') : '');
    $owned = $column !== '';
    $ownerId = $owner['id'];
    if ($column === 'STAFF_ID') {
        $staff = $select($ai, 'SELECT ID FROM ' . $table('ai', 'staff')
            . ' WHERE USER_ID=? AND IS_DELETE=0', [$owner['id']], 1);
        if (count($staff) !== 1 || (int)$staff[0]['id'] <= 0) {
            return ['state' => 'source_actor_staff_absent'];
        }
        $ownerId = (int)$staff[0]['id'];
    }
    $cap = $owned ? 101 : 1;
    $sql = 'SELECT 1 FROM ' . $name . ($owned ? ' WHERE ' . $column . '=?' : '');
    $sql .= $dialect === 'dm' ? ($owned ? ' AND ROWNUM<=' : ' WHERE ROWNUM<=')
        . $cap : ' LIMIT ' . $cap;
    try {
        $rows = $select($pdo, $sql, $owned ? [$ownerId] : [], $cap);
        return ['state' => 'source_read', 'rows_sampled' => count($rows),
            'capped' => $owned && count($rows) === 101];
    } catch (PDOException $e) {
        $code = (string)$e->getCode();
        if (in_array($code, ['2002', '2006', 'HY000', '08S01'], true)) throw $e;
        return ['state' => 'source_sql_blocked', 'code' => $code];
    }
};
$resolveParams = static function (array $route) use ($ai, $table, $dialect): ?array {
    $params = $route['params'];
    $selector = $route['selector'] ?? '';
    if ($selector === '') return $params;
    if ($selector === 'sport_item_id') {
        $name = $table('ai', 'sport_item');
        $sql = 'SELECT ID FROM ' . $name
            . ' WHERE STORE_ID=10001 AND IS_DELETE=0';
        $param = 'sport_item_id';
    } elseif ($selector === 'course_id') {
        $name = $table('ai', 'drill_course');
        $sql = 'SELECT ID FROM ' . $name
            . ' WHERE STORE_ID=10001 AND IS_DELETE=0';
        $param = 'course_id';
    } elseif ($selector === 'activity_id') {
        $name = $table('ai', 'activity');
        $sql = 'SELECT ID FROM ' . $name
            . ' WHERE STORE_ID=10001 AND STATUS=1'
            . ' AND START_TIME IS NOT NULL AND END_TIME IS NOT NULL';
        $param = 'id';
    } else {
        throw new RuntimeException('Unreviewed natural selector');
    }
    $sql .= $dialect === 'dm' ? ' AND ROWNUM<=1' : ' ORDER BY ID ASC LIMIT 1';
    try {
        $id = $ai->query($sql)->fetchColumn();
    } catch (PDOException $e) {
        if (in_array((string)$e->getCode(), ['2002', '2006', '08S01'], true)) throw $e;
        return null;
    }
    if (!is_numeric($id) || (int)$id <= 0) return null;
    $params[$param] = (int)$id;
    return $params;
};
$preflight = [];
foreach ($routes as $r) {
    $key = $r['method'] . ' ' . $r['path'];
    $preflight[$key] = $probe($r);
    if ($next && ($r['selector'] ?? '') !== '') {
        $preflight[$key]['selector_present'] = $resolveParams($r) !== null;
    }
}
if (in_array($mode, ['--preflight', '--preflight-tail', '--preflight-head',
        '--preflight-cross', '--preflight-cross-head',
        '--preflight-cross-tail', '--preflight-info-value',
        '--preflight-user-info-fixed', '--preflight-dishes-fixed',
        '--preflight-next-natural', '--preflight-next-owned'], true)) {
    echo json_encode(['status' => 'batch_preflight_only', 'lane' => $lane,
        'route_count' => count($routes), 'source_sha256' => $sourceSha,
        'plan_sha256' => $planSha,
        'owner_sha12' => substr(hash('sha256', (string)$owner['id']), 0, 12),
        'source_probes' => $preflight, 'http' => 0, 'business_dml' => 0]), PHP_EOL;
    exit(0);
}
$journalPath = (string)getenv('CDX_BATCH_JOURNAL');
if ($mode === '--token-zero' || $mode === '--token-zero-cross'
    || $mode === '--token-zero-info-value'
    || $mode === '--token-zero-user-info-fixed'
    || $mode === '--token-zero-dishes-fixed'
    || $mode === '--token-zero-next-natural'
    || $mode === '--token-zero-next-owned') {
    if (!is_file($journalPath) || is_link($journalPath)
        || (((int)fileperms($journalPath) & 0077) !== 0)) {
        throw new RuntimeException('Private token journal missing');
    }
    $journal = json_decode((string)file_get_contents($journalPath), true);
    if (!is_array($journal) || ($journal['lane'] ?? '') !== $lane
        || ($journal['state'] ?? '') !== 'deleted_needs_zero'
        || !preg_match('/^CDXBATCH[0-9A-F]{36}$/', (string)($journal['token'] ?? ''))) {
        throw new RuntimeException('Token journal state drift');
    }
    if (\think\facade\Cache::has($journal['token'])) {
        throw new RuntimeException('Batch File token not independently zero');
    }
    echo json_encode(['status' => 'batch_token_zero', 'lane' => $lane,
        'http' => 0, 'business_dml' => 0]), PHP_EOL;
    exit(0);
}
$stage = 'before_token';
$dir = sys_get_temp_dir() . '/cdx-ai-batch-'
    . strtolower(bin2hex(random_bytes(8)));
if (!mkdir($dir, 0700) || !chmod($dir, 0700)) {
    throw new RuntimeException('Private batch recovery directory unavailable');
}
$journalPath = $dir . '/journal.json';
$token = 'CDXBATCH' . strtoupper(bin2hex(random_bytes(18)));
$journal = ['lane' => $lane, 'state' => 'before_token', 'token' => $token,
    'owner_sha12' => substr(hash('sha256', (string)$owner['id']), 0, 12),
    'created_at' => time(), 'expires_at' => time() + 300,
    'source_sha256' => $sourceSha, 'plan_sha256' => $planSha,
    'routes' => []];
$save = static function () use (&$journal, $journalPath): void {
    $encoded = json_encode($journal, JSON_UNESCAPED_SLASHES);
    $temp = $journalPath . '.tmp';
    if (!is_string($encoded)
        || file_put_contents($temp, $encoded, LOCK_EX) !== strlen($encoded)
        || !chmod($temp, 0600) || !rename($temp, $journalPath)) {
        throw new RuntimeException('Private batch journal write failed');
    }
};
$save();
echo json_encode(['status' => 'private_recovery_journal_ready',
    'journal' => $journalPath, 'http' => 0]), PHP_EOL;
$model = \app\api\model\User::detail($owner['id']);
if (empty($model) || (int)$model['user_id'] !== $owner['id']) {
    throw new RuntimeException('Synthetic actor model unavailable');
}
$journal['state'] = 'token_issued';
$save();
if (!\think\facade\Cache::set($token, ['user' => $model, 'store_id' => 10001,
        'is_login' => true, 'password_version' => $owner['version'],
        'login_type' => 'sms'], 300)
    || !\think\facade\Cache::has($token)) {
    throw new RuntimeException('Private short token unavailable');
}
$contractFile = $lane === 'C'
    ? '/tmp/zhct-read6-safe2-20261004/read6_safe2_contract.php'
    : __DIR__ . '/../parallel-ai-app-read6/read6_safe2_contract.php';
if (!is_file($contractFile)
    || hash_file('sha256', $contractFile)
        !== 'f34a1b831a0dd9cdfd481563261198ed5ec9a5f3b7c5432dada580bcdcb6c562') {
    throw new RuntimeException('Sanitized JSON shape helper drift');
}
require_once $contractFile;
$out = [];
$startedBatch = microtime(true);
foreach ($routes as $route) {
    if (microtime(true) - $startedBatch >= 90) {
        throw new RuntimeException('Batch time cap; HTTP state must be audited');
    }
    $key = $route['method'] . ' ' . $route['path'];
    $stage = 'route_' . $key;
    $before = $probe($route);
    if ($before['state'] !== 'source_read' || $before['capped']) {
        $out[$key] = ['state' => 'source_blocked', 'source' => $before];
        continue;
    }
    $resolved = $resolveParams($route);
    if ($resolved === null) {
        $out[$key] = ['state' => 'natural_selector_absent', 'source' => $before];
        continue;
    }
    if ($actor()['sha256'] !== $owner['sha256']) {
        throw new RuntimeException('Synthetic actor changed before route');
    }
    $journal['state'] = 'request_started';
    $journal['current_route'] = $key;
    $save();
    $params = array_merge(['storeId' => 10001], $resolved);
    $query = $route['method'] === 'GET' ? '?' . http_build_query($params) : '';
    $body = $route['method'] === 'POST' ? http_build_query($params) : '';
    $headers = 'Access-Token: ' . $token . "\r\n"
        . "platform: MP-WEIXIN\r\nversion: 2.0.51\r\nenv-version: develop\r\n";
    if ($route['method'] === 'POST') {
        $headers .= "Content-Type: application/x-www-form-urlencoded\r\n";
    }
    $context = stream_context_create(['http' => [
        'method' => $route['method'], 'timeout' => 15,
        'ignore_errors' => true, 'follow_location' => 0,
        'header' => $headers, 'content' => $body,
    ]]);
    $t0 = microtime(true);
    $stream = @fopen('http://127.0.0.1:' . $port . $route['path'] . $query,
        'rb', false, $context);
    if ($stream === false) throw new RuntimeException('HTTP state unknown');
    try { $rawBody = stream_get_contents($stream, 262145); }
    finally { fclose($stream); }
    $elapsed = (int)round((microtime(true) - $t0) * 1000);
    $http = isset($http_response_header[0])
        && preg_match('/HTTP\/\S+\s+(\d+)/', $http_response_header[0], $m)
        ? (int)$m[1] : 0;
    if (!is_string($rawBody) || strlen($rawBody) > 262144 || $elapsed >= 15000
        || $http <= 0 || $http >= 300 && $http < 400) {
        throw new RuntimeException('HTTP size, timeout, or redirect unknown');
    }
    $decoded = json_decode($rawBody, true);
    if (!is_array($decoded) || !array_key_exists('status', $decoded)) {
        throw new RuntimeException('HTTP body contract unknown');
    }
    $after = $probe($route);
    if ($after !== $before || $actor()['sha256'] !== $owner['sha256']) {
        throw new RuntimeException('Owned source or actor changed after route');
    }
    if (!\think\facade\Cache::has($token)) {
        throw new RuntimeException('Short token disappeared during read batch');
    }
    $business = (int)$decoded['status'];
    $data = $decoded['data'] ?? null;
    $branch = batch_response_branch($data);
    $result = ['state' => $http === 200 && $business === 200
            ? 'business_shape_observed' : 'business_red',
        'http' => $http, 'business_status' => $business,
        'elapsed_ms' => $elapsed, 'source' => $before,
        'branch' => $branch, 'response_shape' => read6_safe2_shape($data),
        'data_sha256' => hash('sha256', json_encode($data,
            JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES))];
    if (($infoValue || $infoFixed) && $business === 200) {
        $stage = 'dm_user_info_decimal_readback';
        $userTable = $table('ai', 'user');
        $dbRows = $select($ai, 'SELECT ACTIVITY_PRINCIPAL_BALANCE,'
            . 'WITHDRAWABLE_BALANCE,BALANCE,EXPEND_MONEY,GIFT_BALANCE,'
            . 'SUBSIDY_BALANCE,PAY_MONEY FROM ' . $userTable
            . ' WHERE USER_ID=? AND STORE_ID=10001 AND IS_DELETE=0',
            [$owner['id']], 1);
        $info = is_array($data) ? ($data['userInfo'] ?? null) : null;
        if (count($dbRows) !== 1 || !is_array($info)
            || !function_exists('bccomp')) {
            throw new RuntimeException('DM user info value source unavailable');
        }
        $checks = [];
        foreach (['activity_principal_balance', 'withdrawable_balance',
            'balance', 'expend_money', 'gift_balance', 'subsidy_balance',
            'pay_money'] as $field) {
            $fromDb = $dbRows[0][$field] ?? null;
            $fromApi = $info[$field] ?? null;
            $checks[$field] = ['json_type' => gettype($fromApi),
                'matches_own_row' => is_numeric($fromDb) && is_numeric($fromApi)
                    && bccomp((string)$fromDb, (string)$fromApi, 2) === 0];
            if ($infoFixed) {
                $checks[$field]['json_string_fixed2'] = is_string($fromApi)
                    && preg_match('/^-?\d+\.\d{2}$/D', $fromApi) === 1;
            }
            if (in_array($field,
                ['activity_principal_balance', 'withdrawable_balance'], true)) {
                $checks[$field]['dm_row_is_zero_default'] = is_numeric($fromDb)
                    && bccomp((string)$fromDb, '0', 2) === 0;
            }
        }
        $result['dm_new_money_columns'] = $checks;
        if ($infoFixed) {
            foreach ($checks as $check) {
                if (!$check['matches_own_row'] || !$check['json_string_fixed2']) {
                    $result['state'] = 'decimal_contract_red';
                }
            }
        }
    }
    if ($dishesFixed && $business === 200) {
        $stage = 'dishes_decimal_source_readback';
        $items = is_array($data) && isset($data['list']['data'])
            && is_array($data['list']['data']) ? $data['list']['data'] : [];
        $ordinary = null;
        foreach ($items as $item) {
            if (is_array($item) && in_array((int)($item['source'] ?? 0), [1, 2], true)
                && (int)($item['id'] ?? 0) > 0) {
                $ordinary = $item;
                break;
            }
        }
        if ($ordinary === null) {
            $result['state'] = 'ordinary_dish_not_sampled';
        } else {
            $db = $select($ai, 'SELECT WEIGHT,ENERGY,CARBOHYDRATE,PROTEIN,'
                . 'FAT,SOURCE FROM ' . $table('ai', 'dishes') . ' WHERE ID=?',
                [(int)$ordinary['id']], 1);
            if (count($db) !== 1 || (int)$db[0]['source'] !== (int)$ordinary['source']
                || !function_exists('bccomp')) {
                throw new RuntimeException('Ordinary dish own source unavailable');
            }
            $checks = [];
            foreach (['weight', 'energy', 'carbohydrate', 'protein', 'fat',
                'unit_energy'] as $field) {
                $fromApi = $ordinary[$field] ?? null;
                $fromDb = $db[0][$field === 'unit_energy' ? 'energy' : $field] ?? null;
                $checks[$field] = ['json_type' => gettype($fromApi),
                    'json_string_fixed2' => is_string($fromApi)
                        && preg_match('/^-?\d+\.\d{2}$/D', $fromApi) === 1,
                    'matches_own_row' => is_numeric($fromApi) && is_numeric($fromDb)
                        && bccomp((string)$fromApi, (string)$fromDb, 2) === 0];
                if (!$checks[$field]['json_string_fixed2']
                    || !$checks[$field]['matches_own_row']) {
                    $result['state'] = 'decimal_contract_red';
                }
            }
            $result['ordinary_dish_six_fields'] = $checks;
        }
    }
    $out[$key] = $result;
    $journal['routes'][$key] = $result;
    $journal['state'] = 'route_audited';
    unset($journal['current_route']);
    $save();
}
$stage = 'token_delete';
if (!\think\facade\Cache::delete($token)) {
    throw new RuntimeException('Batch token deletion uncertain');
}
$journal['state'] = 'deleted_needs_zero';
$save();
$resultFile = $dir . '/results.json';
$encodedResults = json_encode(['lane' => $lane, 'results' => $out],
    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES);
if (!is_string($encodedResults)
    || file_put_contents($resultFile, $encodedResults, LOCK_EX) !== strlen($encodedResults)
    || !chmod($resultFile, 0600)) {
    throw new RuntimeException('Sanitized result file write failed');
}
echo json_encode(['status' => 'batch_pure_http_needs_independent_zero',
    'lane' => $lane, 'journal' => $journalPath,
    'sanitized_results' => $resultFile,
    'candidate_count' => count($routes),
    'business_http_count' => count(array_filter($out, static function (array $r): bool {
        return isset($r['http']);
    })), 'business_dml' => 0], JSON_UNESCAPED_SLASHES), PHP_EOL;
