<?php
declare(strict_types=1);

// SELECT-only Store e-menu device log schema/batch boundary, no business rows.
$lane = (string)getenv('CDX_EM_LOG_LANE');
if (!in_array($lane, ['A', 'B', 'C'], true)
    || count($argv) !== 2 || ($argv[1] ?? '') !== '--preflight-only'
    || (string)getenv('CDX_EM_LOG_PREFLIGHT') !== 'OWNED_DEVICE_LOG_BATCH_SELECT_ONLY_V1'
    || !hash_equals((string)hash_file('sha256', __FILE__),
        (string)getenv('CDX_EM_LOG_PREFLIGHT_SHA'))) {
    echo json_encode(['status' => 'default-deny', 'db_connected' => false,
        'business_http_sent' => false, 'dml_sent' => false]), PHP_EOL;
    exit(78);
}
$stage = 'runtime_identity';
set_exception_handler(static function (Throwable $error) use (&$stage): void {
    echo json_encode(['status' => 'redacted_stop', 'stage' => $stage,
        'error_class' => get_class($error), 'code' => (string)$error->getCode(),
        'business_http_sent' => false, 'dml_sent' => false]), PHP_EOL;
    exit(2);
});
$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];
if (PHP_VERSION_ID < 70300 || PHP_VERSION_ID >= 70400
    || is_link($root)
    || hash_file('sha256', $root . '/application/common/service/ElectronicMenuDevice.php')
        !== '3335769aee94126f7abfae2f2a305ba09cca9270df2ab7590657c00fabef855f'
    || hash_file('sha256', $root . '/application/api/controller/ElectronicMenu.php')
        !== '2c1df1125c5ced2abd4f97a460f4290950d8ca5fbb978d63800e76ba5d60e056') {
    throw new RuntimeException('Frozen device-log source changed');
}
$cmd = (string)@file_get_contents('/proc/' . $pid . '/cmdline');
$rawEnv = (string)@file_get_contents('/proc/' . $pid . '/environ');
$children = trim((string)@file_get_contents('/proc/' . $pid
    . '/task/' . $pid . '/children'));
if (strpos($cmd, '127.0.0.1:' . $port) === false
    || strpos($cmd, $root . '/public') === false
    || $rawEnv === '' || $children !== ''
    || strpos($rawEnv, 'PHP_CLI_SERVER_WORKERS=') !== false) {
    throw new RuntimeException('Fixed single-worker listener changed');
}
$boundary = json_decode((string)@file_get_contents(
    'http://127.0.0.1:' . $port . '/__poc_boundary', false,
    stream_context_create(['http' => ['timeout' => 5]])), true);
if (!is_array($boundary) || ($boundary['external_stubs'] ?? null) !== true
    || ($boundary['redis_isolated'] ?? null) !== true
    || ($boundary['real_queue'] ?? true) !== false) {
    throw new RuntimeException('External/Redis/queue isolation absent');
}
$config = require $root . '/application/database.php';
if (!is_array($config)) throw new RuntimeException('Runtime database config invalid');
if ($dialect === 'mysql') {
    $local = require '/workspace/zhct/zhctproject/store/application/config/local/database.php';
    foreach (['hostname', 'hostport', 'database', 'username', 'password', 'type'] as $field) {
        if (($config[$field] ?? null) !== ($local[$field] ?? null)) {
            throw new RuntimeException('Not code-local development MySQL');
        }
    }
    $dsn = 'mysql:host=' . $config['hostname'] . ';port=' . (int)$config['hostport']
        . ';dbname=' . $config['database'] . ';charset=' . ($config['charset'] ?? 'utf8');
    $dbUser = (string)$config['username'];
    $dbPassword = (string)$config['password'];
} else {
    foreach (['hostname' => 'POC_HOST', 'hostport' => 'POC_PORT',
        'database' => 'POC_SCHEMA', 'username' => 'POC_USER',
        'password' => 'POC_PASSWORD'] as $field => $envKey) {
        if ((string)($config[$field] ?? '') !== (string)getenv($envKey)) {
            throw new RuntimeException('C runtime differs from nonprod DM');
        }
    }
    if ((string)getenv('POC_HOST') !== '127.0.0.1'
        || (string)getenv('POC_PORT') !== '5237'
        || strtoupper((string)getenv('POC_SCHEMA')) !== 'ZHCT_APP') {
        throw new RuntimeException('C DM target wrong');
    }
    $machine = trim((string)@file_get_contents('/etc/machine-id'));
    if ($machine === '' || gethostname() === false
        || hash('sha256', gethostname() . '|' . $machine)
            !== 'd56d81988bb0f106946cce91c608fdd0ef1116e5150b23376ea87e086eace4b9') {
        throw new RuntimeException('C DM host wrong');
    }
    $dsn = 'dm:host=127.0.0.1;port=5237;schema=ZHCT_APP';
    $dbUser = (string)getenv('POC_USER');
    $dbPassword = (string)getenv('POC_PASSWORD');
}
$stage = 'database_identity_and_metadata';
$db = new PDO($dsn, $dbUser, $dbPassword,
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_CASE => PDO::CASE_LOWER, PDO::ATTR_TIMEOUT => 5]);
if ($dialect === 'mysql') {
    $identity = (string)$db->query(
        "SELECT CONCAT(@@server_uuid,'|',@@hostname,'|',@@port,'|',DATABASE())")
        ->fetchColumn();
    if (hash('sha256', $identity)
        !== '003eb5aecf1cd4595b609c1f7f20b9ee325b578309fb9fe14fedaf77584fa401') {
        throw new RuntimeException('Developer MySQL identity mismatch');
    }
} else {
    $schema = strtoupper((string)$db->query(
        "SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn());
    if ($schema !== 'ZHCT_APP') throw new RuntimeException('C schema mismatch');
}
$prefix = $dialect === 'dm' ? 'YDY_' : 'ydy_';
$logTable = $prefix . 'electronic_menu_device_log';
$deviceTable = $prefix . 'electronic_menu_device';
$menuTable = $prefix . 'electronic_menu';
$threeNames = $dialect === 'dm'
    ? ['YDY_ELECTRONIC_MENU', 'YDY_ELECTRONIC_MENU_DEVICE',
        'YDY_ELECTRONIC_MENU_DEVICE_LOG']
    : ['ydy_electronic_menu', 'ydy_electronic_menu_device',
        'ydy_electronic_menu_device_log'];
if ($dialect === 'dm') {
    $logTable = strtoupper($logTable);
    $deviceTable = strtoupper($deviceTable);
    $menuTable = strtoupper($menuTable);
}
$requiredLog = ['UUID', 'DEVICE_RECORD_ID', 'MENU_ID', 'LEVEL', 'EVENT_CODE',
    'ERROR_CODE', 'MESSAGE', 'CONTEXT_JSON', 'OCCURRED_AT', 'STORE_ID',
    'IS_DELETE', 'CREATE_BY', 'CREATE_TIME', 'UPDATE_TIME'];
$requiredDevice = ['ID', 'UUID', 'DEVICE_ID', 'DEVICE_TOKEN_HASH', 'MENU_ID',
    'STATUS', 'IS_DELETE', 'LAST_SEEN_TIME', 'STORE_ID'];
$db->query('SELECT ' . implode(',', $requiredLog) . ' FROM ' . $logTable . ' WHERE 1=0');
$db->query('SELECT ' . implode(',', $requiredDevice) . ' FROM ' . $deviceTable . ' WHERE 1=0');
$db->query('SELECT ID,UUID,PUBLIC_TOKEN,STATUS,STORE_ID,IS_DELETE'
    . ' FROM ' . $menuTable . ' WHERE 1=0');
if ($dialect === 'mysql') {
    $meta = $db->query('SELECT TABLE_NAME,COLUMN_NAME,DATA_TYPE,'
        . ' CHARACTER_MAXIMUM_LENGTH,IS_NULLABLE FROM INFORMATION_SCHEMA.COLUMNS'
        . ' WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME IN ('
        . "'ydy_electronic_menu_device_log','ydy_electronic_menu_device')")
        ->fetchAll(PDO::FETCH_ASSOC);
    $encoding = (string)$db->query('SELECT @@character_set_connection')->fetchColumn();
} else {
    $meta = $db->query('SELECT TABLE_NAME,COLUMN_NAME,DATA_TYPE,'
        . ' CHAR_LENGTH,DATA_LENGTH,NULLABLE FROM USER_TAB_COLUMNS'
        . " WHERE TABLE_NAME IN ('YDY_ELECTRONIC_MENU_DEVICE_LOG',"
        . "'YDY_ELECTRONIC_MENU_DEVICE')")
        ->fetchAll(PDO::FETCH_ASSOC);
    $encoding = 'dm_driver';
}
$wanted = ['MESSAGE', 'CONTEXT_JSON', 'OCCURRED_AT', 'EVENT_CODE',
    'ERROR_CODE', 'LEVEL', 'CREATE_BY', 'LAST_SEEN_TIME'];
$shape = [];
foreach ($meta as $row) {
    $name = strtoupper((string)$row['column_name']);
    if (!in_array($name, $wanted, true)) continue;
    $table = strtoupper((string)$row['table_name']);
    $shape[$table][$name] = [
        'data_type' => strtoupper((string)$row['data_type']),
        'char_length' => $dialect === 'mysql'
            ? ($row['character_maximum_length'] === null
                ? null : (int)$row['character_maximum_length'])
            : ($row['char_length'] === null ? null : (int)$row['char_length']),
        'nullable' => $dialect === 'mysql'
            ? (string)$row['is_nullable'] : (string)$row['nullable'],
    ];
}
foreach (['MESSAGE', 'CONTEXT_JSON', 'OCCURRED_AT', 'EVENT_CODE',
    'ERROR_CODE', 'LEVEL', 'CREATE_BY'] as $name) {
    if (!isset($shape['YDY_ELECTRONIC_MENU_DEVICE_LOG'][$name])) {
        throw new RuntimeException('Log schema metadata incomplete');
    }
}
if (!isset($shape['YDY_ELECTRONIC_MENU_DEVICE']['LAST_SEEN_TIME'])) {
    throw new RuntimeException('Device time metadata incomplete');
}
$stage = 'trigger_fk_identity_and_owned_zero';
if ($dialect === 'mysql') {
    $triggers = (int)$db->query('SELECT COUNT(*) FROM INFORMATION_SCHEMA.TRIGGERS'
        . ' WHERE EVENT_OBJECT_SCHEMA=DATABASE() AND EVENT_OBJECT_TABLE IN ('
        . "'ydy_electronic_menu','ydy_electronic_menu_device',"
        . "'ydy_electronic_menu_device_log')")->fetchColumn();
    $foreignKeys = (int)$db->query('SELECT COUNT(*) FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE'
        . ' WHERE REFERENCED_TABLE_NAME IS NOT NULL AND ('
        . "(TABLE_SCHEMA=DATABASE() AND TABLE_NAME IN ('ydy_electronic_menu',"
        . "'ydy_electronic_menu_device','ydy_electronic_menu_device_log')) OR "
        . "(REFERENCED_TABLE_SCHEMA=DATABASE() AND REFERENCED_TABLE_NAME IN ("
        . "'ydy_electronic_menu','ydy_electronic_menu_device',"
        . "'ydy_electronic_menu_device_log')))")->fetchColumn();
    $identityRows = $db->query('SELECT TABLE_NAME,COLUMN_NAME,EXTRA'
        . ' FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE()'
        . " AND TABLE_NAME IN ('ydy_electronic_menu',"
        . "'ydy_electronic_menu_device','ydy_electronic_menu_device_log')"
        . " AND COLUMN_NAME='id'")->fetchAll(PDO::FETCH_ASSOC);
    $auto = [];
    foreach ($identityRows as $row) {
        $auto[strtolower((string)$row['table_name'])] =
            stripos((string)$row['extra'], 'auto_increment') !== false;
    }
} else {
    $stage = 'c_user_triggers';
    $triggers = (int)$db->query('SELECT COUNT(*) FROM USER_TRIGGERS'
        . " WHERE TABLE_NAME IN ("
        . "'YDY_ELECTRONIC_MENU','YDY_ELECTRONIC_MENU_DEVICE',"
        . "'YDY_ELECTRONIC_MENU_DEVICE_LOG')")->fetchColumn();
    $stage = 'c_outgoing_fk';
    $outgoing = (int)$db->query('SELECT COUNT(*) FROM USER_CONSTRAINTS'
        . " WHERE CONSTRAINT_TYPE='R' AND TABLE_NAME IN ("
        . "'YDY_ELECTRONIC_MENU','YDY_ELECTRONIC_MENU_DEVICE',"
        . "'YDY_ELECTRONIC_MENU_DEVICE_LOG')")->fetchColumn();
    $stage = 'c_incoming_fk';
    $incoming = (int)$db->query('SELECT COUNT(*) FROM ALL_CONSTRAINTS c'
        . ' JOIN ALL_CONSTRAINTS p ON c.R_OWNER=p.OWNER'
        . ' AND c.R_CONSTRAINT_NAME=p.CONSTRAINT_NAME'
        . " WHERE c.CONSTRAINT_TYPE='R' AND p.OWNER='ZHCT_APP'"
        . " AND p.TABLE_NAME IN ("
        . "'YDY_ELECTRONIC_MENU','YDY_ELECTRONIC_MENU_DEVICE',"
        . "'YDY_ELECTRONIC_MENU_DEVICE_LOG')")->fetchColumn();
    $foreignKeys = $outgoing + $incoming;
    $auto = [];
    foreach ($threeNames as $name) {
        $stage = 'c_table_definition_' . strtolower($name);
        $definitionQuery = $db->prepare('CALL SP_TABLEDEF(?, ?)');
        $definitionQuery->execute(['ZHCT_APP', $name]);
        $definition = $definitionQuery->fetchColumn();
        if (is_resource($definition)) $definition = stream_get_contents($definition);
        if (!is_string($definition) || $definition === '') {
            throw new RuntimeException('DM table definition unavailable');
        }
        $auto[$name] = preg_match(
            '/^\s*"ID"\s+(?:BIGINT|INT|DEC(?:IMAL)?\(\d+\s*,\s*0\))\s+'
            . '(?:AUTO_INCREMENT|IDENTITY)\b/im', $definition) === 1;
        if (!$auto[$name]) throw new RuntimeException('DM ID definition not auto increment');
    }
}
if ($triggers !== 0 || $foreignKeys !== 0
    || count($auto) !== 3) {
    throw new RuntimeException('Trigger/FK/identity structure not isolated');
}
foreach ($threeNames as $name) {
    if (($auto[$name] ?? false) !== true) {
        throw new RuntimeException('Required ID is not auto increment');
    }
}
$owned = [
    'menu' => (int)$db->query('SELECT COUNT(*) FROM ' . $menuTable
        . " WHERE CREATE_BY LIKE 'CDXEMLOG%'")->fetchColumn(),
    'device' => (int)$db->query('SELECT COUNT(*) FROM ' . $deviceTable
        . " WHERE DEVICE_ID LIKE 'CDXEMLOG%' OR CREATE_BY LIKE 'CDXEMLOG%'")
        ->fetchColumn(),
    'log' => (int)$db->query('SELECT COUNT(*) FROM ' . $logTable
        . " WHERE CREATE_BY LIKE 'device:CDXEMLOG%'"
        . " OR EVENT_CODE LIKE 'CDXEMLOG%'")->fetchColumn(),
];
if (array_sum($owned) !== 0) {
    throw new RuntimeException('Old owned log-batch fixture needs review');
}
echo json_encode(['status' => 'select_only_pass', 'lane' => $lane,
    'db' => $dialect === 'dm' ? 'nonproduction_store_dm' : 'code_local_dev_mysql',
    'connection_charset' => $encoding,
    'schema_shape' => $shape,
    'table_counts' => [
        'menus' => (int)$db->query('SELECT COUNT(*) FROM ' . $menuTable)->fetchColumn(),
        'devices' => (int)$db->query('SELECT COUNT(*) FROM ' . $deviceTable)->fetchColumn(),
        'logs' => (int)$db->query('SELECT COUNT(*) FROM ' . $logTable)->fetchColumn(),
    ],
    'trigger_count' => $triggers, 'incoming_outgoing_fk_count' => $foreignKeys,
    'auto_id_on_three_tables' => true,
    'prior_owned_marker_counts' => $owned,
    'batch_limit_from_sha_source' => 20,
    'business_http_sent' => false, 'dml_sent' => false],
    JSON_UNESCAPED_SLASHES), PHP_EOL;
