<?php

declare(strict_types=1);

$root = getenv('POC_APP_ROOT');
$dialect = getenv('POC_DIALECT');
$mainHost = getenv('POC_HOST');
$mainPort = (int)getenv('POC_PORT');
$mainSchema = getenv('POC_SCHEMA');
$mainUser = getenv('POC_USER');
$mainPassword = getenv('POC_PASSWORD');
$storeHost = getenv('POC_STORE_HOST');
$storePort = (int)getenv('POC_STORE_PORT');
$storeSchema = getenv('POC_STORE_SCHEMA');
$storeUser = getenv('POC_STORE_USER');
$storePassword = getenv('POC_STORE_PASSWORD');
if (!$root || !in_array($dialect, ['mysql', 'dm'], true) || !$mainHost || $mainPort < 1 || !$mainSchema || !$mainUser || !$mainPassword
    || !$storeHost || $storePort < 1 || !$storeSchema || !$storeUser || !$storePassword) exit(2);
require $root . '/vendor/autoload.php';
if (!function_exists('app_name')) {
    function app_name(): string { return 'api'; }
}
if (!function_exists('getStoreId')) {
    function getStoreId(): int { return (int)($GLOBALS['POC_SYNTHETIC_STORE_ID'] ?? 0); }
}
set_exception_handler(static function (\Throwable $e): void {
    fwrite(STDERR, 'AI_METRIC_PROBE_FAIL ' . get_class($e) . ': ' . substr($e->getMessage(), 0, 250) . PHP_EOL);
    exit(1);
});

$type = $dialect === 'dm' ? \app\common\database\Dm::class : 'mysql';
\think\facade\Db::setConfig([
    'default' => 'mysql',
    'connections' => [
        'mysql' => [
            'type' => $type, 'hostname' => $mainHost, 'hostport' => $mainPort,
            'database' => $mainSchema, 'username' => $mainUser, 'password' => $mainPassword,
            'prefix' => 'yoshop_', 'debug' => false,
        ],
        'mysql_zhct' => [
            'type' => $type, 'hostname' => $storeHost, 'hostport' => $storePort,
            'database' => $storeSchema, 'username' => $storeUser, 'password' => $storePassword,
            'prefix' => 'ydy_', 'debug' => false,
        ],
    ],
]);
$aiDb = \think\facade\Db::connect('mysql');
$storeDb = \think\facade\Db::connect('mysql_zhct');
$storeId = 900000000 + random_int(10000, 99999);
$GLOBALS['POC_SYNTHETIC_STORE_ID'] = $storeId;
$marker = 'CODEXDM8_AI_METRIC_' . strtoupper(bin2hex(random_bytes(4)));
if ((int)$aiDb->name('user')->where('store_id', $storeId)->count() !== 0) throw new RuntimeException('Synthetic store ID already exists');
echo json_encode(['dialect' => $dialect, 'status' => 'starting', 'store_id' => $storeId, 'marker' => $marker]) . PHP_EOL;

$aiStarted = false;
$storeStarted = false;
try {
    $aiDb->startTrans();
    $aiStarted = true;
    $storeDb->startTrans();
    $storeStarted = true;
    $userId = (int)$aiDb->name('user')->insertGetId([
        'store_id' => $storeId, 'mobile' => '', 'nick_name' => $marker,
        'balance' => '0.00', 'is_delete' => 0, 'create_time' => time(),
    ]);
    $aiDb->name('staff')->insert([
        'user_id' => $userId, 'store_id' => $storeId, 'name' => $marker,
        'gender' => 1, 'height' => '175.00', 'is_delete' => 0,
    ]);
    if ($userId <= 0) throw new RuntimeException('Synthetic AI user insert failed');

    $measuredAt = date('Y-m-d H:i:s');
    $sessionId = (int)$storeDb->name('measurement_session')->insertGetId([
        'ai_user_id' => $userId, 'event_type' => 'measurement',
        'start_time' => $measuredAt, 'source_platform' => 'direct_device',
        'vendor' => 'CODEX', 'device_type' => 'body_scale', 'device_model' => $marker,
        'is_valid' => 1,
    ]);
    if ($sessionId <= 0) throw new RuntimeException('Synthetic store session insert failed');
    $storeDb->name('measurement_claim')->insert([
        'store_id' => $storeId, 'session_id' => $sessionId,
        'vendor' => 'CODEX', 'device_no' => $marker,
        'external_user_id' => $marker, 'record_no' => $marker,
        'measure_time' => $measuredAt, 'status' => 'CLAIMED',
        'claim_source' => 'QR', 'claimed_ai_user_id' => $userId,
        'claimed_at' => $measuredAt, 'expires_at' => date('Y-m-d H:i:s', time() + 3600),
        'is_delete' => 0,
    ]);
    $records = [];
    foreach ([['weight', '70.5', 'kg'], ['bmi', '23.0', ''], ['bfp', '18.5', '%']] as $metric) {
        $records[] = [
            'session_id' => $sessionId, 'ai_user_id' => $userId,
            'metric_code' => $metric[0], 'value' => $metric[1], 'unit' => $metric[2],
            'segment_key' => '', 'start_time' => $measuredAt,
        ];
    }
    $storeDb->name('health_record')->insertAll($records);

    $service = new \app\api\service\health\MetricDeviceSync();
    $first = $service->syncSession($sessionId, $userId, $storeId);
    if ($first === 0) {
        $method = new ReflectionMethod($service, 'syncSessionRecords');
        $method->setAccessible(true);
        $diagnostic = $method->invoke($service, $sessionId, $userId, $storeId);
        throw new RuntimeException('Initial public sync returned zero; direct records result=' . $diagnostic);
    }
    $repeat = $service->syncSession($sessionId, $userId, $storeId);
    $target = $aiDb->name('user_health_metric_record')->where('user_id', $userId)->where('store_id', $storeId)->where('source_type', 2)->select()->toArray();
    if ($first !== 3 || $repeat !== 0 || count($target) !== 3) {
        throw new RuntimeException('AI metric initial write or idempotency mismatch: ' . json_encode([$first, $repeat, count($target)]));
    }
    $storeDb->name('health_record')->where('session_id', $sessionId)->where('metric_code', 'weight')->update(['value' => '71.0']);
    $updated = $service->syncSession($sessionId, $userId, $storeId);
    $weight = $aiDb->name('user_health_metric_record')->where('user_id', $userId)->where('store_id', $storeId)->where('metric_code', 'weight')->find();
    if ($updated !== 1 || number_format((float)$weight['value'], 1, '.', '') !== '71.0') {
        throw new RuntimeException('AI metric source update did not project');
    }

    $aiDb->rollback();
    $aiStarted = false;
    $storeDb->rollback();
    $storeStarted = false;
    $aiDsn = $dialect === 'dm' ? "dm:host=$mainHost;port=$mainPort;schema=$mainSchema" : "mysql:host=$mainHost;port=$mainPort;dbname=$mainSchema;charset=utf8mb4";
    $storeDsn = $dialect === 'dm' ? "dm:host=$storeHost;port=$storePort;schema=$storeSchema" : "mysql:host=$storeHost;port=$storePort;dbname=$storeSchema;charset=utf8mb4";
    $freshAi = new PDO($aiDsn, $mainUser, $mainPassword, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
    $freshStore = new PDO($storeDsn, $storeUser, $storePassword, [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION]);
    $aiTable = $dialect === 'dm' ? 'YOSHOP_USER_HEALTH_METRIC_RECORD' : 'yoshop_user_health_metric_record';
    $storeTable = $dialect === 'dm' ? 'YDY_MEASUREMENT_SESSION' : 'ydy_measurement_session';
    $checkAi = $freshAi->prepare('SELECT COUNT(*) FROM ' . $aiTable . ' WHERE USER_ID=? AND STORE_ID=?');
    $checkAi->execute([$userId, $storeId]);
    $checkStore = $freshStore->prepare('SELECT COUNT(*) FROM ' . $storeTable . ' WHERE SESSION_ID=?');
    $checkStore->execute([$sessionId]);
    if ((int)$checkAi->fetchColumn() !== 0 || (int)$checkStore->fetchColumn() !== 0) {
        throw new RuntimeException('Cross-database rollback left synthetic rows');
    }
    echo json_encode(['dialect' => $dialect, 'status' => 'passed', 'store_id' => $storeId,
        'initial_records' => $first, 'repeat_changes' => $repeat, 'source_update_changes' => $updated,
        'cross_database_rollback_clean' => true]) . PHP_EOL;
} finally {
    if ($aiStarted) $aiDb->rollback();
    if ($storeStarted) $storeDb->rollback();
}
