<?php

declare(strict_types=1);

$lane = $argv[1] ?? '';
if (!in_array($lane, ['B', 'C'], true) || getenv('POC_CASE') !== 'REFUND') exit(2);
$root = '/tmp/cdx-prepay-code-scan-20261012-' . $lane . '-store';
$marker = $lane === 'B' ? 'CDXPPREF12B01-O1' : 'CDXPPREF12C01-O1';
$orderId = $lane === 'B' ? 68503 : 23666;
$prepayId = $lane === 'B' ? 26 : 5;
$actorId = $lane === 'B' ? 97260 : 2791;
$aiUserId = $lane === 'B' ? 46921 : 1936;
$storeUserId = $lane === 'B' ? 99700 : 2799;
$dialect = $lane === 'B' ? 'mysql' : 'dm';
$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 HTTP 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);
}
if (getenv('POC_DIALECT') !== $dialect) throw new RuntimeException('Wrong DB dialect');
$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 unavailable');
$cfg = require $root . '/application/database.php';
$connect = static function (array $c) use ($dialect): PDO {
    $dsn = $dialect === 'dm'
        ? 'dm:host=' . $c['hostname'] . ';port=' . $c['hostport'] . ';schema=' . $c['database']
        : 'mysql:host=' . $c['hostname'] . ';port=' . ($c['hostport'] ?? 3306)
            . ';dbname=' . $c['database'] . ';charset=utf8mb4';
    return new PDO($dsn, $c['username'], $c['password'],
        [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
};
$store = $connect($cfg);
$ai = $connect($cfg['ai_zhct']);
$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));
};
$orderRows = $all($store, 'SELECT ID,ORDER_NO,USER_ID,RESTAURANT_ID,SOURCE,PAY_STATUS,'
    . 'ORDER_STATUS,PAY_PRICE,PAY_TYPE,CASH,SUBSIDY,GIFT,REFUND_AMOUNT,REFUND_STATUS,'
    . 'CASH_REFUND_AMOUNT,SUBSIDY_REFUND_AMOUNT,GIFT_REFUND_AMOUNT FROM '
    . $table('meal_order') . ' WHERE ID=?', [$orderId]);
$order = count($orderRows) === 1 ? $orderRows[0] : [];
$prepayRows = $all($store, 'SELECT ID,DEVICE_ORDER_NO,MEAL_ORDER_ID,USER_ID,PREPAY_STATUS FROM '
    . $table('prepay_order') . ' WHERE ID=?', [$prepayId]);
$prepay = count($prepayRows) === 1 ? $prepayRows[0] : [];
$actorRows = $all($store, 'SELECT ID,UUID,ACCOUNT,NAME,TYPE,STAFF_UUID FROM '
    . $table('user') . ' WHERE ID=?', [$actorId]);
$actor = count($actorRows) === 1 ? $actorRows[0] : [];
$targetUserRows = $all($store, 'SELECT ID,AI_USER_ID,ACCOUNT FROM '
    . $table('user') . ' WHERE ID=?', [$storeUserId]);
if (!$order || !$prepay || !$actor || count($targetUserRows) !== 1
    || (string)$prepay['device_order_no'] !== $marker
    || (int)$prepay['meal_order_id'] !== $orderId
    || (int)$prepay['user_id'] !== $storeUserId
    || (int)$prepay['prepay_status'] !== 1
    || (int)$order['user_id'] !== $storeUserId
    || (int)$order['source'] !== 8
    || (int)$order['pay_status'] !== 20
    || (int)$order['order_status'] !== 30
    || (int)$order['refund_status'] !== 0
    || (int)$order['pay_type'] === 60
    || bccomp((string)$order['pay_price'], '0.01', 2) !== 0
    || bccomp((string)$order['cash'], '0.01', 2) !== 0
    || (int)$targetUserRows[0]['ai_user_id'] !== $aiUserId
    || strpos((string)$targetUserRows[0]['account'], 'CDX') !== 0
    || strpos((string)$actor['account'], 'CDX') !== 0
    || (int)$actor['type'] !== 0 || (string)$actor['uuid'] === '') {
    throw new RuntimeException('Only owned settled local-wallet order and synthetic superadmin allowed');
}
$snapshot = static function (string $refundNo = '') use ($all, $store, $ai, $table,
    $orderId, $prepayId, $aiUserId, $order): array {
    $wallet = $all($ai, 'SELECT BALANCE,SUBSIDY_BALANCE,GIFT_BALANCE FROM '
        . $table('user', true) . ' WHERE USER_ID=?', [$aiUserId]);
    $meal = $all($store, 'SELECT PAY_STATUS,ORDER_STATUS,REFUND_STATUS,REFUND_AMOUNT,'
        . 'CASH_REFUND_AMOUNT,SUBSIDY_REFUND_AMOUNT,GIFT_REFUND_AMOUNT FROM '
        . $table('meal_order') . ' WHERE ID=?', [$orderId]);
    $prepayCurrent = $all($store, 'SELECT PREPAY_STATUS FROM '
        . $table('prepay_order') . ' WHERE ID=?', [$prepayId]);
    $refunds = $all($store, 'SELECT ID,MEAL_ORDER_ID,ORDER_NO,REFUND_AMOUNT,REFUND_STATUS,'
        . 'CASH_REFUND_AMOUNT,SUBSIDY_REFUND_AMOUNT,GIFT_REFUND_AMOUNT,'
        . 'ACCOUNT_SETTLEMENT_ID FROM ' . $table('meal_order_refund')
        . ' WHERE MEAL_ORDER_ID=? ORDER BY ID', [$orderId]);
    $consume = $all($ai, 'SELECT SETTLEMENT_ID,CASH_AMOUNT,SUBSIDY_AMOUNT,GIFT_AMOUNT,'
        . 'AMOUNT,STATUS,BIZ_TYPE FROM ' . $table('account_settlement', true)
        . ' WHERE BIZ_ORDER_NO=? AND BIZ_TYPE=?', [(string)$order['order_no'], 'consume']);
    // Refund cash log references the refund ID, while consume log references meal ID.
    $refundIdForLogs = count($refunds) === 1 ? (int)$refunds[0]['id'] : 0;
    $cashLogs = $all($ai, 'SELECT LOG_ID,ORDER_ID,MONEY FROM '
        . $table('user_balance_log', true)
        . ' WHERE USER_ID=? AND STORE_ID=? AND ORDER_ID IN (?,?) ORDER BY LOG_ID',
        [$aiUserId, 10001, $orderId, $refundIdForLogs]);
    $refundSettlement = $refundNo === '' ? [] : $all($ai,
        'SELECT SETTLEMENT_ID,CASH_AMOUNT,SUBSIDY_AMOUNT,GIFT_AMOUNT,AMOUNT,STATUS,BIZ_TYPE'
        . ' FROM ' . $table('account_settlement', true)
        . ' WHERE REFUND_NO=? AND BIZ_TYPE=?', [$refundNo, 'refund']);
    return compact('wallet', 'meal', 'prepayCurrent', 'refunds', 'consume',
        'cashLogs', 'refundSettlement');
};
$before = $snapshot();
if (count($before['wallet']) !== 1 || count($before['meal']) !== 1
    || count($before['prepayCurrent']) !== 1 || count($before['refunds']) !== 0
    || count($before['consume']) !== 1 || count($before['cashLogs']) !== 1
    || bccomp((string)$before['wallet'][0]['balance'], '0.04', 2) !== 0) {
    throw new RuntimeException('Owned pre-refund balance or ledger drifted');
}
if (getenv('POC_DRY_RUN') === '1') {
    echo json_encode(['lane' => $lane, 'order_id' => $orderId,
        'cash_before' => (string)$before['wallet'][0]['balance'],
        'refund_rows_before' => 0, 'consume_settlements' => 1,
        'cash_logs_before' => 1, 'HTTP' => 0, 'DML' => 0]) . PHP_EOL;
    exit(0);
}
$attemptPath = $root . '/refund10001-attempt.json';
if (is_file($attemptPath)) throw new RuntimeException('Owned refund already attempted');
$stubFile = $root . '/runtime/poc-refund-stub-hits.log';
$stubBefore = is_file($stubFile) ? count(file($stubFile, FILE_IGNORE_NEW_LINES)) : 0;
require_once $root . '/application/base/Security.php';
$token = \app\base\Security::encrypt(implode("\t", [
    (string)$actor['id'], (string)$actor['uuid'], (string)$actor['name'],
    '0', '', (string)$actor['staff_uuid'], (string)$actor['name'],
]));
$attempt = ['lane' => $lane, 'marker' => $marker,
    'order_id' => $orderId, 'prepay_id' => $prepayId,
    'actor_id' => $actorId, 'refund_amount' => '0.01',
    'token_sha256' => hash('sha256', $token),
    'before_wallet_cash' => (string)$before['wallet'][0]['balance'],
    'before_refunds' => 0, 'before_cash_logs' => 1,
    'before_stub_hits' => $stubBefore,
    'source_sha256' => hash_file('sha256', $root . '/application/p/logic/MealOrder.php'),
    'at' => date('c')];
$handle = fopen($attemptPath, 'xb');
if (!$handle) throw new RuntimeException('Could not lock own refund attempt');
fwrite($handle, json_encode($attempt, JSON_UNESCAPED_SLASHES));
fclose($handle);
chmod($attemptPath, 0600);
$base = 'http://127.0.0.1:' . ($lane === 'B' ? 19131 : 19132);
$post = static function (string $path, array $form, string $rawPath)
    use ($base, $token): array {
    if (is_file($rawPath)) throw new RuntimeException('This refund wire already sent');
    $body = http_build_query($form, '', '&', PHP_QUERY_RFC3986);
    $context = stream_context_create(['http' => [
        'method' => 'POST', 'ignore_errors' => true, 'timeout' => 45,
        'header' => "Content-Type: application/x-www-form-urlencoded\r\n"
            . 'token: ' . $token . "\r\n",
        'content' => $body,
    ]]);
    $wire = file_get_contents($base . $path, false, $context);
    $raw = is_string($wire) ? $wire : '';
    $handle = fopen($rawPath, 'xb');
    if (!$handle) throw new RuntimeException('Cannot preserve refund raw response');
    fwrite($handle, $raw);
    fclose($handle);
    chmod($rawPath, 0600);
    $http = 0;
    foreach (($http_response_header ?? []) as $header) {
        if (preg_match('/^HTTP\/\S+ (\d{3})/', $header, $match)) $http = (int)$match[1];
    }
    $json = json_decode($raw, true);
    return ['http' => $http, 'json' => $json,
        'raw_sha256' => hash('sha256', $raw), 'raw_file' => basename($rawPath)];
};
$apply = $post('/p/mealOrder/refund', [
    'order_id' => (string)$orderId,
    'refund_amount' => '0.01',
    'apply_desc' => $marker,
], $root . '/refund10001-apply.raw');
$applied = $snapshot();
$refund = count($applied['refunds']) === 1 ? $applied['refunds'][0] : [];
$refundId = (int)($refund['id'] ?? 0);
if ($apply['http'] !== 200 || !is_array($apply['json'])
    || (int)($apply['json']['code'] ?? -1) !== 0
    || $refundId <= 0
    || (int)($apply['json']['data']['refund_id'] ?? 0) !== $refundId
    || (int)$refund['refund_status'] !== 1
    || (int)$applied['meal'][0]['refund_status'] !== 1
    || $before['wallet'] !== $applied['wallet']
    || $before['cashLogs'] !== $applied['cashLogs']
    || $before['consume'] !== $applied['consume']) {
    $partial = ['lane' => $lane, 'phase' => 'apply', 'http' => $apply['http'],
        'code' => $apply['json']['code'] ?? null,
        'refund_rows' => count($applied['refunds']),
        'refund_id' => $refundId,
        'wallet_unchanged' => $before['wallet'] === $applied['wallet']];
    file_put_contents($root . '/refund10001-partial.json', json_encode($partial));
    throw new RuntimeException('Refund apply did not pass; approval not sent');
}
$refundNo = (string)$refund['order_no'];
$agree = $post('/p/mealOrder/refundAgree', ['refund_id' => (string)$refundId],
    $root . '/refund10001-agree.raw');
$agreed = $snapshot($refundNo);
$stubAfter = is_file($stubFile) ? count(file($stubFile, FILE_IGNORE_NEW_LINES)) : 0;
$refundAfter = count($agreed['refunds']) === 1 ? $agreed['refunds'][0] : [];
$refundSettlement = count($agreed['refundSettlement']) === 1
    ? $agreed['refundSettlement'][0] : [];
$receipt = [
    'lane' => $lane, 'marker' => $marker,
    'order_id' => $orderId, 'refund_id' => $refundId,
    'apply_http' => $apply['http'], 'apply_code' => $apply['json']['code'] ?? null,
    'apply_response_sha256' => $apply['raw_sha256'],
    'apply_raw_file' => $apply['raw_file'],
    'agree_http' => $agree['http'], 'agree_code' => $agree['json']['code'] ?? null,
    'agree_response_sha256' => $agree['raw_sha256'],
    'agree_raw_file' => $agree['raw_file'],
    'wallet_cash_before' => (string)$before['wallet'][0]['balance'],
    'wallet_cash_after_apply' => (string)$applied['wallet'][0]['balance'],
    'wallet_cash_after_agree' => (string)($agreed['wallet'][0]['balance'] ?? ''),
    'cash_logs_before' => count($before['cashLogs']),
    'cash_logs_after_apply' => count($applied['cashLogs']),
    'cash_logs_after_agree' => count($agreed['cashLogs']),
    'consume_settlement_unchanged' => $before['consume'] === $agreed['consume'],
    'refund_rows_before' => count($before['refunds']),
    'refund_rows_after_apply' => count($applied['refunds']),
    'refund_rows_after_agree' => count($agreed['refunds']),
    'refund_status_after_agree' => (int)($refundAfter['refund_status'] ?? -1),
    'meal_refund_status_after_agree' => (int)($agreed['meal'][0]['refund_status'] ?? -1),
    'meal_refund_amount_after_agree' => (string)($agreed['meal'][0]['refund_amount'] ?? ''),
    'meal_cash_refund_amount_after_agree' => (string)($agreed['meal'][0]['cash_refund_amount'] ?? ''),
    'refund_settlement_rows' => count($agreed['refundSettlement']),
    'refund_settlement_cash' => (string)($refundSettlement['cash_amount'] ?? ''),
    'refund_settlement_status' => (int)($refundSettlement['status'] ?? -1),
    'stub_refund_hits_delta' => $stubAfter - $stubBefore,
];
$receipt['passed'] = $receipt['apply_http'] === 200
    && (int)$receipt['apply_code'] === 0
    && $receipt['agree_http'] === 200
    && (int)$receipt['agree_code'] === 0
    && bccomp($receipt['wallet_cash_before'], '0.04', 2) === 0
    && bccomp($receipt['wallet_cash_after_apply'], '0.04', 2) === 0
    && bccomp($receipt['wallet_cash_after_agree'], '0.05', 2) === 0
    && $receipt['cash_logs_before'] === 1
    && $receipt['cash_logs_after_apply'] === 1
    && $receipt['cash_logs_after_agree'] === 2
    && $receipt['consume_settlement_unchanged']
    && $receipt['refund_rows_before'] === 0
    && $receipt['refund_rows_after_apply'] === 1
    && $receipt['refund_rows_after_agree'] === 1
    && $receipt['refund_status_after_agree'] === 2
    && $receipt['meal_refund_status_after_agree'] === 3
    && bccomp($receipt['meal_refund_amount_after_agree'], '0.01', 2) === 0
    && bccomp($receipt['meal_cash_refund_amount_after_agree'], '0.01', 2) === 0
    && $receipt['refund_settlement_rows'] === 1
    && bccomp($receipt['refund_settlement_cash'], '0.01', 2) === 0
    && $receipt['refund_settlement_status'] === 20
    && $receipt['stub_refund_hits_delta'] === 1;
file_put_contents($root . '/refund10001-receipt.json', json_encode($receipt,
    JSON_PRETTY_PRINT | JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES), LOCK_EX);
chmod($root . '/refund10001-receipt.json', 0600);
echo json_encode($receipt, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
exit($receipt['passed'] ? 0 : 1);
