<?php

declare(strict_types=1);

// SELECT-only A/B/C safety gate for Store check_equipment_heart.
$lane = (string)getenv('CDX_HEART_LANE');
if (!in_array($lane, ['A', 'B', 'C'], true)
    || count($argv) !== 2 || ($argv[1] ?? '') !== '--preflight-only'
    || (string)getenv('CDX_HEART_PREFLIGHT') !== 'ROOT_APPROVED_READ_ONLY_V1'
    || (string)getenv('CDX_HEART_SHA') !== hash_file('sha256', __FILE__)) {
    echo json_encode(['status' => 'default-deny', 'db_connected' => false,
        'command_started' => false, 'device_called' => false,
        'dml_sent' => false]), PHP_EOL;
    exit(78);
}
$targets = [
    'A' => ['/tmp/zhct-http-store-a.l5omH8', 18097, 21023, 'mysql'],
    'B' => ['/tmp/zhct-http-store-mysql.dQTTBP', 18081, 10001, 'mysql'],
    'C' => ['/tmp/zhct-dm-poc-20260928.fcpiiN/store', 18080, 112175, 'dm'],
];
[$root, $port, $pid, $dialect] = $targets[$lane];
$base = 'http://127.0.0.1:' . $port;
if ((string)getenv('POC_APP_ROOT') !== $root
    || (string)getenv('POC_BASE_URL') !== $base
    || (string)getenv('POC_DIALECT') !== $dialect
    || is_link($root)
    || hash_file('sha256', $root
        . '/application/cron/command/CheckEquipmentHeart.php')
        !== '30e881eddee2461cdeca4d364a35da19b9c297dd04d5bdcf0ee5a509b76a29fb') {
    throw new RuntimeException('Approved Store heartbeat source/runtime mismatch');
}
$registry = (string)file_get_contents($root . '/application/command.php');
if (strpos($registry, "'app\\cron\\command\\CheckEquipmentHeart'") === false) {
    throw new RuntimeException('check_equipment_heart not actively registered');
}
$cmdline = (string)@file_get_contents('/proc/' . $pid . '/cmdline');
$environment = (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',
    false, stream_context_create(['http' => ['timeout' => 8,
        'follow_location' => 0, 'ignore_errors' => true]])), true);
$hex = strtoupper(str_pad(dechex($port), 4, '0', STR_PAD_LEFT));
$inode = '';
foreach (@file('/proc/net/tcp', FILE_IGNORE_NEW_LINES) ?: [] as $line) {
    $parts = preg_split('/\s+/', trim($line));
    if (($parts[1] ?? '') === '0100007F:' . $hex
        && ($parts[3] ?? '') === '0A') {
        $inode = (string)($parts[9] ?? '');
        break;
    }
}
$ownsPort = false;
if ($inode !== '') foreach (@scandir('/proc/' . $pid . '/fd') ?: [] as $fd) {
    if ($fd === '.' || $fd === '..') continue;
    if (@readlink('/proc/' . $pid . '/fd/' . $fd)
        === 'socket:[' . $inode . ']') {
        $ownsPort = true;
        break;
    }
}
if (strpos($cmdline, '127.0.0.1:' . $port) === false
    || strpos($cmdline, $root . '/public') === false
    || $environment === '' || trim($children) !== '' || !$ownsPort
    || strpos($environment, 'PHP_CLI_SERVER_WORKERS=') !== false
    || !is_array($boundary) || empty($boundary['external_stubs'])
    || empty($boundary['redis_isolated']) || !empty($boundary['real_queue'])) {
    throw new RuntimeException('Isolated Store heartbeat listener boundary absent');
}
$cfg = require $root . '/application/database.php';
if (!is_array($cfg) || (string)($cfg['prefix'] ?? '') !== 'ydy_') {
    throw new RuntimeException('Store runtime DB config invalid');
}
if ($dialect === 'mysql') {
    $local = require '/workspace/zhct/zhctproject/store/application/config/local/database.php';
    if (!is_array($local) || (string)($cfg['type'] ?? '') !== 'mysql') {
        throw new RuntimeException('Code-local Store MySQL config absent');
    }
    foreach (['hostname', 'database', 'username', 'password'] as $field) {
        if (($cfg[$field] ?? null) !== ($local[$field] ?? null)) {
            throw new RuntimeException('Store runtime differs from code local');
        }
    }
    if ((int)($cfg['hostport'] ?? 3306) !== (int)($local['hostport'] ?? 3306)) {
        throw new RuntimeException('Store runtime port differs from code local');
    }
    $dsn = 'mysql:host=' . $cfg['hostname']
        . ';port=' . (int)($cfg['hostport'] ?? 3306)
        . ';dbname=' . $cfg['database']
        . ';charset=' . ($cfg['charset'] ?? 'utf8');
} else {
    if ((string)($cfg['type'] ?? '') !== 'app\\cores\\database\\Dm'
        || (string)getenv('POC_HOST') !== '127.0.0.1'
        || (string)getenv('POC_PORT') !== '5237'
        || strtoupper((string)getenv('POC_SCHEMA')) !== 'ZHCT_APP'
        || strtoupper((string)getenv('POC_USER')) !== 'ZHCT_APP') {
        throw new RuntimeException('Nonproduction Store DM target mismatch');
    }
    foreach (['hostname' => 'POC_HOST', 'hostport' => 'POC_PORT',
        'database' => 'POC_SCHEMA', 'username' => 'POC_USER',
        'password' => 'POC_PASSWORD'] as $field => $key) {
        if ((string)($cfg[$field] ?? '') !== (string)getenv($key)) {
            throw new RuntimeException('DM worker DB context mismatch');
        }
    }
    $machineId = trim((string)@file_get_contents('/etc/machine-id'));
    if ($machineId === '' || gethostname() === false
        || hash('sha256', gethostname() . '|' . $machineId)
            !== 'd56d81988bb0f106946cce91c608fdd0ef1116e5150b23376ea87e086eace4b9') {
        throw new RuntimeException('DM host identity mismatch');
    }
    $dsn = 'dm:host=127.0.0.1;port=5237;schema=ZHCT_APP';
}
$db = 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,
]);
$identity = $dialect === 'dm'
    ? 'ZHCT_APP|5237|' . (string)$db->query(
        "SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn()
    : (string)$db->query(
        "SELECT CONCAT(@@server_uuid,'|',@@hostname,'|',@@port,'|',DATABASE())")
        ->fetchColumn();
$approvedDbSha = $dialect === 'dm'
    ? 'bc98485aed26beab348770a82ae38dbf922b04ab88a210cd7282c4caf0156194'
    : '003eb5aecf1cd4595b609c1f7f20b9ee325b578309fb9fe14fedaf77584fa401';
if (!hash_equals($approvedDbSha, hash('sha256', $identity))) {
    throw new RuntimeException('Store heartbeat database identity differs');
}
$table = $dialect === 'dm' ? 'YDY_EQUIPMENT' : 'ydy_equipment';
$db->query('SELECT ID,CODE,TYPE,RESTAURANT_ID,STATUS,MATE_FLAG,'
    . ' HEARTTIME,DEL_FLAG,CREATE_BY FROM ' . $table . ' WHERE 1=0');
$all = $db->query('SELECT * FROM ' . $table . ' ORDER BY ID')
    ->fetchAll(PDO::FETCH_ASSOC);
if (count($all) > 1000) {
    throw new RuntimeException('Equipment full-row fingerprint scope exceeds cap');
}
$now = time();
$selected = [];
$toOnline = 0;
$toOffline = 0;
$nearBoundary = 0;
foreach ($all as $row) {
    if ((int)$row['del_flag'] !== 0 || (int)$row['mate_flag'] !== 1) continue;
    $selected[] = $row;
    $age = $now - (int)$row['hearttime'];
    $status = (int)$row['status'];
    if (abs($age - 60) <= 5) $nearBoundary++;
    if ($age <= 60 && $status === 2) $toOnline++;
    elseif ($age > 60 && $status === 1) $toOffline++;
}
$sourceSha = hash('sha256', serialize($all));
echo json_encode(['status' => 'select-only-heartbeat-preflight',
    'lane' => $lane, 'database_fingerprint' => $approvedDbSha,
    'equipment_rows_total' => count($all),
    'matched_active_paired_equipment' => count($selected),
    'non_this_run_to_online' => $toOnline,
    'non_this_run_to_offline' => $toOffline,
    'non_this_run_total_would_change' => $toOnline + $toOffline,
    'hearttime_within_five_seconds_of_threshold' => $nearBoundary,
    'full_table_digest' => $sourceSha,
    'real_cli_allowed' => false, 'command_started' => false,
    'redis_or_device_called' => false, 'dml_sent' => false],
    JSON_UNESCAPED_SLASHES) . PHP_EOL;
