<?php

declare(strict_types=1);

$root = getenv('POC_TARGET_ROOT');
$dialect = getenv('POC_DIALECT');
$baseUrl = rtrim((string)getenv('POC_BASE_URL'), '/');
if (!$root || !$baseUrl || !in_array($dialect, ['mysql', 'dm'], true)) exit(2);
foreach (['HOST', 'PORT', 'SCHEMA', 'USER', 'PASSWORD',
    'AI_HOST', 'AI_PORT', 'AI_SCHEMA', 'AI_USER', 'AI_PASSWORD'] as $part) {
    if (!getenv('POC_' . $part)) exit(2);
}
define('TP_ENV', 'poc');
define('BUSINESS', 'aizhct');
define('ENTERPRISE_NO', '10001');
require $root . '/vendor/autoload.php';
(new \think\App($root))->setEnvName('poc')->initialize();

$host = getenv('POC_AI_HOST');
$port = getenv('POC_AI_PORT');
$schema = getenv('POC_AI_SCHEMA');
$dsn = $dialect === 'dm'
    ? "dm:host=$host;port=$port;schema=$schema"
    : "mysql:host=$host;port=$port;dbname=$schema;charset=utf8mb4";
$pdo = new PDO($dsn, getenv('POC_AI_USER'), getenv('POC_AI_PASSWORD'),
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$storeHost = getenv('POC_HOST');
$storePort = getenv('POC_PORT');
$storeSchema = getenv('POC_SCHEMA');
$storeDsn = $dialect === 'dm'
    ? "dm:host=$storeHost;port=$storePort;schema=$storeSchema"
    : "mysql:host=$storeHost;port=$storePort;dbname=$storeSchema;charset=utf8mb4";
$storePdo = new PDO($storeDsn, getenv('POC_USER'), getenv('POC_PASSWORD'),
    [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
$prefix = $dialect === 'dm' ? 'YOSHOP_' : 'yoshop_';
$userTable = $prefix . 'user';
$restaurantTable = $dialect === 'dm' ? 'YDY_RESTAURANTS' : 'ydy_restaurants';
$dishTable = $prefix . 'dishes';
$storeDishTable = $dialect === 'dm' ? 'YDY_DISHES' : 'ydy_dishes';
$cartTable = $prefix . 'meal_cart';
if ($dialect === 'dm') {
    $userTable = strtoupper($userTable);
    $dishTable = strtoupper($dishTable);
    $cartTable = strtoupper($cartTable);
}
$limit = $dialect === 'dm' ? 'FETCH FIRST 1 ROW ONLY' : 'LIMIT 1';
$restaurantId = 0;
$restaurants = $storePdo->query("SELECT ID,BOOK_RULES FROM $restaurantTable"
    . ' WHERE STALL_TYPE=2 AND IS_DELETE=2 AND STATUS=1 ORDER BY ID');
foreach ($restaurants->fetchAll(PDO::FETCH_ASSOC) as $restaurant) {
    $restaurant = array_change_key_case($restaurant);
    $rules = json_decode((string)$restaurant['book_rules'], true);
    if ((int)($rules['rule'] ?? 0) !== 3) {
        $restaurantId = (int)$restaurant['id'];
        break;
    }
}
$dishId = 0;
$mapped = $storePdo->query("SELECT DISTINCT AI_DISHES_ID FROM $storeDishTable"
    . " WHERE IS_DELETE=0 AND AI_DISHES_ID>0 ORDER BY AI_DISHES_ID"
    . ($dialect === 'dm' ? ' FETCH FIRST 50 ROWS ONLY' : ' LIMIT 50'));
$aiDishCheck = $pdo->prepare("SELECT COUNT(*) FROM $dishTable WHERE ID=? AND IS_DELETE=0");
foreach ($mapped->fetchAll(PDO::FETCH_COLUMN) as $candidate) {
    $aiDishCheck->execute([(int)$candidate]);
    if ((int)$aiDishCheck->fetchColumn() === 1) {
        $dishId = (int)$candidate;
        break;
    }
}
if ($restaurantId <= 0 || $dishId <= 0) {
    throw new RuntimeException('Cart restaurant/dish fixture missing');
}
$mealDate = '2099-01-01';
$mealTimes = 1;
$marker = 'CDXMEALCART' . strtoupper(bin2hex(random_bytes(5)));
$token = 'CDXMEALCART-TOKEN-' . strtoupper(bin2hex(random_bytes(12)));
$userId = 0;
$otherMarker = 'CDXMEALCART' . strtoupper(bin2hex(random_bytes(5)));
$otherToken = 'CDXMEALCART-TOKEN-' . strtoupper(bin2hex(random_bytes(12)));
$otherUserId = 0;
$allowLegacyTimestamp = getenv('POC_ALLOW_LEGACY_TIMESTAMP') === '1';
$expectFixedForm = getenv('POC_EXPECT_FIXED_FORM') === '1';
$request = static function (string $path, ?array $post, ?string $auth,
    bool $json = false) use ($baseUrl): array {
    $headers = "platform: POC\r\nContent-Type: "
        . ($json ? 'application/json' : 'application/x-www-form-urlencoded') . "\r\n";
    if ($auth !== null) $headers .= 'Access-Token: ' . $auth . "\r\n";
    $options = ['method' => $post === null ? 'GET' : 'POST', 'header' => $headers,
        'timeout' => 15, 'ignore_errors' => true];
    if ($post !== null) $options['content'] = $json
        ? json_encode($post, JSON_UNESCAPED_UNICODE) : http_build_query($post);
    $body = file_get_contents($baseUrl . $path, false,
        stream_context_create(['http' => $options]));
    $decoded = is_string($body) ? json_decode($body, true) : null;
    if (!is_array($decoded)) {
        throw new RuntimeException('Cart HTTP response not JSON: '
            . (string)($http_response_header[0] ?? 'unknown'));
    }
    unset($decoded['trace_id']);
    return $decoded;
};
$cartRows = static function () use ($pdo, $cartTable, &$userId): array {
    $read = $pdo->prepare('SELECT ID,DISHES_ID,DISHES_NUM,RESTAURANT_ID,MEAL_DATE,'
        . 'MEAL_TIMES,STORE_ID,IS_DELETE,UPDATE_TIME FROM ' . $cartTable
        . ' WHERE USER_ID=? ORDER BY ID');
    $read->execute([$userId]);
    return array_map('array_change_key_case', $read->fetchAll(PDO::FETCH_ASSOC));
};
try {
    $insert = $pdo->prepare('INSERT INTO ' . $userTable
        . ' (STORE_ID,MOBILE,NICK_NAME,BALANCE,GIFT_BALANCE,IS_DELETE,CREATE_TIME)'
        . ' VALUES (10001,?,?,?,?,0,?)');
    $insert->execute(['', $marker, '0.00', '0.00', time()]);
    $userId = (int)$pdo->lastInsertId();
    if ($userId <= 0 || $cartRows() !== []) throw new RuntimeException('Cart precondition failed');
    $insert->execute(['', $otherMarker, '0.00', '0.00', time()]);
    $otherUserId = (int)$pdo->lastInsertId();
    if ($otherUserId <= 0 || $otherUserId === $userId) {
        throw new RuntimeException('Cart second synthetic user insert failed');
    }
    \think\facade\Cache::set($token, [
        'user' => ['user_id' => $userId], 'password_version' => 0,
        'login_type' => 'thirdparty',
    ], 300);
    \think\facade\Cache::set($otherToken, [
        'user' => ['user_id' => $otherUserId], 'password_version' => 0,
        'login_type' => 'thirdparty',
    ], 300);
    $basePath = '/api/mealCart';
    $args = [
        'restaurant_id' => $restaurantId, 'meal_date' => $mealDate,
        'meal_times' => $mealTimes, 'ai_dishes_id' => $dishId,
        'quantity' => 2,
    ];
    $unauth = $request($basePath . '/add?storeId=10001', $args, null, true);
    if ((int)($unauth['status'] ?? 0) !== 401 || $cartRows() !== []) {
        throw new RuntimeException('Cart unauthenticated request mismatch');
    }
    $invalid = $request($basePath . '/add?storeId=10001',
        array_replace($args, ['quantity' => 'not-integer']), $token, true);
    if ((int)($invalid['status'] ?? 200) === 200 || $cartRows() !== []) {
        throw new RuntimeException('Cart invalid quantity accepted');
    }
    $first = $request($basePath . '/add?storeId=10001', $args, $token, true);
    $rows = $cartRows();
    if ((int)($first['status'] ?? 0) !== 200 || count($rows) !== 1
        || (int)$rows[0]['dishes_id'] !== $dishId
        || (int)$rows[0]['dishes_num'] !== 2
        || (int)$rows[0]['restaurant_id'] !== $restaurantId
        || (int)$rows[0]['meal_times'] !== $mealTimes
        || (int)$rows[0]['store_id'] !== 10001
        || (int)$rows[0]['is_delete'] !== 0) {
        throw new RuntimeException('Cart first add/database readback mismatch: '
            . json_encode(['response_status' => $first['status'] ?? null,
                'response_message' => $first['message'] ?? null,
                'restaurant' => $restaurantId, 'dish' => $dishId,
                'rows' => $rows], JSON_UNESCAPED_UNICODE));
    }
    $cartId = (int)$rows[0]['id'];
    $otherList = $request($basePath . '/list?storeId=10001&restaurant_id=' . $restaurantId
        . '&meal_date=' . $mealDate . '&meal_times=' . $mealTimes, null, $otherToken);
    if ((int)($otherList['status'] ?? 0) !== 200
        || !empty($otherList['data']['list'])) {
        throw new RuntimeException('Cart cross-user list leaked rows');
    }
    $otherClear = $request($basePath . '/clear?storeId=10001', [
        'restaurant_id' => $restaurantId, 'meal_date' => $mealDate,
        'meal_times' => $mealTimes, 'cartIds' => [$cartId],
    ], $otherToken);
    $rows = $cartRows();
    if ((int)($otherClear['status'] ?? 0) !== 200 || count($rows) !== 1
        || (int)$rows[0]['is_delete'] !== 0) {
        throw new RuntimeException('Cart cross-user clear changed first user row');
    }
    $update = $request($basePath . '/add?storeId=10001',
        array_replace($args, ['quantity' => 3]), $token, true);
    $rows = $cartRows();
    if ((int)($update['status'] ?? 0) !== 200 || count($rows) !== 1
        || (int)$rows[0]['id'] !== $cartId
        || (int)$rows[0]['dishes_num'] !== 3
        || (!$allowLegacyTimestamp && (int)$rows[0]['update_time'] < 1000000000)) {
        throw new RuntimeException('Cart quantity update/database readback mismatch: '
            . json_encode(['response_status' => $update['status'] ?? null,
                'response_message' => $update['message'] ?? null,
                'rows' => $rows, 'expected_cart_id' => $cartId], JSON_UNESCAPED_UNICODE));
    }
    $legacyTimestampCorruption = (int)$rows[0]['update_time'] < 1000000000;
    $list = $request($basePath . '/list?storeId=10001&restaurant_id=' . $restaurantId
        . '&meal_date=' . $mealDate . '&meal_times=' . $mealTimes, null, $token);
    $listRows = (array)($list['data']['list'] ?? []);
    if ((int)($list['status'] ?? 0) !== 200 || count($listRows) !== 1
        || (int)($listRows[0]['cart_id'] ?? 0) !== $cartId
        || (int)($listRows[0]['quantity'] ?? 0) !== 3) {
        throw new RuntimeException('Cart list/database response mismatch: '
            . json_encode(['status' => $list['status'] ?? null,
                'message' => $list['message'] ?? null,
                'data_keys' => array_keys((array)($list['data'] ?? [])),
                'list_count' => count($listRows),
                'first_cart_id' => $listRows[0]['cart_id'] ?? null,
                'first_quantity' => $listRows[0]['quantity'] ?? null,
                'expected_cart_id' => $cartId], JSON_UNESCAPED_UNICODE));
    }
    $checkout = $request('/api/mealCheckout/order?storeId=10001&restaurant_id='
        . $restaurantId . '&meal_date=' . $mealDate . '&meal_times=' . $mealTimes,
        null, $token);
    if ((int)($checkout['status'] ?? 0) !== 200
        || (int)($checkout['data']['order']['restaurant_id'] ?? 0) !== $restaurantId
        || (int)($checkout['data']['order']['user_id'] ?? 0) !== $userId
        || count((array)($checkout['data']['detail'] ?? [])) !== 1
        || (int)($checkout['data']['detail'][0]['cart_id'] ?? 0) !== $cartId
        || (int)($checkout['data']['detail'][0]['quantity'] ?? 0) !== 3) {
        throw new RuntimeException('Meal checkout order/readback mismatch: '
            . json_encode(['status' => $checkout['status'] ?? null,
                'message' => $checkout['message'] ?? null,
                'order_keys' => array_keys((array)($checkout['data']['order'] ?? [])),
                'detail_count' => count((array)($checkout['data']['detail'] ?? []))],
                JSON_UNESCAPED_UNICODE));
    }
    $clear = $request($basePath . '/clear?storeId=10001', [
        'restaurant_id' => $restaurantId,
        'meal_date' => $mealDate, 'meal_times' => $mealTimes,
        'cartIds' => [$cartId],
    ], $token);
    $rows = $cartRows();
    if ((int)($clear['status'] ?? 0) !== 200 || count($rows) !== 1
        || (int)$rows[0]['is_delete'] !== 1
        || (!$allowLegacyTimestamp && (int)$rows[0]['update_time'] < 1000000000)) {
        throw new RuntimeException('Cart clear/soft-delete readback mismatch');
    }
    $empty = $request($basePath . '/list?storeId=10001&restaurant_id=' . $restaurantId
        . '&meal_date=' . $mealDate . '&meal_times=' . $mealTimes, null, $token);
    if ((int)($empty['status'] ?? 0) !== 200
        || !empty($empty['data']['list'])) {
        throw new RuntimeException('Cart list after clear was not empty');
    }
    $reAdd = $request($basePath . '/add?storeId=10001',
        array_replace($args, ['quantity' => 1]), $token, true);
    $beforeZero = $cartRows();
    if ((int)($reAdd['status'] ?? 0) !== 200 || count($beforeZero) !== 2
        || (int)$beforeZero[1]['is_delete'] !== 0
        || (int)$beforeZero[1]['dishes_num'] !== 1) {
        throw new RuntimeException('Cart re-add after clear mismatch');
    }
    $zero = $request($basePath . '/add?storeId=10001',
        array_replace($args, ['quantity' => 0]), $token, true);
    $afterZero = $cartRows();
    if ((int)($zero['status'] ?? 0) !== 200 || count($afterZero) !== 2
        || (int)$afterZero[1]['is_delete'] !== 1
        || (!$allowLegacyTimestamp && (int)$afterZero[1]['update_time'] < 1000000000)) {
        throw new RuntimeException('Cart zero-quantity deletion mismatch');
    }
    $formEncoded = $request($basePath . '/add?storeId=10001', $args, $otherToken);
    $otherCartRead = $pdo->prepare('SELECT DISHES_ID,DISHES_NUM,IS_DELETE,UPDATE_TIME FROM '
        . $cartTable . ' WHERE USER_ID=? ORDER BY ID');
    $otherCartRead->execute([$otherUserId]);
    $otherCartRows = array_map('array_change_key_case',
        $otherCartRead->fetchAll(PDO::FETCH_ASSOC));
    if ($expectFixedForm) {
        if ((int)($formEncoded['status'] ?? 0) !== 200
            || count($otherCartRows) !== 1
            || (int)$otherCartRows[0]['dishes_id'] !== $dishId
            || (int)$otherCartRows[0]['dishes_num'] !== 2
            || (int)$otherCartRows[0]['is_delete'] !== 0) {
            throw new RuntimeException('Cart form-encoded insert/readback failed');
        }
        $formUpdate = $request($basePath . '/add?storeId=10001',
            array_replace($args, ['quantity' => 3]), $otherToken);
        $otherCartRead->execute([$otherUserId]);
        $otherCartRows = array_map('array_change_key_case',
            $otherCartRead->fetchAll(PDO::FETCH_ASSOC));
        if ((int)($formUpdate['status'] ?? 0) !== 200
            || count($otherCartRows) !== 1
            || (int)$otherCartRows[0]['dishes_num'] !== 3
            || (int)$otherCartRows[0]['update_time'] < 1000000000) {
            throw new RuntimeException('Cart form-encoded update/readback failed');
        }
    } elseif ((int)($formEncoded['status'] ?? 0) !== 500
        || $otherCartRows !== []) {
        throw new RuntimeException('Cart baseline form-encoded input contract changed');
    }
    echo json_encode(['dialect' => $dialect, 'status' => 'passed',
        'unauth' => $unauth, 'invalid' => $invalid,
        'form_encoded_status' => $formEncoded['status'],
        'form_encoded_update_status' => $formUpdate['status'] ?? null,
        'first' => $first, 'update' => $update,
        'list_quantity' => 3, 'clear' => $clear, 'empty' => $empty,
        'checkout_order_status' => $checkout['status'],
        're_add' => $reAdd, 'zero_delete' => $zero,
        'cross_user_list_empty' => true, 'cross_user_clear_preserved_first_row' => true,
        'legacy_timestamp_corruption' => $legacyTimestampCorruption,
        'physical_rows_before_cleanup' => 2], JSON_UNESCAPED_UNICODE) . PHP_EOL;
} finally {
    \think\facade\Cache::delete($token);
    \think\facade\Cache::delete($otherToken);
    if ($userId > 0) {
        $pdo->prepare('DELETE FROM ' . $cartTable . ' WHERE USER_ID=?')->execute([$userId]);
        $pdo->prepare('DELETE FROM ' . $userTable . ' WHERE USER_ID=? AND NICK_NAME=?')
            ->execute([$userId, $marker]);
        $userCheck = $pdo->prepare('SELECT COUNT(*) FROM ' . $userTable . ' WHERE USER_ID=?');
        $userCheck->execute([$userId]);
        if ($cartRows() !== [] || (int)$userCheck->fetchColumn() !== 0) {
            throw new RuntimeException('Cart synthetic cleanup left rows');
        }
    }
    if ($otherUserId > 0) {
        $pdo->prepare('DELETE FROM ' . $cartTable . ' WHERE USER_ID=?')
            ->execute([$otherUserId]);
        $pdo->prepare('DELETE FROM ' . $userTable . ' WHERE USER_ID=? AND NICK_NAME=?')
            ->execute([$otherUserId, $otherMarker]);
        $otherUserCheck = $pdo->prepare('SELECT COUNT(*) FROM ' . $userTable
            . ' WHERE USER_ID=?');
        $otherUserCheck->execute([$otherUserId]);
        if ((int)$otherUserCheck->fetchColumn() !== 0) {
            throw new RuntimeException('Cart second synthetic user cleanup left rows');
        }
    }
}
