<?php
declare(strict_types=1);

// Executes only the actual mobile-page getRecipes input against an isolated HTTP worker.
$lane = (string)getenv('CDX_DMR9_LANE');
$mode = (string)($argv[1] ?? '');
if (!in_array($lane, ['B', 'C'], true)
    || !in_array($mode, ['--preflight', '--run', '--audit'], true)
    || ($mode === '--run' && (string)getenv('CDX_DMR9_RUN') !== 'ONE_PAGE_READ_V1')) {
    echo json_encode(['status' => 'default_deny', 'http_sent' => false]), PHP_EOL;
    exit(78);
}
$root = $lane === 'B' ? '/tmp/zhct-http-store-mysql.dQTTBP' : '/tmp/zhct-dm-poc-20260928.fcpiiN/store';
$pid = $lane === 'B' ? 10001 : 112175;
$port = $lane === 'B' ? 18081 : 18080;
$stage = 'boundary';
set_exception_handler(static function (Throwable $e) use (&$stage, $lane): void {
    echo json_encode(['status' => 'stop', 'lane' => $lane, 'stage' => $stage,
        'class' => get_class($e), 'message_sha' => hash('sha256', $e->getMessage())]), PHP_EOL;
    exit(2);
});
if (PHP_VERSION_ID < 70300 || PHP_VERSION_ID >= 70400 || is_link($root)) {
    throw new RuntimeException('Wrong runtime/root');
}
$hashes = [
    '/application/p/controller/DateMenu.php' => 'e8c33254370994003a183c911dc343b319e8dc8624ca98a8acd757c04deb9710',
    '/application/p/logic/DateMenu.php' => '1ef62d1280f54fe556821e71983d58b5758e0d84cd0be44d3ee67fa15ee1dd0a',
    '/application/model/Recipes.php' => 'cd0bf238f33277efbbf0bed8fde4c7c6b3774018ea7fe2dea89aa0e8017ddc7f',
];
foreach ($hashes as $path => $sha) {
    if (!hash_equals($sha, (string)@hash_file('sha256', $root . $path))) {
        throw new RuntimeException('Worker source drift');
    }
}
$base = 'http://127.0.0.1:' . $port;
$cmdline = (string)@file_get_contents('/proc/' . $pid . '/cmdline');
$boundary = json_decode((string)@file_get_contents($base . '/__poc_boundary'), true);
if (strpos($cmdline, $root . '/public') === false || strpos($cmdline, '127.0.0.1:' . $port) === false
    || !is_array($boundary) || ($boundary['external_stubs'] ?? null) !== true
    || ($boundary['redis_isolated'] ?? null) !== true || ($boundary['real_queue'] ?? true) !== false) {
    throw new RuntimeException('Worker isolation drift');
}
$cfg = require $root . '/application/database.php';
if ($lane === 'B') {
    $local = require '/workspace/zhct/zhctproject/store/application/config/local/database.php';
    foreach (['hostname', 'database', 'username', 'password'] as $key) {
        if (($cfg[$key] ?? null) !== ($local[$key] ?? null)) {
            throw new RuntimeException('Not code-local MySQL');
        }
    }
    if ((int)($cfg['hostport'] ?? 3306) !== (int)($local['hostport'] ?? 3306)) {
        throw new RuntimeException('MySQL port drift');
    }
    $dsn = 'mysql:host=' . $cfg['hostname'] . ';port=' . (int)($cfg['hostport'] ?? 3306)
        . ';dbname=' . $cfg['database'] . ';charset=' . ($cfg['charset'] ?? 'utf8');
    $prefix = 'ydy_';
} else {
    foreach (['hostname' => 'POC_HOST', 'hostport' => 'POC_PORT', 'database' => 'POC_SCHEMA',
        'username' => 'POC_USER', 'password' => 'POC_PASSWORD'] as $key => $env) {
        if ((string)($cfg[$key] ?? '') !== (string)getenv($env)) {
            throw new RuntimeException('DM config drift');
        }
    }
    if ((string)getenv('POC_HOST') !== '127.0.0.1' || (string)getenv('POC_PORT') !== '5237'
        || strtoupper((string)getenv('POC_SCHEMA')) !== 'ZHCT_APP') {
        throw new RuntimeException('Not approved DM');
    }
    $dsn = 'dm:host=127.0.0.1;port=5237;schema=ZHCT_APP';
    $prefix = 'YDY_';
}
$connect = static function () use ($dsn, $cfg): PDO {
    return new PDO($dsn, (string)$cfg['username'], (string)$cfg['password'], [
        PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_CASE => PDO::CASE_LOWER,
        PDO::ATTR_TIMEOUT => 5,
    ]);
};
$db = $connect();
$identity = $lane === 'B'
    ? (string)$db->query("SELECT CONCAT(@@server_uuid,'|',@@hostname,'|',@@port,'|',DATABASE())")->fetchColumn()
    : 'ZHCT_APP|5237|' . (string)$db->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn();
$expected = $lane === 'B' ? '003eb5aecf1cd4595b609c1f7f20b9ee325b578309fb9fe14fedaf77584fa401'
    : 'bc98485aed26beab348770a82ae38dbf922b04ab88a210cd7282c4caf0156194';
if (!hash_equals($expected, hash('sha256', $identity))) {
    throw new RuntimeException('DB identity drift');
}
$table = static function (string $name) use ($prefix): string {
    if (!in_array($name, ['recipes', 'dishes', 'staff', 'user'], true)) {
        throw new RuntimeException('Unapproved table');
    }
    return $prefix . $name;
};
$query = static function (PDO $pdo, string $sql, array $args = []): array {
    $stmt = $pdo->prepare($sql);
    $stmt->execute($args);
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
};
$stage = 'natural_source';
$groupSql = 'SELECT R.MENU_DATE,R.MEAL_TIMES,R.RESTAURANT_ID,COUNT(*) N FROM ' . $table('recipes')
    . ' R JOIN ' . $table('dishes') . ' D ON D.UUID=R.DISHES_UUID'
    . ' WHERE D.CATEGORY=1 AND D.STATUS=1 AND D.IS_DEL=1'
    . ' GROUP BY R.MENU_DATE,R.MEAL_TIMES,R.RESTAURANT_ID ORDER BY N DESC';
$groups = $query($db, $lane === 'B' ? $groupSql . ' LIMIT 16' : 'SELECT * FROM (' . $groupSql . ') WHERE ROWNUM<=16');
$chosen = null;
foreach ($groups as $group) {
    if ((int)$group['n'] < 1 || (int)$group['n'] > 100 || (int)$group['restaurant_id'] <= 0) {
        continue;
    }
    $chosen = ['date' => substr((string)$group['menu_date'], 0, 10),
        'meal' => (int)$group['meal_times'], 'restaurant' => (int)$group['restaurant_id'],
        'joined_count' => (int)$group['n']];
    break;
}
if ($chosen === null || $chosen['restaurant'] !== 1) {
    throw new RuntimeException('No bounded restaurant-1 recipe source');
}
$snapshot = static function (PDO $pdo) use ($chosen, $query, $table): array {
    $rows = $query($pdo, 'SELECT R.*,D.* FROM ' . $table('recipes') . ' R JOIN ' . $table('dishes')
        . ' D ON D.UUID=R.DISHES_UUID WHERE R.MENU_DATE=? AND R.MEAL_TIMES=?'
        . ' AND R.RESTAURANT_ID=? AND D.CATEGORY=1 AND D.STATUS=1 AND D.IS_DEL=1 ORDER BY R.ID',
        [$chosen['date'], $chosen['meal'], $chosen['restaurant']]);
    $staffOne = $query($pdo, 'SELECT * FROM ' . $table('staff') . ' WHERE UUID=?', ['1']);
    return ['recipes_and_dishes' => $rows, 'staff_uuid_1' => $staffOne];
};
$sourceSha = hash('sha256', serialize($snapshot($db)));
$defaultStaffCount = count($query($db, 'SELECT UUID FROM ' . $table('staff') . ' WHERE UUID=?', ['1']));
if ($defaultStaffCount !== 0) {
    throw new RuntimeException('Default UUID=1 now exists; prior gate no longer applies');
}
if ($mode === '--preflight') {
    echo json_encode(['status' => 'source_ready', 'lane' => $lane, 'joined_count' => $chosen['joined_count'],
        'default_staff_uuid_1_count' => $defaultStaffCount, 'source_sha' => $sourceSha,
        'http_sent' => false, 'dml' => false]), PHP_EOL;
    exit(0);
}
if ($mode === '--audit') {
    $marker = (string)getenv('CDX_DMR9_MARKER');
    $oldSha = (string)getenv('CDX_DMR9_SOURCE_SHA');
    if (!preg_match('/^CDXDMR9[A-F0-9]{12}$/D', $marker) || !preg_match('/^[a-f0-9]{64}$/D', $oldSha)) {
        throw new RuntimeException('Bad audit identifiers');
    }
    $count = count($query($db, 'SELECT ID FROM ' . $table('user') . ' WHERE ACCOUNT=? OR UUID=?',
        [$marker, $marker . 'U']));
    echo json_encode(['status' => $count === 0 && $oldSha === $sourceSha ? 'independent_zero_pass' : 'audit_fail',
        'lane' => $lane, 'actor_rows' => $count, 'source_unchanged' => $oldSha === $sourceSha]), PHP_EOL;
    exit($count === 0 && $oldSha === $sourceSha ? 0 : 2);
}
$base = $lane === 'B' ? 'http://127.0.0.1:18082' : 'http://127.0.0.1:18083';
$marker = 'CDXDMR9' . strtoupper(bin2hex(random_bytes(6)));
$actorUuid = $marker . 'U';
$actorId = 0;
$result = ['status' => 'run_failed', 'lane' => $lane, 'marker' => $marker,
    'joined_count' => $chosen['joined_count'], 'default_staff_uuid_1_count' => $defaultStaffCount,
    'source_sha' => $sourceSha, 'http_sent' => false];
try {
    $stage = 'own_actor';
    $stmt = $db->prepare('INSERT INTO ' . $table('user')
        . ' (UUID,ACCOUNT,PWD,SALT,NAME,TYPE,STATUS,CREATE_TIME,CREATE_BY,IS_DELETE)'
        . ' VALUES (?,?,?,?,?,0,0,?,?,0)');
    $stmt->execute([$actorUuid, $marker, 'DISABLED', strtoupper(bin2hex(random_bytes(8))),
        $marker, date('Y-m-d H:i:s'), $marker]);
    $actorRows = $query($db, 'SELECT ID FROM ' . $table('user') . ' WHERE UUID=? AND ACCOUNT=? AND CREATE_BY=?',
        [$actorUuid, $marker, $marker]);
    if (count($actorRows) !== 1 || (int)$actorRows[0]['id'] <= 0) {
        throw new RuntimeException('Actor ownership ambiguous');
    }
    $actorId = (int)$actorRows[0]['id'];
    require_once $root . '/application/base/Security.php';
    $token = \app\base\Security::encrypt(implode("\t", [(string)$actorId, $actorUuid, $marker,
        '0', '', '', $marker]));
    $form = ['recipe_uuids' => '', 'date' => $chosen['date'], 'type' => 'staple_food',
        'meal_times' => $chosen['meal']];
    $headers = "Content-Type: application/x-www-form-urlencoded;charset=UTF-8\r\ntoken: " . $token . "\r\n";
    $options = ['http' => ['method' => 'POST', 'timeout' => 15, 'ignore_errors' => true,
        'follow_location' => 0, 'header' => $headers,
        'content' => http_build_query($form, '', '&', PHP_QUERY_RFC3986)]];
    $stage = 'http';
    $stream = @fopen($base . '/p/dateMenu/getRecipes', 'rb', false, stream_context_create($options));
    $result['http_sent'] = true;
    $raw = $stream === false ? false : stream_get_contents($stream, 2097153);
    $headersOut = $http_response_header ?? [];
    if (is_resource($stream)) {
        fclose($stream);
    }
    preg_match('/HTTP\/\S+\s+(\d+)/', (string)($headersOut[0] ?? ''), $matches);
    $body = is_string($raw) ? json_decode($raw, true) : null;
    $result['http_status'] = (int)($matches[1] ?? 0);
    $result['business_code'] = is_array($body) ? ($body['code'] ?? null) : null;
    $result['data_kind'] = is_array($body) ? gettype($body['data'] ?? null) : null;
    $result['data_count'] = is_array($body['data'] ?? null) ? count($body['data']) : null;
    $result['response_top_keys'] = is_array($body) ? array_keys($body) : [];
    $shape = static function ($node) use (&$shape) {
        if (!is_array($node)) return gettype($node);
        if ($node === []) return [];
        if (array_keys($node) === range(0, count($node) - 1)) return ['[]' => $shape($node[0])];
        $out = [];
        foreach ($node as $key => $value) $out[(string)$key] = $shape($value);
        ksort($out);
        return $out;
    };
    $result['data_shape_sha'] = is_array($body) ? hash('sha256', serialize($shape($body['data'] ?? null))) : null;
    $result['first_row_key_sha'] = is_array($body['data'] ?? null) && isset($body['data'][0])
        ? hash('sha256', serialize(array_keys($body['data'][0]))) : null;
    $result['first_row_shape'] = is_array($body['data'] ?? null) && isset($body['data'][0])
        ? $shape($body['data'][0]) : null;
    $sourceDishUuids = [];
    foreach ($snapshot($db)['recipes_and_dishes'] as $row) $sourceDishUuids[] = trim((string)$row['dishes_uuid']);
    $returnedDishUuids = [];
    foreach (($body['data'] ?? []) as $row) {
        if (is_array($row)) $returnedDishUuids[] = trim((string)($row['dishes_uuid'] ?? ''));
    }
    sort($sourceDishUuids);
    sort($returnedDishUuids);
    $result['all_returned_dishes_match_source'] = $sourceDishUuids === $returnedDishUuids;
    $result['response_type_sha'] = hash('sha256', serialize([
        'http' => $result['http_status'], 'code' => gettype($result['business_code']),
        'message' => is_array($body) ? gettype($body['message'] ?? null) : null,
        'data' => $result['data_kind'], 'count' => $result['data_count'],
    ]));
    $result['status'] = $result['http_status'] === 200 && $result['business_code'] === 0
        && $result['data_count'] === $chosen['joined_count']
        && $result['all_returned_dishes_match_source'] ? 'positive_pass' : 'positive_mismatch';
} catch (Throwable $e) {
    $result['failure_stage'] = $stage;
    $result['failure_class'] = get_class($e);
    $result['failure_message_sha'] = hash('sha256', $e->getMessage());
} finally {
    $stage = 'own_actor_cleanup';
    if ($actorId > 0) {
        $delete = $db->prepare('DELETE FROM ' . $table('user') . ' WHERE ID=? AND UUID=? AND ACCOUNT=? AND CREATE_BY=?');
        $delete->execute([$actorId, $actorUuid, $marker, $marker]);
        $result['actor_deleted'] = $delete->rowCount();
    }
    $fresh = $connect();
    $result['actor_rows_after'] = count($query($fresh, 'SELECT ID FROM ' . $table('user')
        . ' WHERE ACCOUNT=? OR UUID=?', [$marker, $actorUuid]));
    $result['source_unchanged'] = hash('sha256', serialize($snapshot($fresh))) === $sourceSha;
}
echo json_encode($result, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), PHP_EOL;
exit($result['status'] === 'positive_pass' && $result['actor_rows_after'] === 0
    && $result['source_unchanged'] ? 0 : 2);
