<?php
declare(strict_types=1);

$lane = (string)($argv[1] ?? '');
if (!in_array($lane, ['B', 'C'], true)) {
    fwrite(STDERR, "DENIED business_http=0 business_dml=0\n");
    exit(78);
}
if ($lane === 'B') {
    $config = include '/tmp/zhct-http-store-mysql.dQTTBP/application/config/local/database.php';
    if (!is_array($config) || ($config['database'] ?? '') !== 'zhct'
        || hash('sha256', (string)($config['hostname'] ?? ''))
            !== '47e8be48b0cfd56dcdb9d9d426e6637b245f340b4efad4cd2f084ec5851ecc8e') {
        throw new RuntimeException('B code-local Store MySQL identity drift');
    }
    $db = new PDO('mysql:host=' . $config['hostname'] . ';port=3306;dbname='
        . $config['database'] . ';charset=utf8', $config['username'], $config['password'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER]);
    $port = 18081;
} else {
    if (getenv('POC_DIALECT') !== 'dm' || getenv('POC_HOST') !== '127.0.0.1'
        || getenv('POC_PORT') !== '5237' || getenv('POC_SCHEMA') !== 'ZHCT_APP'
        || getenv('POC_USER') !== 'ZHCT_APP') {
        throw new RuntimeException('C isolated Store DM identity drift');
    }
    $db = 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 ($db->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn() !== 'ZHCT_APP') {
        throw new RuntimeException('C schema drift');
    }
    $port = 18080;
}
$http = static function (string $path, array $form = [], string $method = 'POST') use ($port): array {
    if (!in_array($method, ['GET', 'POST'], true)) throw new RuntimeException('unsupported HTTP method');
    $context = stream_context_create(['http' => [
        'method' => $method, 'header' => "Content-Type: application/x-www-form-urlencoded\r\n",
        'content' => http_build_query($form, '', '&', PHP_QUERY_RFC3986),
        'ignore_errors' => true, 'timeout' => 20, 'follow_location' => 0,
    ]]);
    $handle = @fopen('http://127.0.0.1:' . $port . $path, 'rb', false, $context);
    if (!is_resource($handle)) throw new RuntimeException('isolated Store HTTP unavailable');
    $raw = stream_get_contents($handle, 4194305);
    fclose($handle);
    if (!is_string($raw) || strlen($raw) > 4194304) throw new RuntimeException('HTTP response cap');
    $body = json_decode($raw, true);
    if (!is_array($body) || (int)($body['code'] ?? -1) !== 0) {
        throw new RuntimeException('HTTP business failure at ' . $path);
    }
    return $body;
};
$source = static function (string $sql) use ($db): array {
    return array_map(static function (array $row): array {
        return array_change_key_case($row, CASE_LOWER);
    }, $db->query($sql)->fetchAll(PDO::FETCH_ASSOC));
};
$articleSource = $source('SELECT uuid,title,article_cate_uuid,status FROM ydy_article ORDER BY id');
$categorySource = $source('SELECT uuid,name,puuid,cate_path FROM ydy_article_cate ORDER BY id');
$departmentSource = $source('SELECT uuid,name,puuid FROM ydy_department'
    . ' WHERE status=1 AND is_show=1 ORDER BY id');
$restaurantSource = $source('SELECT * FROM ydy_restaurants WHERE id=1');
if (!$articleSource || !$categorySource || !$departmentSource || count($restaurantSource) !== 1) {
    throw new RuntimeException('source positive fixture absent');
}
$articleMap = [];
foreach ($articleSource as $row) $articleMap[(string)$row['uuid']] = $row;
$categoryMap = [];
foreach ($categorySource as $row) $categoryMap[(string)$row['uuid']] = $row;
$departmentMap = [];
foreach ($departmentSource as $row) $departmentMap[(string)$row['uuid']] = $row;
$candidate = null;
foreach ($articleSource as $row) {
    if ((int)$row['status'] === 1 && isset($categoryMap[(string)$row['article_cate_uuid']])
        && (string)$row['title'] !== '') {
        $candidate = $row;
        break;
    }
}
if ($candidate === null) throw new RuntimeException('no enabled article with existing category');
$articleList = $http('/p/api/articleLists')['data'] ?? null;
if (!is_array($articleList) || !$articleList) throw new RuntimeException('article tree empty');
$articleListGet = $http('/p/api/articleLists', [], 'GET')['data'] ?? null;
if ($articleListGet !== $articleList) throw new RuntimeException('article GET/POST data differ');
$articleNodes = 0;
$articleTreeWalk = static function (array $nodes) use (&$articleTreeWalk, $articleMap, $categoryMap, &$articleNodes): void {
    foreach ($nodes as $node) {
        if (!is_array($node)) throw new RuntimeException('article tree node invalid');
        $uuid = (string)($node['uuid'] ?? '');
        if (isset($articleMap[$uuid]) && isset($node['title'])) {
            if ((string)$node['title'] !== (string)$articleMap[$uuid]['title']
                || (int)$articleMap[$uuid]['status'] !== 1) {
                throw new RuntimeException('article tree row differs from original article');
            }
            $articleNodes++;
        } elseif (isset($categoryMap[$uuid])) {
            if ((string)($node['name'] ?? '') !== (string)$categoryMap[$uuid]['name']) {
                throw new RuntimeException('article tree category differs from original');
            }
        } else {
            throw new RuntimeException('article tree node lacks original row');
        }
        if (isset($node['children'])) $articleTreeWalk($node['children']);
    }
};
$articleTreeWalk($articleList);
if ($articleNodes <= 0) throw new RuntimeException('article tree has no positive article');
$article = $http('/p/api/articleInfo', ['uuid' => $candidate['uuid']])['data'] ?? null;
if (!is_array($article) || (string)($article['uuid'] ?? '') !== (string)$candidate['uuid']
    || (string)($article['title'] ?? '') !== (string)$candidate['title']) {
    throw new RuntimeException('article detail differs from original article');
}
$more = $http('/p/api/getMore', ['cate_uuid' => $candidate['article_cate_uuid'],
    'keyword' => $candidate['title']])['data'] ?? null;
if (!is_array($more)) throw new RuntimeException('article category more shape');
$moreOwn = array_values(array_filter($more, static function ($item) use ($candidate): bool {
    return is_array($item) && (string)($item['uuid'] ?? '') === (string)$candidate['uuid'];
}));
if (count($moreOwn) !== 1 || (string)$moreOwn[0]['title'] !== (string)$candidate['title']) {
    throw new RuntimeException('article category/keyword source row absent');
}
$restaurant = $http('/p/api/getRestaurantsInfo', ['id' => 1])['data'] ?? null;
$originalRestaurant = $restaurantSource[0];
if (!is_array($restaurant) || (int)($restaurant['id'] ?? 0) !== 1
    || (string)($restaurant['uuid'] ?? '') !== (string)$originalRestaurant['uuid']
    || (string)($restaurant['name'] ?? '') !== (string)$originalRestaurant['name']
    || count($restaurant['meal_times_map'] ?? []) !== 5) {
    throw new RuntimeException('restaurant info differs from original restaurant');
}
$restaurantAliases = [
    'zpterm' => 'zpTerm', 'zpchargeterm' => 'zpChargeTerm',
    'zpkey' => 'zpKey', 'zpchargekey' => 'zpChargeKey',
    'zpchargeapiurl' => 'zpChargeApiUrl', 'zpbzapiurl' => 'zpBzApiUrl',
    'zpapiurl' => 'zpApiUrl',
];
foreach ($restaurantAliases as $rawField => $apiField) {
    if (!array_key_exists($apiField, $restaurant)
        || $restaurant[$apiField] !== $originalRestaurant[$rawField]) {
        throw new RuntimeException('restaurant camelCase field differs from original row');
    }
}
foreach (['package_fee', 'locker_package_fee'] as $feeField) {
    $rawFee = $originalRestaurant[$feeField];
    $expectedFee = $rawFee === null ? null : bcadd((string)$rawFee, '0', 2);
    if (!array_key_exists($feeField, $restaurant)
        || $restaurant[$feeField] !== $expectedFee) {
        throw new RuntimeException('restaurant decimal text differs from original row');
    }
}
$departmentTree = $http('/p/api/getDepartment')['data'] ?? null;
if (!is_array($departmentTree) || !$departmentTree) {
    throw new RuntimeException('department tree empty');
}
$departmentNodes = 0;
$departmentWalk = static function (array $nodes) use (&$departmentWalk, $departmentMap, &$departmentNodes): void {
    foreach ($nodes as $node) {
        $uuid = (string)($node['uuid'] ?? '');
        if (!isset($departmentMap[$uuid])
            || (string)($node['name'] ?? '') !== (string)$departmentMap[$uuid]['name']) {
            throw new RuntimeException('department tree row differs from original');
        }
        $departmentNodes++;
        if (isset($node['children'])) $departmentWalk($node['children']);
    }
};
$departmentWalk($departmentTree);
$afterSources = [
    $source('SELECT uuid,title,article_cate_uuid,status FROM ydy_article ORDER BY id'),
    $source('SELECT uuid,name,puuid,cate_path FROM ydy_article_cate ORDER BY id'),
    $source('SELECT uuid,name,puuid FROM ydy_department WHERE status=1 AND is_show=1 ORDER BY id'),
    $source('SELECT * FROM ydy_restaurants WHERE id=1'),
];
if ($afterSources !== [$articleSource, $categorySource, $departmentSource, $restaurantSource]) {
    throw new RuntimeException('original table rows changed during read');
}
$topTypes = [];
foreach ($restaurant as $key => $value) $topTypes[$key] = gettype($value);
ksort($topTypes);
echo json_encode(['lane' => $lane, 'http_business_ok' => 6,
    'article_source_count' => count($articleSource), 'article_tree_nodes' => $articleNodes,
    'category_source_count' => count($categorySource), 'more_target_article' => 1,
    'restaurant_own_source_equal' => true,
    'restaurant_type_sha256' => hash('sha256', json_encode($topTypes)),
    'department_source_active' => count($departmentSource),
    'department_tree_nodes' => $departmentNodes,
    'business_dml' => 0], JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
