<?php
declare(strict_types=1);

// Bounded Store personnel/department report probe. Preflight is SELECT-only.
// --execute creates one private type-0 operator, calls one read HTTP route,
// and deletes exactly that operator only after the HTTP outcome is known.
$lane = (string)getenv('CDX_DEPT_LANE');
$action = (string)($argv[1] ?? '');
$targets = [
    'A' => ['/tmp/zhct-http-store-a.l5omH8', 'http://127.0.0.1:18097', 21023,
        'mysql', '2026-09-18', '9b392fa3664b',
        '04e3f80cb0dddff6ec657f731185013ffffebcb4da48be8402b6d86547e14742'],
    'B' => ['/tmp/zhct-http-store-mysql.dQTTBP', 'http://127.0.0.1:18081', 10001,
        'mysql', '2026-09-18', '9b392fa3664b',
        '69ee1a79b8aa88bba2b22f15e3b6bc8c34eb508e6fba12950844e77eb438e499'],
    'C' => ['/tmp/zhct-dm-poc-20260928.fcpiiN/store', 'http://127.0.0.1:18080', 112175,
        'dm', '2025-02-17', 'b42d4036c9d8',
        '69ee1a79b8aa88bba2b22f15e3b6bc8c34eb508e6fba12950844e77eb438e499'],
];
if (!isset($targets[$lane]) || !in_array($action, ['--preflight', '--execute'], true)
    || ($action === '--execute' && ((string)getenv('CDX_DEPT_EXECUTE') !== 'ROOT_WINDOW_APPROVED'
        || (string)getenv('CDX_DEPT_SHA') !== hash_file('sha256', __FILE__)))) {
    fwrite(STDERR, "execution disabled\n");
    exit(78);
}
[$root, $base, $pid, $dialect, $day, $deptHash, $logicSha] = $targets[$lane];
if (is_link($root) || !is_dir($root)
    || hash_file('sha256', $root . '/application/p/controller/MealOrder.php')
        !== '0e818de300828a2c3546d6ccb83fe22ef0dd87bf5cc9c885ef646e6342f2ad5b'
    || hash_file('sha256', $root . '/application/p/logic/MealOrder.php') !== $logicSha) {
    throw new RuntimeException('Source root or SHA drift');
}
$ready = static function () use ($root, $base, $pid): bool {
    $cmd = (string)@file_get_contents('/proc/' . $pid . '/cmdline');
    $env = (string)@file_get_contents('/proc/' . $pid . '/environ');
    $children = (string)@file_get_contents('/proc/' . $pid . '/task/' . $pid . '/children');
    $boundary = json_decode((string)@file_get_contents($base . '/__poc_boundary'), true);
    $port = (int)parse_url($base, PHP_URL_PORT);
    return strpos($cmd, '127.0.0.1:' . $port) !== false
        && strpos($cmd, $root . '/public') !== false
        && $env !== '' && trim($children) === ''
        && !preg_match('/(?:^|\x00)PHP_CLI_SERVER_WORKERS=([2-9]|\d{2,})(?:\x00|$)/', $env)
        && is_array($boundary) && !empty($boundary['external_stubs'])
        && !empty($boundary['redis_isolated']) && empty($boundary['real_queue']);
};
if (!$ready()) throw new RuntimeException('Isolated one-worker boundary absent');
$config = require $root . '/application/database.php';
if (!is_array($config) || (string)($config['prefix'] ?? '') !== 'ydy_') {
    throw new RuntimeException('Runtime config shape mismatch');
}
if ($dialect === 'mysql') {
    $local = require '/workspace/zhct/zhctproject/store/application/config/local/database.php';
    if ((string)($config['type'] ?? '') !== 'mysql'
        || !is_array($local) || (string)($local['database'] ?? '') !== (string)$config['database']) {
        throw new RuntimeException('Code-local MySQL mismatch');
    }
    foreach (['hostname', 'database', 'username', 'password'] as $key) {
        if (($local[$key] ?? null) !== ($config[$key] ?? null)) {
            throw new RuntimeException('Code-local MySQL mismatch');
        }
    }
    $dsn = 'mysql:host=' . $config['hostname'] . ';port=' . (int)($config['hostport'] ?? 3306)
        . ';dbname=' . $config['database'] . ';charset=' . (string)($config['charset'] ?? 'utf8');
    $identityExpected = '003eb5aecf1cd4595b609c1f7f20b9ee325b578309fb9fe14fedaf77584fa401';
} else {
    if ((string)($config['type'] ?? '') !== 'app\\cores\\database\\Dm'
        || (string)($config['hostname'] ?? '') !== '127.0.0.1'
        || (int)($config['hostport'] ?? 0) !== 5237
        || strtoupper((string)($config['database'] ?? '')) !== 'ZHCT_APP'
        || strtoupper((string)($config['username'] ?? '')) !== 'ZHCT_APP') {
        throw new RuntimeException('Nonproduction DM target mismatch');
    }
    $dsn = 'dm:host=127.0.0.1;port=5237;schema=ZHCT_APP';
    $identityExpected = 'bc98485aed26beab348770a82ae38dbf922b04ab88a210cd7282c4caf0156194';
}
$connect = static function () use ($dsn, $config): PDO {
    return new PDO($dsn, (string)$config['username'], (string)$config['password'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION, PDO::ATTR_CASE => PDO::CASE_LOWER,
            PDO::ATTR_TIMEOUT => 5]);
};
try { $db = $connect(); }
catch (Throwable $e) { fwrite(STDERR, "DB connect failed: " . get_class($e) . "\n"); exit(75); }
$identity = $dialect === 'mysql'
    ? (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();
if (!hash_equals($identityExpected, hash('sha256', $identity))) {
    throw new RuntimeException('DB identity mismatch');
}
if ($dialect === 'dm') {
    $machine = trim((string)@file_get_contents('/etc/machine-id'));
    if ($machine === '' || hash('sha256', gethostname() . '|' . $machine)
        !== 'd56d81988bb0f106946cce91c608fdd0ef1116e5150b23376ea87e086eace4b9') {
        throw new RuntimeException('DM host fingerprint mismatch');
    }
}
$prefix = $dialect === 'dm' ? 'YDY_' : 'ydy_';
$window = [$day . ' 00:00:00', $day . ' 23:59:59'];
$sql = 'SELECT d.UUID AS DEPARTMENT_UUID,COUNT(DISTINCT m.ID) AS ORDERS'
    . ' FROM ' . $prefix . 'department d'
    . ' JOIN ' . $prefix . 'staff_department sd ON sd.DEPARTMENT_UUID=d.UUID'
    . ' JOIN ' . $prefix . 'user u ON u.STAFF_UUID=sd.STAFF_UUID'
    . ' JOIN ' . $prefix . 'meal_order m ON m.USER_ID=u.ID'
    . ' WHERE d.IS_SHOW=1 AND d.LEAF_FLAG=1 AND m.IS_DELETE=0 AND m.ORDER_STATUS=30'
    . ' AND m.BIND_TIME BETWEEN ? AND ? AND m.RESTAURANT_ID=1'
    . ' GROUP BY d.UUID';
$stmt = $db->prepare($sql);
$stmt->execute($window);
$candidates = $stmt->fetchAll(PDO::FETCH_ASSOC);
$matched = array_values(array_filter($candidates, static function (array $row) use ($deptHash): bool {
    return substr(hash('sha256', (string)$row['department_uuid']), 0, 12) === $deptHash
        && (int)$row['orders'] === 1;
}));
if (count($matched) !== 1 || count($candidates) > 3) {
    throw new RuntimeException('Audited leaf department changed');
}
$department = (string)$matched[0]['department_uuid'];
$sourceSql = 'SELECT m.ID,m.CASH,m.SUBSIDY,m.GIFT,m.REFUND_AMOUNT,m.PAY_PRICE'
    . ' FROM ' . $prefix . 'meal_order m'
    . ' JOIN ' . $prefix . 'user u ON u.ID=m.USER_ID'
    . ' JOIN ' . $prefix . 'staff_department sd ON sd.STAFF_UUID=u.STAFF_UUID'
    . ' WHERE sd.DEPARTMENT_UUID=? AND m.IS_DELETE=0 AND m.ORDER_STATUS=30'
    . ' AND m.BIND_TIME BETWEEN ? AND ? AND m.RESTAURANT_ID=1';
$readSource = static function (PDO $pdo) use ($sourceSql, $department, $window): array {
    $s = $pdo->prepare($sourceSql);
    $s->execute([$department, $window[0], $window[1]]);
    return $s->fetchAll(PDO::FETCH_ASSOC);
};
$source = $readSource($db);
if (count($source) !== 1) throw new RuntimeException('Source row bound changed');
$sourceHash = hash('sha256', serialize($source));
$refundStmt = $db->prepare('SELECT COUNT(*) FROM ' . $prefix
    . 'meal_order_refund WHERE MEAL_ORDER_ID=? AND IS_DELETE=0');
$refundStmt->execute([$source[0]['id']]);
$refundCount = (int)$refundStmt->fetchColumn();
$markerPrefix = 'CDXDEP4';
$markerCount = (int)$db->query('SELECT COUNT(*) FROM ' . $prefix . 'user'
    . " WHERE ACCOUNT LIKE 'CDXDEP4%'")->fetchColumn();
if ($action === '--preflight') {
    echo json_encode(['lane' => $lane, 'mode' => 'select-only',
        'target' => $dialect === 'dm' ? 'nonprod-dm-store' : 'code-local-dev-mysql',
        'department_sha_prefix' => $deptHash, 'source_orders' => count($source),
        'source_refunds' => $refundCount, 'prior_actor_count' => $markerCount]) . PHP_EOL;
    exit(0);
}
if ($markerCount !== 0) throw new RuntimeException('Prior actor requires review');
$marker = $markerPrefix . strtoupper(bin2hex(random_bytes(8)));
$dir = sys_get_temp_dir() . '/zhct-department-report-' . $marker;
if (!mkdir($dir, 0700)) throw new RuntimeException('Recovery journal unavailable');
$journalPath = $dir . '/recovery.json';
$operatorId = 0;
$httpSent = false;
$httpKnown = false;
$failure = '';
$result = [];
$cleanup = [];
$journal = ['lane' => $lane, 'marker' => $marker, 'operator_id' => 0,
    'database_fingerprint' => $identityExpected, 'phase' => 'before_insert'];
$save = static function (array $delta) use (&$journal, $journalPath): void {
    $journal = array_merge($journal, $delta);
    if (file_put_contents($journalPath, json_encode($journal)) === false) {
        throw new RuntimeException('Recovery journal write failed');
    }
    chmod($journalPath, 0600);
};
$save([]);
try {
    $db->query('SELECT ID,UUID,ACCOUNT,PWD,SALT,NAME,TYPE,CREATE_TIME,CREATE_BY,IS_DELETE'
        . ' FROM ' . $prefix . 'user WHERE 1=0');
    $stmt = $db->prepare('INSERT INTO ' . $prefix . 'user'
        . ' (UUID,ACCOUNT,PWD,SALT,NAME,TYPE,CREATE_TIME,CREATE_BY,IS_DELETE)'
        . ' VALUES (?,?,?,?,?,?,?,?,?)');
    $stmt->execute([$marker, $marker, 'DISABLED', strtoupper(bin2hex(random_bytes(8))),
        $marker, 0, date('Y-m-d H:i:s'), 'CODEX', 0]);
    $stmt = $db->prepare('SELECT ID FROM ' . $prefix . 'user'
        . ' WHERE UUID=? AND ACCOUNT=? AND TYPE=0 AND CREATE_BY=?');
    $stmt->execute([$marker, $marker, 'CODEX']);
    $operatorId = (int)$stmt->fetchColumn();
    if ($operatorId <= 0) throw new RuntimeException('Inserted actor not readable');
    $save(['operator_id' => $operatorId, 'phase' => 'actor_inserted']);
    require_once $root . '/application/base/Security.php';
    $token = \app\base\Security::encrypt(implode("\t",
        [(string)$operatorId, $marker, $marker, '0', '', '', $marker]));
    if (!is_string($token) || $token === '') throw new RuntimeException('Token unavailable');
    if (!$ready()) throw new RuntimeException('Worker drift before HTTP');
    $form = ['start_date' => $day, 'end_date' => $day,
        'department_uuids' => $department, 'restaurant_shop_id' => '1'];
    $ctx = stream_context_create(['http' => ['method' => 'POST',
        'header' => "Content-Type: application/x-www-form-urlencoded\r\n"
            . 'token: ' . $token . "\r\n",
        'content' => http_build_query($form, '', '&', PHP_QUERY_RFC3986),
        'timeout' => 25, 'ignore_errors' => true]]);
    $httpSent = true;
    $stream = @fopen($base . '/p/mealOrder/departmentList', 'rb', false, $ctx);
    $body = is_resource($stream) ? stream_get_contents($stream, 256 * 1024 + 1) : false;
    $meta = is_resource($stream) ? stream_get_meta_data($stream) : [];
    if (is_resource($stream)) fclose($stream);
    if (!is_string($body) || !empty($meta['timed_out']) || strlen($body) > 256 * 1024) {
        throw new RuntimeException('HTTP outcome incomplete');
    }
    $httpKnown = true;
    $headers = $http_response_header ?? [];
    preg_match('/HTTP\/\S+\s+(\d+)/', (string)($headers[0] ?? ''), $match);
    $response = json_decode($body, true);
    $payload = is_array($response) ? ($response['data'] ?? null) : null;
    $rows = is_array($payload) ? ($payload['data'] ?? null) : null;
    $row = is_array($rows) && count($rows) === 1 ? $rows[0] : null;
    $summary = is_array($payload) ? ($payload['summary'] ?? null) : null;
    $expected = ['consume_count' => 1, 'refund_count' => $refundCount];
    foreach (['cash' => 'cash_amount', 'subsidy' => 'subsidy_amount',
        'gift' => 'gift_amount', 'refund_amount' => 'refund_amount'] as $raw => $field) {
        $expected[$field] = number_format((float)($source[0][$raw] ?? 0), 2, '.', '');
    }
    $expected['pay_amount'] = number_format((float)$source[0]['pay_price']
        - (float)$source[0]['refund_amount'], 2, '.', '');
    $sourceMatch = is_array($row) && is_array($summary)
        && (string)($row['uuid'] ?? '') === $department;
    foreach ($expected as $field => $value) {
        foreach ([$row, $summary] as $check) {
            $actual = is_array($check) ? ($check[$field] ?? null) : null;
            $equal = in_array($field, ['consume_count', 'refund_count'], true)
                ? (is_numeric($actual) && (int)$actual === $value)
                : (is_numeric($actual) && number_format((float)$actual, 2, '.', '') === $value);
            $sourceMatch = $sourceMatch && $equal;
        }
    }
    $types = static function ($value): array {
        if (!is_array($value)) return [];
        $out = array_map('gettype', $value);
        ksort($out);
        return $out;
    };
    $result = ['http' => (int)($match[1] ?? 0),
        'code' => is_array($response) ? ($response['code'] ?? null) : null,
        'business_success' => is_array($response) && ($response['code'] ?? null) === 0,
        'rows' => is_array($rows) ? count($rows) : null,
        'pagination_total' => is_array($payload) ? ($payload['total'] ?? null) : null,
        'own_source_match' => $sourceMatch,
        'row_types' => $types($row), 'summary_types' => $types($summary),
        'data_types' => $types($payload), 'body_bytes' => strlen($body)];
    if ($result['http'] !== 200 || !$result['business_success']
        || !$sourceMatch || (int)$result['pagination_total'] !== 1) {
        throw new RuntimeException('Positive department report mismatch');
    }
    $again = $readSource($connect());
    if (hash('sha256', serialize($again)) !== $sourceHash) {
        throw new RuntimeException('Source changed during read HTTP');
    }
    $result['source_unchanged'] = true;
} catch (Throwable $e) {
    $failure = get_class($e) . ':' . hash('sha256', $e->getMessage());
} finally {
    $cleanup['barrier_safe'] = (!$httpSent || $httpKnown) && $ready();
    if ($cleanup['barrier_safe']) {
        try {
            $verify = $connect();
            $stmt = $verify->prepare('SELECT ID FROM ' . $prefix . 'user'
                . ' WHERE UUID=? AND ACCOUNT=? AND TYPE=0 AND CREATE_BY=?');
            $stmt->execute([$marker, $marker, 'CODEX']);
            $owned = $stmt->fetchAll(PDO::FETCH_COLUMN);
            if (count($owned) > 1 || ($owned && (int)$owned[0] !== $operatorId)) {
                throw new RuntimeException('Actor identity drift');
            }
            if ($owned) {
                $delete = $verify->prepare('DELETE FROM ' . $prefix . 'user'
                    . ' WHERE ID=? AND UUID=? AND ACCOUNT=? AND TYPE=0 AND CREATE_BY=?');
                $delete->execute([$operatorId, $marker, $marker, 'CODEX']);
                if ($delete->rowCount() !== 1) throw new RuntimeException('Exact cleanup changed');
            }
            $stmt = $verify->prepare('SELECT COUNT(*) FROM ' . $prefix . 'user'
                . ' WHERE UUID=? OR ACCOUNT=?');
            $stmt->execute([$marker, $marker]);
            $cleanup['actor_zero'] = (int)$stmt->fetchColumn() === 0;
        } catch (Throwable $e) {
            $cleanup['error_class'] = get_class($e);
        }
    }
    $save(['phase' => !empty($cleanup['actor_zero']) ? 'cleaned' : 'needs_recovery',
        'cleanup' => $cleanup, 'failure' => $failure]);
}
$clean = !empty($cleanup['barrier_safe']) && !empty($cleanup['actor_zero'])
    && empty($cleanup['error_class']);
if ($clean && $failure === '') {
    unlink($journalPath);
    rmdir($dir);
}
echo json_encode(['lane' => $lane, 'route' => 'POST /p/mealOrder/departmentList',
    'department_sha_prefix' => $deptHash, 'source_orders' => 1,
    'result' => $result, 'failure' => $failure, 'cleanup' => $cleanup],
    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
if (!$clean || $failure !== '') exit(1);
