<?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 . '-ai';
$marker = $lane === 'B' ? 'CDXPPPAY12B01' : 'CDXPPPAY12C01';
$aiUserId = $lane === 'B' ? 46916 : 1931;
$storeUserId = $lane === 'B' ? 99689 : 2792;
$dialect = $lane === 'B' ? 'mysql' : 'dm';
if ($lane === 'B') {
    $ini = parse_ini_file('/Users/wiscourper/Documents/workspace/zhct/zhctproject/ai_api/.env.local',
        true, INI_SCANNER_RAW);
    if (!is_array($ini)) throw new RuntimeException('Missing local AI config');
    foreach (['HOST' => 'HOSTNAME', 'PORT' => 'HOSTPORT', 'SCHEMA' => 'DATABASE',
        'USER' => 'USERNAME', 'PASSWORD' => 'PASSWORD'] as $suffix => $source) {
        putenv('POC_AI_' . $suffix . '=' . ($ini['DATABASE'][$source] ?? ($suffix === 'PORT' ? '3306' : '')));
        putenv('POC_' . $suffix . '=' . ($ini['DATABASE_ZHCT'][$source] ?? ($suffix === 'PORT' ? '3306' : '')));
    }
} else {
    $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 AI process');
    }
    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);
    }
}
$connect = static function (bool $isAi) use ($dialect): PDO {
    $prefix = $isAi ? 'POC_AI_' : 'POC_';
    $host = getenv($prefix . 'HOST');
    $port = getenv($prefix . 'PORT');
    $schema = getenv($prefix . 'SCHEMA');
    $dsn = $dialect === 'dm'
        ? "dm:host=$host;port=$port;schema=$schema"
        : "mysql:host=$host;port=$port;dbname=$schema;charset=utf8mb4";
    return new PDO($dsn, getenv($prefix . 'USER'), getenv($prefix . 'PASSWORD'),
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
};
$ai = $connect(true);
$store = $connect(false);
$table = static function (string $name, bool $isAi = false) use ($dialect): string {
    $prefix = $isAi ? 'yoshop_' : 'ydy_';
    return $dialect === 'dm' ? strtoupper($prefix . $name) : $prefix . $name;
};
$all = static function (PDO $db, string $sql, array $params = []): array {
    $stmt = $db->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));
};
$prepayRows = $all($store, 'SELECT ID,DEVICE_ORDER_NO,MEAL_ORDER_ID,USER_ID,PREPAY_STATUS FROM '
    . $table('prepay_order') . ' WHERE DEVICE_ORDER_NO=?', [$marker]);
if (count($prepayRows) !== 1) throw new RuntimeException('Own paid prepay missing');
$prepay = $prepayRows[0];
$orderId = (int)$prepay['meal_order_id'];
$orderRows = $all($store, 'SELECT ID,ORDER_NO,USER_ID,PAY_STATUS,ORDER_STATUS,REFUND_STATUS,'
    . 'PAY_PRICE,PAY_METHOD FROM ' . $table('meal_order') . ' WHERE ID=?', [$orderId]);
$order = count($orderRows) === 1 ? $orderRows[0] : [];
$users = $all($store, 'SELECT AI_USER_ID,ACCOUNT,PASSWORD_VERSION FROM '
    . $table('user') . ' WHERE ID=?', [$storeUserId]);
$aiUsers = $all($ai, 'SELECT BALANCE,SUBSIDY_BALANCE,GIFT_BALANCE,STORE_ID FROM '
    . $table('user', true) . ' WHERE USER_ID=?', [$aiUserId]);
if (count($users) !== 1 || count($aiUsers) !== 1 || !$order
    || (int)$users[0]['ai_user_id'] !== $aiUserId
    || strpos((string)$users[0]['account'], 'CDX') !== 0
    || (int)$aiUsers[0]['store_id'] !== 2147001111
    || (int)$order['user_id'] !== $storeUserId
    || (int)$order['pay_status'] !== 20
    || (int)$order['order_status'] !== 30
    || (int)$order['refund_status'] !== 0
    || (int)$prepay['prepay_status'] !== 1
    || bccomp((string)$order['pay_price'], '0.01', 2) !== 0) {
    throw new RuntimeException('Owned order is not paid once and unrefunded');
}
$snapshot = static function () use ($all, $ai, $store, $table, $orderId, $order,
    $aiUserId, $prepay): array {
    $wallet = $all($ai, 'SELECT BALANCE,SUBSIDY_BALANCE,GIFT_BALANCE FROM '
        . $table('user', true) . ' WHERE USER_ID=?', [$aiUserId]);
    $settlements = $all($ai, 'SELECT SETTLEMENT_ID,CASH_AMOUNT,SUBSIDY_AMOUNT,GIFT_AMOUNT,'
        . 'AMOUNT,STATUS,BIZ_TYPE FROM ' . $table('account_settlement', true)
        . ' WHERE BIZ_ORDER_NO=? ORDER BY SETTLEMENT_ID', [(string)$order['order_no']]);
    $logs = $all($ai, 'SELECT LOG_ID,MONEY FROM ' . $table('user_balance_log', true)
        . ' WHERE USER_ID=? AND ORDER_ID=? ORDER BY LOG_ID', [$aiUserId, $orderId]);
    $meal = $all($store, 'SELECT PAY_STATUS,ORDER_STATUS,REFUND_STATUS FROM '
        . $table('meal_order') . ' WHERE ID=?', [$orderId]);
    $prepayCurrent = $all($store, 'SELECT PREPAY_STATUS FROM '
        . $table('prepay_order') . ' WHERE ID=?', [(int)$prepay['id']]);
    return ['wallet' => $wallet, 'settlements' => $settlements,
        'balance_logs' => $logs, 'meal' => $meal, 'prepay' => $prepayCurrent];
};
$before = $snapshot();
if (count($before['settlements']) !== 1 || count($before['balance_logs']) !== 1
    || bccomp((string)$before['wallet'][0]['balance'], '0.96', 2) !== 0) {
    throw new RuntimeException('Paid source ledger drifted before duplicate');
}
if (getenv('POC_DRY_RUN') === '1') {
    echo json_encode(['lane' => $lane, 'owned_order' => $orderId,
        'cash_before' => (string)$before['wallet'][0]['balance'],
        'settlement_rows' => 1, 'cash_log_rows' => 1,
        'HTTP' => 0, 'DML' => 0]) . PHP_EOL;
    exit(0);
}
$attemptPath = $root . '/duplicate-pay-attempt.json';
$rawPath = $root . '/duplicate-pay-response.raw';
$receiptPath = $root . '/duplicate-pay-receipt.json';
if (is_file($attemptPath) || is_file($rawPath) || is_file($receiptPath)) {
    throw new RuntimeException('Duplicate test already attempted');
}
$_SERVER['HTTP_HOST'] = 'prepay.code-scan.invalid';
$_SERVER['HTTP_PLATFORM'] = 'POC';
define('TP_ENV', 'poc');
define('BUSINESS', 'aizhct');
define('ENTERPRISE_NO', '10001');
require $root . '/vendor/autoload.php';
spl_autoload_register(static function (string $class) use ($root): void {
    foreach (['app\\' => 'app/', 'cores\\' => 'cores/'] as $prefix => $directory) {
        if (strpos($class, $prefix) !== 0) continue;
        $file = $root . '/' . $directory
            . str_replace('\\', '/', substr($class, strlen($prefix))) . '.php';
        if (is_file($file)) require_once $file;
        return;
    }
}, true, true);
$app = (new \think\App($root))->setEnvName('poc');
$app->initialize();
$app->env->set('store.host', ['prepay.code-scan.invalid' => 2147001111]);
$app->env->set('redis.hostname', '');
require_once $root . '/app/api/common.php';
$token = 'CDXPPDUP12_' . $lane . '_' . bin2hex(random_bytes(20));
$version = (int)($users[0]['password_version'] ?? 0);
if (!$app->cache->set($token, [
    'user' => ['user_id' => $aiUserId], 'password_version' => $version,
    'login_type' => 'thirdparty',
], 120)) throw new RuntimeException('Private duplicate token failed');
$verified = \app\api\model\User::getUserByToken($token);
if (!$verified || (int)$verified['user_id'] !== $aiUserId
    || (int)$verified['store_id'] !== 2147001111) {
    $app->cache->delete($token);
    throw new RuntimeException('Actual duplicate auth failed');
}
$form = ['orderId' => (string)$orderId, 'method' => 'balance', 'client' => 'APP'];
$body = http_build_query($form, '', '&', PHP_QUERY_RFC3986);
$stubPath = $root . '/runtime/poc-stub-hits.log';
$stubBefore = is_file($stubPath) ? count(file($stubPath, FILE_IGNORE_NEW_LINES)) : 0;
$attempt = ['lane' => $lane, 'marker' => $marker,
    'order_id' => $orderId, 'route' => '/api/MealCashier/orderPay',
    'body_sha256' => hash('sha256', $body),
    'token_sha256' => hash('sha256', $token),
    'before_wallet_cash' => (string)$before['wallet'][0]['balance'],
    'before_settlements' => count($before['settlements']),
    'before_balance_logs' => count($before['balance_logs']),
    'before_stub_events' => $stubBefore, 'at' => date('c')];
$handle = fopen($attemptPath, 'xb');
if (!$handle) throw new RuntimeException('Duplicate attempt lock failed');
fwrite($handle, json_encode($attempt, JSON_UNESCAPED_SLASHES));
fclose($handle);
chmod($attemptPath, 0600);
$base = 'http://127.0.0.1:' . ($lane === 'B' ? 19133 : 19134);
$context = stream_context_create(['http' => [
    'method' => 'POST', 'ignore_errors' => true, 'timeout' => 40,
    'header' => "Content-Type: application/x-www-form-urlencoded\r\n"
        . "Host: prepay.code-scan.invalid\r\nplatform: POC\r\nAccess-Token: "
        . $token . "\r\n",
    'content' => $body,
]]);
try {
    $wire = file_get_contents($base . '/api/MealCashier/orderPay', false, $context);
} finally {
    $app->cache->delete($token);
}
$raw = is_string($wire) ? $wire : '';
$handle = fopen($rawPath, 'xb');
if (!$handle) throw new RuntimeException('Could not preserve raw duplicate response');
fwrite($handle, $raw);
fclose($handle);
chmod($rawPath, 0600);
$status = 0;
foreach (($http_response_header ?? []) as $header) {
    if (preg_match('/^HTTP\/\S+ (\d{3})/', $header, $match)) $status = (int)$match[1];
}
$response = json_decode($raw, true);
$after = $snapshot();
$stubAfter = is_file($stubPath) ? count(file($stubPath, FILE_IGNORE_NEW_LINES)) : 0;
$receipt = ['lane' => $lane, 'marker' => $marker,
    'http_status' => $status, 'response_json' => is_array($response),
    'response_status' => $response['status'] ?? null,
    'response_keys' => is_array($response) ? array_keys($response) : [],
    'response_body_sha256' => hash('sha256', $raw),
    'response_raw_file' => basename($rawPath),
    'wallet_cash_before' => (string)$before['wallet'][0]['balance'],
    'wallet_cash_after' => (string)($after['wallet'][0]['balance'] ?? ''),
    'wallet_exact_unchanged' => $before['wallet'] === $after['wallet'],
    'settlements_exact_unchanged' => $before['settlements'] === $after['settlements'],
    'balance_logs_exact_unchanged' => $before['balance_logs'] === $after['balance_logs'],
    'meal_exact_unchanged' => $before['meal'] === $after['meal'],
    'prepay_exact_unchanged' => $before['prepay'] === $after['prepay'],
    'stub_events_delta' => $stubAfter - $stubBefore];
$receipt['passed'] = $status === 200 && $receipt['response_json']
    && (int)$receipt['response_status'] !== 200
    && $receipt['wallet_exact_unchanged']
    && $receipt['settlements_exact_unchanged']
    && $receipt['balance_logs_exact_unchanged']
    && $receipt['meal_exact_unchanged']
    && $receipt['prepay_exact_unchanged']
    && $receipt['stub_events_delta'] === 0;
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);
