<?php
declare(strict_types=1);

// B/C isolated Store kiosk routes only. No SQL write, no external calls.
$lane = (string)($argv[1] ?? '');
if (!in_array($lane, ['B', 'C'], true)) {
    fwrite(STDERR, "DENIED: lane\n"); exit(78);
}
$isDm = $lane === 'C';
$pid = $isDm ? 112175 : 10001;
$root = $isDm ? '/tmp/zhct-dm-poc-20260928.fcpiiN/store'
    : '/tmp/zhct-http-store-mysql.dQTTBP';
$port = $isDm ? 18080 : 18081;
$cmdline = str_replace("\0", ' ', (string)@file_get_contents('/proc/' . $pid . '/cmdline'));
if (strpos($cmdline, '-S 127.0.0.1:' . $port) === false
    || strpos($cmdline, $root . '/public') === false
    || !is_file($root . '/application/m/controller/ControlScreen.php')) {
    throw new RuntimeException('DENIED: isolated worker/source drift');
}
$environment = (string)@file_get_contents('/proc/' . $pid . '/environ');
if ($environment === '' || ($isDm && (getenv('POC_DIALECT') !== 'dm'
    || getenv('POC_HOST') !== '127.0.0.1'
    || getenv('POC_PORT') !== '5237'
    || getenv('POC_SCHEMA') !== 'ZHCT_APP'))) {
    throw new RuntimeException('DENIED: worker/DM environment drift');
}
foreach (explode("\0", $environment) as $item) {
    if (strpos($item, 'JSR_REPORT_') === 0
        || strpos($item, 'JSR_READONLY_') === 0) {
        throw new RuntimeException('DENIED: external JSR integration present');
    }
}
foreach (['jsr-report.env', 'jsr-db.env'] as $name) {
    if (is_file($root . '/config/local/' . $name)) {
        throw new RuntimeException('DENIED: external JSR config present');
    }
}
$boundaryRaw = @file_get_contents('http://127.0.0.1:' . $port . '/__poc_boundary');
$boundary = json_decode((string)$boundaryRaw, true);
if (!is_array($boundary) || empty($boundary['external_stubs'])
    || empty($boundary['redis_isolated']) || !empty($boundary['real_queue'])) {
    throw new RuntimeException('DENIED: isolated HTTP boundary missing');
}
if ($isDm) {
    $config = require $root . '/application/database.php';
    foreach (['hostname' => 'POC_HOST', 'hostport' => 'POC_PORT',
        'database' => 'POC_SCHEMA', 'username' => 'POC_USER',
        'password' => 'POC_PASSWORD'] as $key => $envKey) {
        if ((string)($config[$key] ?? '') !== (string)getenv($envKey)) {
            throw new RuntimeException('DENIED: DM app config/worker mismatch');
        }
    }
    $pdo = new PDO('dm:host=127.0.0.1;port=5237;schema=ZHCT_APP',
        getenv('POC_USER'), getenv('POC_PASSWORD'),
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_CASE => PDO::CASE_LOWER]);
    if ($pdo->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")
        ->fetchColumn() !== 'ZHCT_APP') {
        throw new RuntimeException('DENIED: DM schema drift');
    }
} else {
    $config = require $root . '/application/config/local/database.php';
    $codeLocal = require '/workspace/zhct/zhctproject/store/application/config/local/database.php';
    foreach (['hostname', 'hostport', 'database', 'username', 'password'] as $key) {
        if (($config[$key] ?? null) !== ($codeLocal[$key] ?? null)) {
            throw new RuntimeException('DENIED: B app config/code local mismatch');
        }
    }
    if (($config['database'] ?? '') !== 'zhct') {
        throw new RuntimeException('DENIED: B database drift');
    }
    $pdo = new PDO('mysql:host=' . $config['hostname'] . ';port='
        . (int)($config['hostport'] ?? 3306) . ';dbname=' . $config['database']
        . ';charset=' . ($config['charset'] ?? 'utf8'),
        $config['username'], $config['password'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_CASE => PDO::CASE_LOWER]);
}
$prefix = $isDm ? 'YDY_' : 'ydy_';
$source = static function () use ($pdo, $prefix): array {
    $areaRows = $pdo->query('SELECT ID,NAME FROM ' . $prefix . 'restaurant_area'
        . " WHERE IS_DELETE=0 AND STATUS=1 AND (NAME LIKE '%南京%' OR NAME LIKE '%镇江%')"
        . ' ORDER BY ID')->fetchAll(PDO::FETCH_ASSOC);
    $articleCount = (int)$pdo->query('SELECT COUNT(*) FROM ' . $prefix
        . 'article WHERE STATUS=1')->fetchColumn();
    $ids = array_map(static function ($row): int {
        return (int)array_change_key_case($row, CASE_LOWER)['id'];
    }, $areaRows);
    $shopCount = 0;
    $stallCount = 0;
    foreach ($ids as $id) {
        $stmt = $pdo->prepare('SELECT COUNT(*) FROM ' . $prefix
            . 'restaurant_shop WHERE AREA_ID=? AND IS_DELETE=0 AND STATUS=1');
        $stmt->execute([$id]);
        $shopCount += (int)$stmt->fetchColumn();
        $stmt = $pdo->prepare('SELECT COUNT(*) FROM ' . $prefix
            . 'restaurants r INNER JOIN ' . $prefix
            . 'restaurant_shop s ON r.RESTAURANT_SHOP_ID=s.ID'
            . ' WHERE s.AREA_ID=? AND s.IS_DELETE=0 AND s.STATUS=1'
            . ' AND r.IS_DELETE=2 AND r.STATUS=1');
        $stmt->execute([$id]);
        $stallCount += (int)$stmt->fetchColumn();
    }
    return ['area_count' => count($ids), 'shop_count' => $shopCount,
        'stall_count' => $stallCount, 'published_article_count' => $articleCount];
};
$before = $source();
$shape = static function ($value) use (&$shape) {
    if (!is_array($value)) return gettype($value);
    if (array_values($value) === $value) {
        $variants = [];
        foreach ($value as $item) {
            $variants[] = json_encode($shape($item), JSON_UNESCAPED_UNICODE);
        }
        // Dataset sizes differ across the two independent test snapshots.
        return ['list' => true,
            'item_shape_variants' => array_values(array_unique($variants))];
    }
    $out = [];
    foreach ($value as $key => $item) $out[$key] = $shape($item);
    ksort($out);
    return $out;
};
$results = [];
foreach (['stallRevenueSnapshot', 'kioskSnapshot'] as $route) {
    foreach (['GET', 'POST'] as $method) {
        $base = 'http://127.0.0.1:' . $port . '/m/controlScreen/' . $route;
        $options = ['method' => $method, 'ignore_errors' => true,
            'timeout' => 45, 'follow_location' => 0];
        if ($method === 'POST') {
            $options['header'] = "Content-Type: application/x-www-form-urlencoded\r\n";
            $options['content'] = 'date=2026-09-28';
        } else {
            $base .= '?date=2026-09-28';
        }
        $context = stream_context_create(['http' => $options]);
        $opened = microtime(true);
        $handle = @fopen($base, 'rb', false, $context);
        if (!$handle) throw new RuntimeException('HTTP open failed: ' . $route);
        $body = stream_get_contents($handle, 8388609);
        fclose($handle);
        if (!is_string($body) || strlen($body) > 8388608
            || strpos((string)($http_response_header[0] ?? ''), ' 200 ') === false) {
            throw new RuntimeException('HTTP status/size failed: ' . $route);
        }
        $decoded = json_decode($body, true);
        if (!is_array($decoded) || ($decoded['code'] ?? null) !== 0
            || ($decoded['message'] ?? null) !== '获取成功'
            || !is_array($decoded['data'] ?? null)) {
            throw new RuntimeException('HTTP JSON envelope failed: ' . $route);
        }
        $data = $decoded['data'];
        if ($route === 'stallRevenueSnapshot') {
            if (($data['date'] ?? '') !== '2026-09-28'
                || ($data['source'] ?? '') !== 'local'
                || !is_array($data['areas'] ?? null)
                || count($data['areas']) !== $before['area_count']) {
                throw new RuntimeException('Stall revenue source/count failed');
            }
        } else {
            if (($data['menuDate'] ?? '') !== '2026-09-28'
                || ($data['source'] ?? '') !== 'realtime'
                || !is_array($data['regions'] ?? null)
                || !is_array($data['stalls'] ?? null)
                || count($data['regions']) !== $before['area_count']
                || count($data['stalls']) !== $before['stall_count']
                || (int)($data['articles']['total'] ?? -1)
                    !== $before['published_article_count']) {
                throw new RuntimeException('Kiosk source/count failed');
            }
        }
        $results[$route][$method] = [
            'http_status' => 200,
            'code' => $decoded['code'],
            'byte_count' => strlen($body),
            'elapsed_ms' => (int)round((microtime(true) - $opened) * 1000),
            'data_keys' => array_keys($data),
            'data_shape_sha256' => hash('sha256', json_encode($shape($data),
                JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)),
            'area_count' => count($data['areas'] ?? $data['regions'] ?? []),
            'stall_count' => count($data['stalls'] ?? []),
            'ranking_row_count' => count($data['revenueRanking']['rows'] ?? []),
            'article_count' => (int)($data['articles']['total'] ?? 0),
            'article_first_row_shape_sha256' => isset($data['articles']['rows'][0])
                ? hash('sha256', json_encode($shape($data['articles']['rows'][0]),
                    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES)) : null,
        ];
    }
    if ($results[$route]['GET']['data_shape_sha256']
        !== $results[$route]['POST']['data_shape_sha256']) {
        throw new RuntimeException('GET/POST JSON type/shape mismatch: ' . $route);
    }
}
$after = $source();
if ($after !== $before) throw new RuntimeException('Read-only source counts changed');
echo json_encode(['lane' => $lane, 'date' => '2026-09-28',
    'source_before' => $before, 'source_after' => $after,
    'results' => $results], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), "\n";
