<?php

declare(strict_types=1);

$lane = $argv[1] ?? '';
if (!in_array($lane, ['B', 'C'], true)) exit(2);
$root = '/tmp/cdx-prepay-code-scan-20261012-' . $lane . '-store';
$pid = (int)file_get_contents($root . '/private-http.pid');
if (strpos((string)file_get_contents('/proc/' . $pid . '/cmdline'), $root . '/public') === false) {
    throw new RuntimeException('Wrong private Store owner');
}
foreach (explode("\0", (string)file_get_contents('/proc/' . $pid . '/environ')) as $pair) {
    $parts = explode('=', $pair, 2);
    if (count($parts) === 2 && strpos($parts[0], 'POC_') === 0) putenv($pair);
}
$boundary = json_decode((string)file_get_contents('http://127.0.0.1:'
    . ($lane === 'B' ? 19131 : 19132) . '/__poc_boundary'), true);
if (empty($boundary['external_stubs']) || empty($boundary['redis_isolated'])
    || !empty($boundary['real_queue'])) throw new RuntimeException('External boundary unsafe');
$cfg = require $root . '/application/database.php';
$dialect = $lane === 'B' ? 'mysql' : 'dm';
$dsn = $dialect === 'dm'
    ? 'dm:host=' . $cfg['hostname'] . ';port=' . $cfg['hostport'] . ';schema=' . $cfg['database']
    : 'mysql:host=' . $cfg['hostname'] . ';port=' . ($cfg['hostport'] ?? 3306)
        . ';dbname=' . $cfg['database'] . ';charset=utf8mb4';
$pdo = new PDO($dsn, $cfg['username'], $cfg['password'],
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$table = static function (string $name) use ($dialect): string {
    return $dialect === 'dm' ? 'YDY_' . strtoupper($name) : 'ydy_' . $name;
};
$all = static function (string $sql, array $params = []) use ($pdo): array {
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return array_map(static function (array $row): array {
        return array_change_key_case($row, CASE_LOWER);
    }, $stmt->fetchAll(PDO::FETCH_ASSOC));
};
$payCase = getenv('POC_CASE') === 'PAY';
$refundCase = getenv('POC_CASE') === 'REFUND';
$marker = $refundCase
    ? ($lane === 'B' ? 'CDXPPREF12B01-O1' : 'CDXPPREF12C01-O1')
    : ($payCase
        ? ($lane === 'B' ? 'CDXPPPAY12B01' : 'CDXPPPAY12C01')
        : ($lane === 'B' ? 'CDXPP121012B01' : 'CDXPP121012C01'));
$basename = $refundCase ? 'refund-code' : ($payCase ? 'pay-code' : 'code');
$attemptPath = $root . '/' . $basename . '-attempt.json';
$receiptPath = $root . '/' . $basename . '-receipt.json';
$deviceId = $lane === 'B' ? 232 : 178;
$deviceRows = $all('SELECT * FROM ' . $table('equipment') . ' WHERE ID=?', [$deviceId]);
if (count($deviceRows) !== 1) throw new RuntimeException('Owned device missing');
$device = $deviceRows[0];
if ((string)$device['sn'] !== 'CDXPPFORM11_M1' || (int)$device['type'] !== 60
    || (int)$device['del_flag'] !== 0 || (int)$device['status'] !== 2
    || strpos((string)$device['code'], 'CDXPPFORM11') !== 0) {
    throw new RuntimeException('Owned device identity changed');
}
$before = $all('SELECT ID FROM ' . $table('prepay_order') . ' WHERE DEVICE_ORDER_NO=?', [$marker]);
if ($before || is_file($attemptPath) || is_file($receiptPath)) {
    throw new RuntimeException('Owned marker already consumed; do not resend');
}
$keyRows = $all('SELECT CONFIG_VALUE FROM ' . $table('config')
    . " WHERE CONFIG_KEY='coffee_machine_appkey' AND STATUS=1");
$distinctKeys = array_values(array_unique(array_map(static function (array $row): string {
    return trim((string)$row['config_value']);
}, $keyRows)));
if (count($distinctKeys) > 1) throw new RuntimeException('Ambiguous active coffee app keys');
$appKey = isset($keyRows[0]['config_value']) && trim((string)$keyRows[0]['config_value']) !== ''
    ? trim((string)$keyRows[0]['config_value']) : '1234567890abcdef';
$timestamp = (string)time();
$randstr = bin2hex(random_bytes(16));
$signParts = [$appKey, $timestamp, $randstr];
sort($signParts, SORT_STRING);
$form = [
    'orderid' => $marker,
    'machid' => (string)$device['sn'],
    'trackno' => $marker . '-T1',
    'name' => $marker,
    'price' => '1',
    'channelid' => 'POC',
    'timestamp' => $timestamp,
    'randstr' => $randstr,
    'sign' => sha1(implode('', $signParts)),
];
if (getenv('POC_DRY_RUN') === '1') {
    echo json_encode(['lane' => $lane, 'marker' => $marker,
        'owned_device' => $deviceId, 'prepay_marker_rows' => count($before),
        'active_appkey_rows' => count($keyRows), 'no_HTTP_or_DML' => true,
        'store_service_sha256' => hash_file('sha256', $root . '/application/api/service/PrepayOrder.php')]) . PHP_EOL;
    exit(0);
}
$body = http_build_query($form, '', '&', PHP_QUERY_RFC1738);
$attempt = ['lane' => $lane, 'marker' => $marker, 'device_id' => $deviceId,
    'path' => '/api/prepayOrder/code', 'body_sha256' => hash('sha256', $body),
    'source_controller_sha256' => hash_file('sha256', $root . '/application/api/controller/PrepayOrder.php'),
    'source_service_sha256' => hash_file('sha256', $root . '/application/api/service/PrepayOrder.php'),
    'before_marker_rows' => count($before), 'at' => date('c')];
$handle = fopen($attemptPath, 'xb');
if (!$handle) throw new RuntimeException('Could not lock one-shot code attempt');
fwrite($handle, json_encode($attempt, JSON_UNESCAPED_SLASHES));
fclose($handle);
chmod($attemptPath, 0600);
$context = stream_context_create(['http' => [
    'method' => 'POST', 'ignore_errors' => true, 'timeout' => 30,
    'header' => "Content-Type: application/x-www-form-urlencoded\r\nHost: prepay.code-scan.invalid\r\n",
    'content' => $body,
]]);
$start = microtime(true);
$responseBody = file_get_contents('http://127.0.0.1:' . ($lane === 'B' ? 19131 : 19132)
    . '/api/prepayOrder/code', false, $context);
$elapsed = microtime(true) - $start;
$status = 0;
foreach (($http_response_header ?? []) as $header) {
    if (preg_match('/^HTTP\/\S+ (\d{3})/', $header, $match)) $status = (int)$match[1];
}
$response = is_string($responseBody) ? json_decode($responseBody, true) : null;
$rows = $all('SELECT * FROM ' . $table('prepay_order') . ' WHERE DEVICE_ORDER_NO=?', [$marker]);
$row = count($rows) === 1 ? $rows[0] : [];
$id = (int)($row['id'] ?? 0);
$expectedCode = 'type=200&torderid=' . $id;
$raw = json_decode((string)($row['raw_request_a'] ?? ''), true);
$receipt = [
    'lane' => $lane, 'marker' => $marker, 'http_status' => $status,
    'response_json' => is_array($response),
    'response_code' => $response['code'] ?? null,
    'response_field_types' => is_array($response) ? array_map('gettype', $response) : [],
    'response_orderid_match' => (string)($response['orderid'] ?? '') === $marker,
    'response_torderid_match' => (string)($response['torderid'] ?? '') === (string)$id,
    'response_twocode_match' => (string)($response['twocode'] ?? '') === $expectedCode,
    'body_sha256' => hash('sha256', (string)$responseBody),
    'duration_seconds' => round($elapsed, 3), 'source_rows' => count($rows),
    'prepay_id' => $id,
    'device_id_match' => (int)($row['equipment_id'] ?? 0) === $deviceId,
    'restaurant_id_match' => (int)($row['restaurant_id'] ?? 0) === (int)$device['restaurant_id'],
    'price_1_cent' => (int)($row['price'] ?? -1) === 1,
    'waiting_unpaid' => (int)($row['prepay_status'] ?? -1) === 2
        && (int)($row['ship_status'] ?? -1) === 0
        && (int)($row['meal_order_id'] ?? -1) === 0
        && (int)($row['user_id'] ?? -1) === 0,
    'raw_input_roundtrip' => $raw === $form,
    'qrcode_column_match' => (string)($row['qrcode_payload'] ?? '') === $expectedCode,
];
$receipt['passed'] = $receipt['http_status'] === 200
    && $receipt['response_json'] && (string)$receipt['response_code'] === '1'
    && $receipt['response_orderid_match'] && $receipt['response_torderid_match']
    && $receipt['response_twocode_match'] && $receipt['source_rows'] === 1
    && $receipt['prepay_id'] > 0 && $receipt['device_id_match']
    && $receipt['restaurant_id_match'] && $receipt['price_1_cent']
    && $receipt['waiting_unpaid'] && $receipt['raw_input_roundtrip']
    && $receipt['qrcode_column_match'];
file_put_contents($receiptPath, json_encode($receipt,
    JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), LOCK_EX);
chmod($receiptPath, 0600);
echo json_encode($receipt, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
exit($receipt['passed'] ? 0 : 1);
