<?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';
$dialect = $lane === 'B' ? 'mysql' : 'dm';
$refundCase = getenv('POC_CASE') === 'REFUND';
$marker = $refundCase
    ? ($lane === 'B' ? 'CDXPPREF12B01-O1' : 'CDXPPREF12C01-O1')
    : ($lane === 'B' ? 'CDXPPPAY12B01' : 'CDXPPPAY12C01');
$aiUserId = $refundCase ? ($lane === 'B' ? 46921 : 1936) : ($lane === 'B' ? 46916 : 1931);
$storeUserId = $refundCase ? ($lane === 'B' ? 99700 : 2799) : ($lane === 'B' ? 99689 : 2792);
$expectedStoreId = $refundCase ? 10001 : 2147001111;
$requestHost = $refundCase ? 'prepay.refund10001.invalid' : 'prepay.code-scan.invalid';
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) || empty($ini['DATABASE']) || empty($ini['DATABASE_ZHCT'])) {
        throw new RuntimeException('Local AI development DB config unavailable');
    }
    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' : '')));
    }
    putenv('POC_DIALECT=mysql');
    $local = require '/Users/wiscourper/Documents/workspace/zhct/zhctproject/store/application/config/local/database.php';
    foreach (['HOST' => 'hostname', 'PORT' => 'hostport', 'SCHEMA' => 'database',
        'USER' => 'username', 'PASSWORD' => 'password'] as $suffix => $field) {
        $default = $suffix === 'PORT' ? '3306' : '';
        if ((string)getenv('POC_' . $suffix) !== (string)($local[$field] ?? $default)
            || (string)getenv('POC_AI_' . $suffix) !== (string)($local['ai_zhct'][$field] ?? $default)) {
            throw new RuntimeException('AI/Store local dev DB identity differs');
        }
    }
} 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 DM AI worker');
    }
    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);
    }
}
if (getenv('POC_DIALECT') !== $dialect) throw new RuntimeException('Wrong DB dialect');
$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 * FROM ' . $table('prepay_order')
    . ' WHERE DEVICE_ORDER_NO=?', [$marker]);
if (count($prepayRows) !== 1) throw new RuntimeException('One owned PAY prepay required');
$prepay = $prepayRows[0];
$orderId = (int)($prepay['meal_order_id'] ?? 0);
$orders = $orderId > 0 ? $all($store, 'SELECT * FROM ' . $table('meal_order')
    . ' WHERE ID=?', [$orderId]) : [];
if (count($orders) !== 1) throw new RuntimeException('One owned pending meal order required');
$order = $orders[0];
$aiRows = $all($ai, 'SELECT * FROM ' . $table('user', true)
    . ' WHERE USER_ID=?', [$aiUserId]);
$storeUsers = $all($store, 'SELECT * FROM ' . $table('user')
    . ' WHERE ID=?', [$storeUserId]);
if (count($aiRows) !== 1 || count($storeUsers) !== 1
    || (int)$aiRows[0]['store_id'] !== $expectedStoreId
    || (int)$storeUsers[0]['ai_user_id'] !== $aiUserId
    || strpos((string)$storeUsers[0]['account'], 'CDX') !== 0
    || (int)$order['user_id'] !== $storeUserId
    || (int)$order['source'] !== 8
    || (int)$order['pay_status'] !== 10
    || (int)$order['order_status'] !== 10
    || (int)$prepay['prepay_status'] !== 2
    || (int)$prepay['user_id'] !== $storeUserId
    || bccomp((string)$order['pay_price'], '0.01', 2) !== 0
    || strtotime((string)$order['bind_time']) < time() - 160) {
    throw new RuntimeException('Owned pending meal is not within safe 3-minute pay window');
}
$settlementsBefore = $all($ai, 'SELECT SETTLEMENT_ID FROM '
    . $table('account_settlement', true) . ' WHERE BIZ_ORDER_NO=?', [(string)$order['order_no']]);
if ($settlementsBefore) throw new RuntimeException('Owned order already has account settlement');
$walletBefore = [(string)$aiRows[0]['balance'],
    (string)$aiRows[0]['subsidy_balance'], (string)$aiRows[0]['gift_balance']];
if (bccomp($walletBefore[0], '0.01', 2) < 0) {
    throw new RuntimeException('Owned synthetic wallet has insufficient cash');
}
$_SERVER['HTTP_HOST'] = $requestHost;
$_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,
    'prepay.refund10001.invalid' => 10001]);
$app->env->set('redis.hostname', '');
require_once $root . '/app/api/common.php';
$oneCard = (new \app\common\service\onecard\UserGuard())
    ->isOneCardUserByZhctIdentity($storeUserId, (string)$storeUsers[0]['staff_uuid']);
if ($oneCard) throw new RuntimeException('One-card user cannot use isolated local wallet path');
$restaurantRows = $all($store, 'SELECT STALL_TYPE FROM ' . $table('restaurants')
    . ' WHERE ID=?', [(int)$order['restaurant_id']]);
if (count($restaurantRows) !== 1 || (int)$restaurantRows[0]['stall_type'] !== 1) {
    throw new RuntimeException('Only no-pickup restaurant type1 allowed');
}
$balance = (new \app\common\service\account\AccountSettlementService())
    ->getAvailableBalance($aiUserId);
if (bccomp((string)$balance['total'], '0.01', 2) < 0) {
    throw new RuntimeException('Real account service sees insufficient funds');
}
if (getenv('POC_DRY_RUN') === '1') {
    echo json_encode(['lane' => $lane, 'marker' => $marker,
        'owned_order' => $orderId, 'wallet_cash' => $walletBefore[0],
        'available_total' => (string)$balance['total'],
        'one_card_user' => false, 'restaurant_stall_type' => 1,
        'external_method' => 'none-balance', 'HTTP' => 0, 'DML' => 0]) . PHP_EOL;
    exit(0);
}
$attemptPath = $root . '/' . ($refundCase ? 'refund-balance-pay-attempt.json' : 'pay-attempt.json');
$receiptPath = $root . '/' . ($refundCase ? 'refund-balance-pay-receipt.json' : 'pay-receipt.json');
if (is_file($attemptPath) || is_file($receiptPath)) {
    throw new RuntimeException('Owned balance payment already attempted; no resend');
}
$base = 'http://127.0.0.1:' . ($lane === 'B' ? 19133 : 19134);
$boundary = json_decode((string)file_get_contents($base . '/__poc_boundary'), true);
if (empty($boundary['external_stubs'])
    || ($boundary[$refundCase ? 'host_map_refund' : 'host_map'] ?? '') !== $requestHost) {
    throw new RuntimeException('Private payment HTTP boundary unavailable');
}
$token = ($refundCase ? 'CDXPPREF12_' : 'CDXPPPAY12_') . $lane . '_'
    . bin2hex(random_bytes(20));
$profile = ['user' => ['user_id' => $aiUserId],
    'password_version' => (int)($storeUsers[0]['password_version']
        ?? $aiRows[0]['password_version'] ?? 0),
    'login_type' => 'thirdparty'];
if (!$app->cache->set($token, $profile, 120)) {
    throw new RuntimeException('Private payment auth token creation failed');
}
$verified = \app\api\model\User::getUserByToken($token);
if (!$verified || (int)$verified['user_id'] !== $aiUserId
    || (int)$verified['store_id'] !== $expectedStoreId) {
    $app->cache->delete($token);
    throw new RuntimeException('Actual payment auth failed');
}
$form = ['orderId' => (string)$orderId, 'method' => 'balance', 'client' => 'APP'];
$body = http_build_query($form, '', '&', PHP_QUERY_RFC3986);
$stubFile = $root . '/runtime/poc-stub-hits.log';
$stubBeforeLines = is_file($stubFile) ? file($stubFile, FILE_IGNORE_NEW_LINES) : [];
$attempt = ['lane' => $lane, 'marker' => $marker, 'order_id' => $orderId,
    'path' => '/api/MealCashier/orderPay', 'method' => 'balance',
    'body_sha256' => hash('sha256', $body), 'token_sha256' => hash('sha256', $token),
    'before_wallet' => $walletBefore,
    'before_settlement_rows' => 0,
    'before_stub_events' => count($stubBeforeLines ?: []),
    'source_sha256' => hash_file('sha256', $root . '/app/api/service/cashier/MealPayment.php'),
    'at' => date('c')];
$handle = fopen($attemptPath, 'xb');
if (!$handle) throw new RuntimeException('Could not lock payment 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' => 45,
    'header' => "Content-Type: application/x-www-form-urlencoded\r\n"
        . "Host: " . $requestHost . "\r\nplatform: POC\r\nAccess-Token: "
        . $token . "\r\n",
    'content' => $body,
]]);
$started = microtime(true);
try {
    $wire = file_get_contents($base . '/api/MealCashier/orderPay', false, $context);
} finally {
    $app->cache->delete($token);
}
$duration = microtime(true) - $started;
$httpStatus = 0;
foreach (($http_response_header ?? []) as $header) {
    if (preg_match('/^HTTP\/\S+ (\d{3})/', $header, $match)) $httpStatus = (int)$match[1];
}
$response = is_string($wire) ? json_decode($wire, true) : null;
$orderAfterRows = $all($store, 'SELECT * FROM ' . $table('meal_order')
    . ' WHERE ID=?', [$orderId]);
$orderAfter = count($orderAfterRows) === 1 ? $orderAfterRows[0] : [];
$prepayAfterRows = $all($store, 'SELECT * FROM ' . $table('prepay_order')
    . ' WHERE ID=?', [(int)$prepay['id']]);
$prepayAfter = count($prepayAfterRows) === 1 ? $prepayAfterRows[0] : [];
$aiAfterRows = $all($ai, 'SELECT BALANCE,SUBSIDY_BALANCE,GIFT_BALANCE FROM '
    . $table('user', true) . ' WHERE USER_ID=?', [$aiUserId]);
$walletAfter = count($aiAfterRows) === 1
    ? [(string)$aiAfterRows[0]['balance'],
        (string)$aiAfterRows[0]['subsidy_balance'],
        (string)$aiAfterRows[0]['gift_balance']] : [];
$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=?', [(string)$order['order_no']]);
$balanceLogs = $all($ai, 'SELECT LOG_ID,MONEY FROM '
    . $table('user_balance_log', true) . ' WHERE USER_ID=? AND ORDER_ID=?',
    [$aiUserId, $orderId]);
$stubLines = is_file($stubFile) ? file($stubFile, FILE_IGNORE_NEW_LINES) : [];
$newStubLines = array_slice($stubLines ?: [], count($stubBeforeLines ?: []));
$receipt = [
    'lane' => $lane, 'marker' => $marker, 'order_id' => $orderId,
    'http_status' => $httpStatus, 'response_json' => is_array($response),
    'response_status' => $response['status'] ?? null,
    'response_keys' => is_array($response) ? array_keys($response) : [],
    'response_data_keys' => is_array($response['data'] ?? null)
        ? array_keys($response['data']) : [],
    'response_settlement_types' => is_array($response['data']['settlement'] ?? null)
        ? array_map('gettype', $response['data']['settlement']) : [],
    'body_sha256' => hash('sha256', (string)$wire),
    'duration_seconds' => round($duration, 3),
    'meal_rows' => count($orderAfterRows),
    'meal_pay_status' => (int)($orderAfter['pay_status'] ?? -1),
    'meal_order_status' => (int)($orderAfter['order_status'] ?? -1),
    'meal_pay_method' => (string)($orderAfter['pay_method'] ?? ''),
    'meal_cash' => (string)($orderAfter['cash'] ?? ''),
    'prepay_rows' => count($prepayAfterRows),
    'prepay_status' => (int)($prepayAfter['prepay_status'] ?? -1),
    'prepay_update_by' => (string)($prepayAfter['update_by'] ?? ''),
    'wallet_cash_before' => $walletBefore[0],
    'wallet_cash_after' => $walletAfter[0] ?? null,
    'other_wallets_unchanged' => count($walletAfter) === 3
        && $walletBefore[1] === $walletAfter[1]
        && $walletBefore[2] === $walletAfter[2],
    'settlement_rows' => count($settlements),
    'settlement_cash' => (string)($settlements[0]['cash_amount'] ?? ''),
    'settlement_amount' => (string)($settlements[0]['amount'] ?? ''),
    'settlement_status' => (int)($settlements[0]['status'] ?? -1),
    'settlement_biz_type' => (string)($settlements[0]['biz_type'] ?? ''),
    'balance_log_rows' => count($balanceLogs),
    'external_stub_events_delta' => count($newStubLines),
    'external_stub_events_new' => array_values(array_unique($newStubLines)),
];
$receipt['passed'] = $httpStatus === 200
    && $receipt['response_json'] && (int)$receipt['response_status'] === 200
    && $receipt['meal_rows'] === 1
    && $receipt['meal_pay_status'] === 20
    && $receipt['meal_order_status'] === 30
    && $receipt['meal_pay_method'] === 'balance'
    && bccomp($receipt['meal_cash'], '0.01', 2) === 0
    && $receipt['prepay_rows'] === 1 && $receipt['prepay_status'] === 1
    && $receipt['prepay_update_by'] === 'pay_success'
    && bccomp((string)$receipt['wallet_cash_after'],
        bcsub($walletBefore[0], '0.01', 2), 2) === 0
    && $receipt['other_wallets_unchanged']
    && $receipt['settlement_rows'] === 1
    && bccomp($receipt['settlement_cash'], '0.01', 2) === 0
    && bccomp($receipt['settlement_amount'], '0.01', 2) === 0
    && $receipt['settlement_status'] === 20
    && $receipt['settlement_biz_type'] === 'consume'
    && $receipt['balance_log_rows'] === 1;
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);
