<?php

declare(strict_types=1);

// Run only against the isolated A/B/C HTTP runtimes and CDXPAY synthetic users.
$root = (string)getenv('POC_APP_ROOT');
$baseUrl = rtrim((string)getenv('POC_BASE_URL'), '/');
$marker = (string)getenv('POC_MARKER');
$dialect = (string)getenv('POC_DIALECT');
if (!preg_match('/^CDXPAY[A-F0-9]{8}$/D', $marker)
    || !in_array($dialect, ['mysql', 'dm'], true)
    || !is_file($root . '/application/database.php') || $baseUrl === '') {
    fwrite(STDERR, "Invalid isolated fixture parameters\n");
    exit(2);
}
$boundary = json_decode((string)file_get_contents($baseUrl . '/__poc_boundary'), true);
if (empty($boundary['external_stubs']) || empty($boundary['redis_isolated'])) {
    throw new RuntimeException('Isolated HTTP boundary unavailable');
}
$config = require $root . '/application/database.php';
$connection = static function (array $config) use ($dialect): PDO {
    $dsn = $dialect === 'dm'
        ? 'dm:host=' . $config['hostname'] . ';port=' . $config['hostport'] . ';schema=' . $config['database']
        : 'mysql:host=' . $config['hostname'] . ';port=' . $config['hostport'] . ';dbname=' . $config['database'] . ';charset=utf8mb4';
    return new PDO($dsn, $config['username'], $config['password'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
};
$store = $connection($config);
$ai = $connection($config['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;
};
$one = static function (PDO $pdo, string $sql, array $params = []): array {
    $stmt = $pdo->prepare($sql);
    $stmt->execute($params);
    return array_change_key_case($stmt->fetch(PDO::FETCH_ASSOC) ?: [], CASE_LOWER);
};
$user = $one($store, 'SELECT ID,UUID,ACCOUNT,TYPE,STAFF_UUID,AI_USER_ID FROM ' . $table('user') . ' WHERE ACCOUNT=?', [$marker]);
if (!$user || strpos((string)$user['uuid'], 'CDXUSER-') !== 0 || (string)$user['account'] !== $marker) {
    throw new RuntimeException('Only a synthetic user may be queried');
}
$order = $one($store, 'SELECT ID,ORDER_NO,PAY_PRICE,ORDER_STATUS,REFUND_STATUS,USER_ID FROM ' . $table('meal_order') . ' WHERE MSGID=?', [$marker]);
if (!$order || (int)$order['user_id'] !== (int)$user['id']) {
    throw new RuntimeException('Synthetic meal order unavailable');
}
$refund = $one($store, 'SELECT ID,ORDER_NO,REFUND_AMOUNT,REFUND_STATUS,MEAL_ORDER_ID FROM ' . $table('meal_order_refund') . ' WHERE MEAL_ORDER_ID=? ORDER BY ID DESC', [(int)$order['id']]);
if (!$refund || (int)$refund['meal_order_id'] !== (int)$order['id']) {
    throw new RuntimeException('Synthetic refund unavailable');
}
$recharge = $one($ai, 'SELECT ORDER_ID,ORDER_NO,USER_ID,PAY_PRICE,ACTUAL_MONEY,PAY_STATUS FROM ' . $table('recharge_order', true) . ' WHERE USER_ID=? ORDER BY ORDER_ID DESC', [(int)$user['ai_user_id']]);
if (!$recharge || (int)$recharge['user_id'] !== (int)$user['ai_user_id']) {
    throw new RuntimeException('Synthetic recharge unavailable');
}
require_once $root . '/application/base/Security.php';
$token = \app\base\Security::encrypt(implode("\t", [
    (string)$user['id'], (string)$user['uuid'], $marker, '0', '', '', $marker,
]));
$post = static function (string $route, array $form, bool $authenticated = true) use ($baseUrl, $token): array {
    $context = stream_context_create(['http' => [
        'method' => 'POST',
        'header' => "Content-Type: application/x-www-form-urlencoded\r\n"
            . ($authenticated ? 'token: ' . $token . "\r\n" : ''),
        'content' => http_build_query($form, '', '&', PHP_QUERY_RFC3986),
        'timeout' => 30,
        'ignore_errors' => true,
    ]]);
    $body = file_get_contents($baseUrl . $route, false, $context);
    $parsed = is_string($body) ? json_decode($body, true) : null;
    if (!is_array($parsed)) throw new RuntimeException('Non-JSON HTTP response for ' . $route);
    return $parsed;
};
$find = static function (array $response, string $field, $value): ?array {
    $rows = $response['data']['data'] ?? [];
    if (!is_array($rows)) return null;
    foreach ($rows as $row) {
        if (is_array($row) && (string)($row[$field] ?? '') === (string)$value) return $row;
    }
    return null;
};
$shape = static function (array $data): array {
    $types = [];
    foreach ($data as $key => $value) $types[$key] = gettype($value);
    ksort($types);
    return $types;
};
$originalType = (int)$user['type'];
$granted = false;
if ($originalType !== 0) {
    $stmt = $store->prepare('UPDATE ' . $table('user') . ' SET TYPE=0 WHERE ID=? AND UUID=? AND ACCOUNT=?');
    $stmt->execute([(int)$user['id'], (string)$user['uuid'], $marker]);
    if ($stmt->rowCount() !== 1) throw new RuntimeException('Synthetic admin grant failed');
    $granted = true;
}
try {
    $checks = [];
    $cases = [
        'recharge_list' => ['/p/rechargeOrder/list', ['staff_uuid' => $user['staff_uuid']], 'order_no', $recharge['order_no']],
        'meal_list' => ['/p/mealOrder/list', ['order_no' => $order['order_no']], 'order_no', $order['order_no']],
        'refund_list' => ['/p/mealOrder/refundList', ['nickname' => $marker], 'refund_order_no', $refund['order_no']],
    ];
    foreach ($cases as $name => [$path, $params, $matchKey, $matchValue]) {
        $response = $post($path, $params);
        $row = $find($response, $matchKey, $matchValue);
        if (($response['code'] ?? null) !== 0 || !is_array($row)) {
            $checks[$name] = ['status' => 'fail', 'code' => $response['code'] ?? null,
                'message' => $response['message'] ?? null, 'returned_count' => count($response['data']['data'] ?? [])];
            continue;
        }
        $expectedMoney = $name === 'recharge_list' ? (string)$recharge['pay_price']
            : ($name === 'refund_list' ? (string)$refund['refund_amount'] : (string)$order['pay_price']);
        $moneyField = $name === 'refund_list' ? 'refund_amount' : 'pay_price';
        $moneyPass = isset($row[$moneyField]) && bccomp((string)$row[$moneyField], $expectedMoney, 2) === 0;
        $checks[$name] = [
            'status' => $moneyPass ? 'pass' : 'fail',
            'code' => $response['code'],
            'message' => $response['message'] ?? null,
            'returned_count' => count($response['data']['data'] ?? []),
            'matched_key' => $matchKey,
            'matched_value' => (string)$matchValue,
            'money_field' => $moneyField,
            'money' => (string)($row[$moneyField] ?? ''),
            'db_money' => $expectedMoney,
            'field_types' => $shape($row),
            'data_field_types' => $shape($response['data']),
        ];
    }
    $detail = $post('/p/mealOrder/detail', ['orderId' => (int)$order['id']]);
    $detailRow = $detail['data'] ?? null;
    $detailPass = ($detail['code'] ?? null) === 0 && is_array($detailRow)
        && (string)($detailRow['order_no'] ?? '') === (string)$order['order_no']
        && bccomp((string)($detailRow['pay_price'] ?? '0'), (string)$order['pay_price'], 2) === 0;
    $checks['meal_detail'] = [
        'status' => $detailPass ? 'pass' : 'fail',
        'code' => $detail['code'] ?? null,
        'message' => $detail['message'] ?? null,
        'matched_order_id' => (int)$order['id'],
        'money' => (string)($detailRow['pay_price'] ?? ''),
        'db_money' => (string)$order['pay_price'],
        'field_types' => is_array($detailRow) ? $shape($detailRow) : [],
    ];
    $wallet = $one($ai, 'SELECT USER_ID,BALANCE,STORE_ID FROM ' . $table('user', true)
        . ' WHERE USER_ID=? AND IS_DELETE=0', [(int)$user['ai_user_id']]);
    $refundable = $one($ai, 'SELECT COALESCE(SUM(REFUNDABLE_MONEY),0) AS TOTAL FROM '
        . $table('recharge_order', true)
        . ' WHERE USER_ID=? AND STORE_ID=? AND PAY_STATUS=20 AND REFUNDABLE_MONEY>0 AND IS_DELETE=0',
        [(int)$user['ai_user_id'], (int)$wallet['store_id']]);
    $expectedBalance = bccomp((string)$wallet['balance'], (string)$refundable['total'], 2) < 0
        ? (string)$wallet['balance'] : (string)$refundable['total'];
    $cashProfile = $post('/p/cashRefund/staffBalance', [
        'staff_uuid' => (string)$user['staff_uuid'], 'refund_amount' => '0.01',
    ]);
    $profile = $cashProfile['data'] ?? null;
    $profilePass = ($cashProfile['code'] ?? null) === 0 && is_array($profile)
        && (int)($profile['ai_user_id'] ?? 0) === (int)$user['ai_user_id']
        && (string)($profile['staff_uuid'] ?? '') === (string)$user['staff_uuid']
        && bccomp((string)($profile['balance'] ?? '-1'), $expectedBalance, 2) === 0
        && bccomp((string)($profile['after_balance'] ?? '-1'),
            bccomp($expectedBalance, '0.01', 2) >= 0 ? bcsub($expectedBalance, '0.01', 2) : '0.00', 2) === 0;
    $checks['cash_refund_staff_balance'] = [
        'status' => $profilePass ? 'pass' : 'fail',
        'code' => $cashProfile['code'] ?? null,
        'message' => $cashProfile['message'] ?? null,
        'balance' => (string)($profile['balance'] ?? ''),
        'db_balance' => $expectedBalance,
        'after_balance' => (string)($profile['after_balance'] ?? ''),
        'field_types' => is_array($profile) ? $shape($profile) : [],
    ];
    $noAuth = $post('/p/mealOrder/refundList', ['nickname' => $marker], false);
    $checks['refund_list_no_auth'] = [
        'status' => (($noAuth['code'] ?? null) !== 0) ? 'pass' : 'fail',
        'code' => $noAuth['code'] ?? null,
        'message' => $noAuth['message'] ?? null,
    ];
    $columnNames = [
        'activity_principal_payment', 'activity_principal_refund_amount',
        'face_direct_payment', 'is_supplement', 'origin_cost_center_code',
        'settlement_order_no', 'strategy_account_snapshot', 'supplement_id',
        'supplement_no', 'withdrawable_cash_payment',
        'withdrawable_cash_refund_amount', 'expense_owner_id',
    ];
    $schemaSql = $dialect === 'dm'
        ? "SELECT COLUMN_NAME FROM USER_TAB_COLUMNS WHERE TABLE_NAME='YDY_MEAL_ORDER'"
        : "SELECT COLUMN_NAME FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=? AND TABLE_NAME='ydy_meal_order'";
    $schemaStmt = $store->prepare($schemaSql);
    $schemaStmt->execute($dialect === 'dm' ? [] : [$config['database']]);
    $availableColumns = array_map('strtolower', $schemaStmt->fetchAll(PDO::FETCH_COLUMN));
    $checks['detail_schema_columns'] = [];
    foreach ($columnNames as $columnName) {
        $checks['detail_schema_columns'][$columnName] = in_array($columnName, $availableColumns, true);
    }
    echo json_encode(['dialect' => $dialect, 'marker' => $marker,
        'order_id' => (int)$order['id'], 'refund_id' => (int)$refund['id'],
        'recharge_id' => (int)$recharge['order_id'], 'checks' => $checks],
        JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
    foreach ($checks as $name => $check) {
        if ($name !== 'detail_schema_columns' && ($check['status'] ?? '') !== 'pass') exit(1);
    }
} finally {
    if ($granted) {
        $stmt = $store->prepare('UPDATE ' . $table('user') . ' SET TYPE=? WHERE ID=? AND UUID=? AND ACCOUNT=?');
        $stmt->execute([$originalType, (int)$user['id'], (string)$user['uuid'], $marker]);
        $restored = $one($store, 'SELECT TYPE FROM ' . $table('user') . ' WHERE ID=?', [(int)$user['id']]);
        if ((int)($restored['type'] ?? -1) !== $originalType) {
            throw new RuntimeException('Synthetic admin permission not restored');
        }
    }
}
