<?php

declare(strict_types=1);

// C-only draft. No DML or business HTTP without POC_APPROVED_HABIT3_WRITE=1.
set_exception_handler(static function (Throwable $error): void {
    fwrite(STDERR, 'AI_HABIT3_TEST_FAILED ' . get_class($error) . PHP_EOL);
    exit(2);
});
$root = '/tmp/zhct-ai-http-20260929.3XDj3w';
$base = 'http://127.0.0.1:18102';
$pid = 53772;
$env = [];
foreach (explode("\0", (string)@file_get_contents('/proc/' . $pid . '/environ')) as $item) {
    $parts = explode('=', $item, 2);
    if (count($parts) === 2) $env[$parts[0]] = $parts[1];
}
if (($env['POC_TARGET_ROOT'] ?? '') !== $root
    || ($env['POC_BASE_URL'] ?? '') !== $base
    || ($env['POC_DIALECT'] ?? '') !== 'dm'
    || ($env['POC_AI_HOST'] ?? '') !== '127.0.0.1'
    || ($env['POC_AI_PORT'] ?? '') !== '5238'
    || ($env['POC_AI_SCHEMA'] ?? '') !== 'AIZHCT_APP'
    || ($env['POC_AI_USER'] ?? '') !== 'AIZHCT_APP'
    || !is_file($root . '/vendor/autoload.php')) {
    throw new RuntimeException('Unapproved C AI runtime');
}
$boundaryRaw = @file_get_contents($base . '/__poc_boundary', false,
    stream_context_create(['http' => ['timeout' => 5]]));
$boundary = is_string($boundaryRaw) ? json_decode($boundaryRaw, true) : null;
unset($boundaryRaw);
if (!is_array($boundary) || empty($boundary['external_stubs'])) {
    throw new RuntimeException('C HTTP external stubs absent');
}
$db = new PDO('dm:host=' . $env['POC_AI_HOST'] . ';port=' . $env['POC_AI_PORT']
    . ';schema=' . $env['POC_AI_SCHEMA'], $env['POC_AI_USER'],
    $env['POC_AI_PASSWORD'], [PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
        PDO::ATTR_CASE => PDO::CASE_LOWER]);
if ((string)$db->query("SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")
    ->fetchColumn() !== 'AIZHCT_APP') {
    throw new RuntimeException('C effective schema mismatch');
}
$tables = [
    'store' => ['STORE_ID'],
    'store_user' => ['STORE_USER_ID', 'USER_NAME', 'REAL_NAME', 'PASSWORD',
        'IS_SUPER', 'STORE_ID', 'IS_DELETE'],
    'store_role' => ['ROLE_ID', 'ROLE_NAME', 'STORE_ID'],
    'store_user_role' => ['ID', 'STORE_USER_ID', 'ROLE_ID', 'STORE_ID'],
    'store_role_menu' => ['ID', 'ROLE_ID', 'MENU_ID', 'STORE_ID'],
    'store_api' => ['API_ID', 'URL'],
    'store_menu_api' => ['API_ID', 'MENU_ID'],
    'staff' => ['ID', 'NAME', 'STORE_ID', 'IS_DELETE'],
    'habit' => ['ID', 'STAFF_ID', 'HABIT', 'STORE_ID', 'CREATE_BY'],
    'habit_rpe' => ['ID', 'STAFF_ID', 'TRAIN_DATE', 'STORE_ID', 'CREATE_BY'],
    'habit_rpe_info' => ['ID', 'RPE_ID', 'STORE_ID', 'CREATE_BY'],
    'config_enum' => ['ID', 'ENUM_TYPE', 'ENUM_KEY', 'IS_DELETE'],
];
$assert = static function (bool $ok, string $message): void {
    if (!$ok) throw new RuntimeException($message);
};
$count = static function (string $sql, array $args = []) use ($db): int {
    $stmt = $db->prepare($sql);
    $stmt->execute($args);
    return (int)$stmt->fetchColumn();
};
$rows = static function (string $sql, array $args = []) use ($db): array {
    $stmt = $db->prepare($sql);
    $stmt->execute($args);
    return $stmt->fetchAll(PDO::FETCH_ASSOC);
};
foreach ($tables as $short => $needed) {
    $name = 'YOSHOP_' . strtoupper($short);
    $assert($count('SELECT COUNT(*) FROM USER_TABLES WHERE TABLE_NAME=?', [$name]) === 1,
        'Habit3 required table absent');
    $stmt = $db->prepare('SELECT COLUMN_NAME FROM USER_TAB_COLUMNS WHERE TABLE_NAME=?');
    $stmt->execute([$name]);
    $actual = array_map('strtoupper', $stmt->fetchAll(PDO::FETCH_COLUMN));
    $assert(count(array_diff($needed, $actual)) === 0,
        'Habit3 required columns absent');
}
$assert($count('SELECT COUNT(*) FROM YOSHOP_STORE WHERE STORE_ID=?', [10001]) === 1,
    'Approved store 10001 missing/ambiguous');
$assert($count('SELECT COUNT(*) FROM YOSHOP_CONFIG_ENUM'
    . ' WHERE ENUM_TYPE=? AND ENUM_KEY=? AND IS_DELETE=0', ['2', '2']) === 1,
    'Synthetic habit enum fixture absent');
$urls = ['/store.habit/detail', '/store.habit/dateRpe',
    '/store.habit/detailRpe'];
$menuSql = 'SELECT m.MENU_ID,COUNT(DISTINCT m.API_ID) API_COUNT'
    . ' FROM YOSHOP_STORE_MENU_API m WHERE m.MENU_ID IN ('
    . ' SELECT m2.MENU_ID FROM YOSHOP_STORE_MENU_API m2'
    . ' JOIN YOSHOP_STORE_API a2 ON a2.API_ID=m2.API_ID'
    . ' WHERE a2.URL IN (?,?,?) GROUP BY m2.MENU_ID'
    . ' HAVING COUNT(DISTINCT a2.URL)=3) GROUP BY m.MENU_ID'
    . ' ORDER BY API_COUNT ASC,m.MENU_ID ASC';
$menus = $rows($menuSql, $urls);
$assert(count($menus) === 2 && (int)$menus[0]['api_count'] === 7,
    'Expected narrow existing Habit ACL menu changed');
$menuId = (int)$menus[0]['menu_id'];
$assert($menuId > 0, 'Habit ACL menu id invalid');
$marker = 'CDXPARNUH' . strtoupper(bin2hex(random_bytes(4)));
$assert($count('SELECT COUNT(*) FROM YOSHOP_STORE_USER WHERE USER_NAME=?', [$marker]) === 0
    && $count('SELECT COUNT(*) FROM YOSHOP_STORE_ROLE WHERE ROLE_NAME=?', [$marker]) === 0
    && $count('SELECT COUNT(*) FROM YOSHOP_STAFF WHERE NAME=?', [$marker]) === 0,
    'Habit3 synthetic marker already exists');
if ((string)getenv('POC_APPROVED_HABIT3_WRITE') !== '1') {
    echo json_encode(['suite' => 'ai-store-habit3-C',
        'mode' => 'read-only-preflight', 'required_tables_and_columns' => true,
        'store_10001' => true, 'habit_enum_fixture' => true,
        'common_menu_count' => count($menus),
        'selected_menu_api_count' => 7,
        'fresh_marker_zero' => true, 'external_stubs' => true],
        JSON_UNESCAPED_SLASHES) . PHP_EOL;
    exit(0);
}

define('TP_ENV', 'poc');
define('BUSINESS', 'aizhct');
define('ENTERPRISE_NO', '10001');
require $root . '/vendor/autoload.php';
(new \think\App($root))->setEnvName('poc')->initialize();
$token = $marker . 'TOKEN' . strtoupper(bin2hex(random_bytes(8)));
$operatorId = 0;
$roleId = 0;
$staffId = 0;
$rpeId = 0;
$trainDate = date('Y-m-d');
$unsettledRequest = false;
$results = [];
$cleanup = [];
$failure = '';
$request = static function (string $path, array $data, string $token) use ($base): array {
    $context = stream_context_create(['http' => [
        'method' => 'POST',
        'header' => "Content-Type: application/json;charset=utf-8\r\n"
            . 'Access-Token: ' . $token . "\r\n",
        'content' => json_encode($data, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES),
        'timeout' => 30, 'ignore_errors' => true,
    ]]);
    $started = microtime(true);
    // ai_store axios baseURL ends in index.php?s=/store and appends this relative API path.
    $raw = @file_get_contents($base . '/index.php?s=/store/' . $path, false, $context);
    preg_match('/HTTP\/\S+\s+(\d+)/', $http_response_header[0] ?? '', $match);
    $body = is_string($raw) ? json_decode($raw, true) : null;
    unset($raw);
    return ['http' => isset($match[1]) ? (int)$match[1] : 0,
        'complete' => is_array($body),
        'status' => is_array($body) ? (int)($body['status'] ?? -1) : -1,
        'message' => is_array($body) ? (string)($body['message'] ?? '') : '',
        'data' => is_array($body) ? ($body['data'] ?? null) : null,
        'elapsed_ms' => (int)round((microtime(true) - $started) * 1000)];
};
try {
    $disabledPassword = '!disabled-' . strtoupper(bin2hex(random_bytes(12)));
    $db->prepare('INSERT INTO YOSHOP_STORE_USER'
        . ' (USER_NAME,PASSWORD,REAL_NAME,IS_SUPER,IS_DELETE,STORE_ID,CREATE_BY,CREATE_TIME,UPDATE_TIME)'
        . ' VALUES (?,?,?,0,0,10001,?,?,?)')
        ->execute([$marker, $disabledPassword, $marker, $marker, time(), time()]);
    $operatorRows = $rows('SELECT STORE_USER_ID FROM YOSHOP_STORE_USER'
        . ' WHERE USER_NAME=? AND STORE_ID=10001', [$marker]);
    $assert(count($operatorRows) === 1, 'Habit3 synthetic operator ambiguous');
    $operatorId = (int)$operatorRows[0]['store_user_id'];
    $assert($operatorId > 0, 'Habit3 synthetic operator ID invalid');
    \think\facade\Cache::set($token, [
        'user' => ['store_user_id' => $operatorId, 'store_id' => 10001,
            'user_name' => $marker, 'real_name' => $marker, 'is_super' => 0],
        'store_id' => 10001, 'is_login' => true,
    ], 300);
    foreach (['store.habit/detail' => ['staff_id' => 0],
        'store.habit/dateRpe' => ['staff_id' => 0],
        'store.habit/detailRpe' => ['staff_id' => 0, 'train_date' => $trainDate]] as $path => $form) {
        $denied = $request($path, $form, $token);
        if (!$denied['complete']) { $unsettledRequest = true; throw new RuntimeException('Habit3 negative request unsettled'); }
        $assert($denied['status'] !== 200
            && strpos($denied['message'], '没有当前api的访问权限') !== false,
            'Habit3 without-role request not denied by ACL');
    }
    $results['no_role_three_denied'] = true;

    $db->prepare('INSERT INTO YOSHOP_STORE_ROLE'
        . ' (ROLE_NAME,STORE_ID,CREATE_BY,CREATE_TIME,UPDATE_TIME)'
        . ' VALUES (?,10001,?,?,?)')
        ->execute([$marker, $marker, time(), time()]);
    $roles = $rows('SELECT ROLE_ID FROM YOSHOP_STORE_ROLE WHERE ROLE_NAME=?', [$marker]);
    $assert(count($roles) === 1, 'Habit3 synthetic role ambiguous');
    $roleId = (int)$roles[0]['role_id'];
    $assert($roleId > 0, 'Habit3 synthetic role ID invalid');
    $db->prepare('INSERT INTO YOSHOP_STORE_USER_ROLE'
        . ' (STORE_USER_ID,ROLE_ID,STORE_ID,CREATE_TIME) VALUES (?,?,10001,?)')
        ->execute([$operatorId, $roleId, time()]);
    $db->prepare('INSERT INTO YOSHOP_STORE_ROLE_MENU'
        . ' (ROLE_ID,MENU_ID,STORE_ID,CREATE_TIME) VALUES (?,?,10001,?)')
        ->execute([$roleId, $menuId, time()]);
    $assert($count('SELECT COUNT(*) FROM YOSHOP_STORE_USER_ROLE'
        . ' WHERE STORE_USER_ID=? AND ROLE_ID=?', [$operatorId, $roleId]) === 1
        && $count('SELECT COUNT(*) FROM YOSHOP_STORE_ROLE_MENU'
            . ' WHERE ROLE_ID=? AND MENU_ID=?', [$roleId, $menuId]) === 1,
        'Habit3 role attachment mismatch');
    $results['role_menu_scope_api_count'] = 7;

    $db->prepare('INSERT INTO YOSHOP_STAFF'
        . ' (NAME,STORE_ID,HEIGHT,WEIGHT,GENDER,BIRTHDAY,PHONE,IS_DELETE)'
        . " VALUES (?,10001,170,60,1,'1990-01-01','',0)")
        ->execute([$marker]);
    $staffRows = $rows('SELECT ID FROM YOSHOP_STAFF WHERE NAME=? AND STORE_ID=10001', [$marker]);
    $assert(count($staffRows) === 1, 'Habit3 synthetic staff ambiguous');
    $staffId = (int)$staffRows[0]['id'];
    $db->prepare('INSERT INTO YOSHOP_HABIT'
        . ' (STAFF_ID,HABIT,STORE_ID,IS_DELETE,CREATE_BY)'
        . ' VALUES (?,2,10001,0,?)')->execute([$staffId, $marker]);
    $db->prepare('INSERT INTO YOSHOP_HABIT_RPE'
        . ' (STAFF_ID,TRAIN_DATE,STORE_ID,IS_DELETE,CREATE_BY)'
        . ' VALUES (?,?,10001,0,?)')->execute([$staffId, $trainDate, $marker]);
    $rpeRows = $rows('SELECT ID FROM YOSHOP_HABIT_RPE'
        . ' WHERE STAFF_ID=? AND TRAIN_DATE=? AND CREATE_BY=?',
        [$staffId, $trainDate, $marker]);
    $assert(count($rpeRows) === 1, 'Habit3 synthetic RPE ambiguous');
    $rpeId = (int)$rpeRows[0]['id'];
    $db->prepare('INSERT INTO YOSHOP_HABIT_RPE_INFO'
        . ' (RPE_ID,START_TIME,END_TIME,TRAIN_MIN,REST_MIN,STRENGTH,STORE_ID,IS_DELETE,CREATE_BY)'
        . " VALUES (?,'09:00:00','09:30:00',30,0,1,10001,0,?)")
        ->execute([$rpeId, $marker]);
    $assert($count('SELECT COUNT(*) FROM YOSHOP_HABIT WHERE STAFF_ID=? AND CREATE_BY=?',
        [$staffId, $marker]) === 1
        && $count('SELECT COUNT(*) FROM YOSHOP_HABIT_RPE_INFO'
            . ' WHERE RPE_ID=? AND CREATE_BY=?', [$rpeId, $marker]) === 1,
        'Habit3 source fixture original-table mismatch');

    $cases = [
        'detail' => ['staff_id' => $staffId],
        'dateRpe' => ['staff_id' => $staffId],
        'detailRpe' => ['staff_id' => $staffId, 'train_date' => $trainDate],
    ];
    foreach ($cases as $action => $form) {
        $response = $request('store.habit/' . $action, $form, $token);
        if (!$response['complete']) { $unsettledRequest = true; throw new RuntimeException('Habit3 positive request unsettled'); }
        $assert($response['http'] === 200 && $response['status'] === 200
            && $response['elapsed_ms'] <= 15000,
            'Habit3 authorized response status/timing mismatch');
        $data = $response['data'];
        if ($action === 'detail') {
            $detail = $data['detail'] ?? null;
            $checked = is_array($detail) ? array_filter($detail,
                static function ($row): bool { return (int)($row['checked'] ?? 0) === 1; }) : [];
            $assert(is_array($detail) && count($detail) > 0 && count($checked) === 1,
                'Habit3 detail enum checked mismatch');
        } elseif ($action === 'dateRpe') {
            $dates = $data['date_list'] ?? null;
            $assert(is_array($dates) && count($dates) === 1
                && substr((string)$dates[0], 0, 10) === $trainDate,
                'Habit3 RPE date list mismatch');
        } else {
            $detail = $data['detail'] ?? null;
            $assert(is_array($detail) && is_array($detail['info'] ?? null)
                && count($detail['info']) === 1,
                'Habit3 RPE nested detail mismatch');
        }
        $results[$action . '_nonempty_original_match'] = true;
    }
} catch (Throwable $error) {
    $failure = get_class($error) . ':' . hash('sha256', $error->getMessage());
} finally {
    try {
        $cacheAbsent = false;
        try {
            \think\facade\Cache::delete($token);
            $cacheAbsent = !\think\facade\Cache::has($token);
        } catch (Throwable $cacheError) {
            $cleanup['cache_error_class'] = get_class($cacheError);
        }
        $cleanup['cache_absent'] = $cacheAbsent;
        // Resolve only this marker's identity before revoking its role grants.
            if ($operatorId <= 0) {
                $owned = $rows('SELECT STORE_USER_ID FROM YOSHOP_STORE_USER WHERE USER_NAME=?', [$marker]);
                $assert(count($owned) <= 1, 'Synthetic operator ambiguous; refusing cleanup');
                if ($owned) $operatorId = (int)$owned[0]['store_user_id'];
            }
            if ($roleId <= 0) {
                $owned = $rows('SELECT ROLE_ID FROM YOSHOP_STORE_ROLE WHERE ROLE_NAME=?', [$marker]);
                $assert(count($owned) <= 1, 'Synthetic role ambiguous; refusing cleanup');
                if ($owned) $roleId = (int)$owned[0]['role_id'];
            }
        $grantsRevoked = true;
        if ($roleId > 0) {
            $roleMenus = $rows('SELECT MENU_ID,STORE_ID FROM YOSHOP_STORE_ROLE_MENU'
                . ' WHERE ROLE_ID=?', [$roleId]);
            $userRoles = $rows('SELECT STORE_USER_ID,STORE_ID FROM YOSHOP_STORE_USER_ROLE'
                . ' WHERE ROLE_ID=?', [$roleId]);
            $assert(count($roleMenus) <= 1 && (!$roleMenus
                || ((int)$roleMenus[0]['menu_id'] === $menuId
                    && (int)$roleMenus[0]['store_id'] === 10001))
                && count($userRoles) <= 1 && (!$userRoles
                    || ((int)$userRoles[0]['store_user_id'] === $operatorId
                        && (int)$userRoles[0]['store_id'] === 10001)),
                'Unexpected synthetic role relation; refusing revocation');
            $db->prepare('DELETE FROM YOSHOP_STORE_ROLE_MENU WHERE ROLE_ID=? AND MENU_ID=?')
                ->execute([$roleId, $menuId]);
            $db->prepare('DELETE FROM YOSHOP_STORE_USER_ROLE WHERE ROLE_ID=? AND STORE_USER_ID=?')
                ->execute([$roleId, $operatorId]);
            $grantsRevoked = $count('SELECT COUNT(*) FROM YOSHOP_STORE_ROLE_MENU'
                . ' WHERE ROLE_ID=?', [$roleId]) === 0
                && $count('SELECT COUNT(*) FROM YOSHOP_STORE_USER_ROLE'
                    . ' WHERE ROLE_ID=?', [$roleId]) === 0;
        }
        $cleanup['role_grants_revoked'] = $grantsRevoked;
        $barrierComplete = true;
        if ($unsettledRequest) {
            // C's approved 18102 PHP server has one worker: a same-port response
            // proves the previous request completed before actor rows are removed.
            $barrierRaw = @file_get_contents($base . '/__poc_boundary', false,
                stream_context_create(['http' => [
                    'method' => 'GET', 'header' => "Connection: close\r\n",
                    'timeout' => 30, 'ignore_errors' => true,
                ]]));
            $barrier = is_string($barrierRaw) ? json_decode($barrierRaw, true) : null;
            unset($barrierRaw);
            preg_match('/HTTP\/\S+\s+(\d+)/', $http_response_header[0] ?? '', $barrierHttp);
            $cmdline = str_replace("\0", ' ',
                (string)@file_get_contents('/proc/' . $pid . '/cmdline'));
            $oneWorker = !isset($env['PHP_CLI_SERVER_WORKERS'])
                || (int)$env['PHP_CLI_SERVER_WORKERS'] <= 1;
            $barrierComplete = is_array($barrier)
                && !empty($barrier['external_stubs'])
                && (int)($barrierHttp[1] ?? 0) === 200
                && is_dir('/proc/' . $pid)
                && strpos($cmdline, '-S 127.0.0.1:18102') !== false
                && $oneWorker;
            $cleanup['single_worker_barrier_complete'] = $barrierComplete;
        }
        $canCleanupActor = $cacheAbsent && $grantsRevoked && $barrierComplete;
        if (!$canCleanupActor) {
            $cleanup['deferred_actor_and_business_cleanup'] = true;
        } else {
            if ($staffId <= 0) {
                $owned = $rows('SELECT ID FROM YOSHOP_STAFF WHERE NAME=?', [$marker]);
                $assert(count($owned) <= 1, 'Synthetic staff ambiguous; refusing cleanup');
                if ($owned) $staffId = (int)$owned[0]['id'];
            }
            if ($rpeId <= 0 && $staffId > 0) {
                $owned = $rows('SELECT ID FROM YOSHOP_HABIT_RPE'
                    . ' WHERE STAFF_ID=? AND TRAIN_DATE=? AND CREATE_BY=?',
                    [$staffId, $trainDate, $marker]);
                $assert(count($owned) <= 1, 'Synthetic RPE ambiguous; refusing cleanup');
                if ($owned) $rpeId = (int)$owned[0]['id'];
            }
            if ($rpeId > 0) {
                $owned = $rows('SELECT ID,RPE_ID,CREATE_BY FROM YOSHOP_HABIT_RPE_INFO'
                    . ' WHERE RPE_ID=?', [$rpeId]);
                $assert(count($owned) <= 1 && (!$owned
                    || (string)$owned[0]['create_by'] === $marker),
                    'Unexpected RPE child; refusing cleanup');
                $db->prepare('DELETE FROM YOSHOP_HABIT_RPE_INFO WHERE RPE_ID=? AND CREATE_BY=?')
                    ->execute([$rpeId, $marker]);
                $db->prepare('DELETE FROM YOSHOP_HABIT_RPE WHERE ID=? AND STAFF_ID=? AND CREATE_BY=?')
                    ->execute([$rpeId, $staffId, $marker]);
            }
            if ($staffId > 0) {
                $habits = $rows('SELECT ID,CREATE_BY FROM YOSHOP_HABIT WHERE STAFF_ID=?', [$staffId]);
                $rpes = $rows('SELECT ID,CREATE_BY FROM YOSHOP_HABIT_RPE WHERE STAFF_ID=?', [$staffId]);
                $assert(count($habits) <= 1 && (!$habits || (string)$habits[0]['create_by'] === $marker)
                    && count($rpes) === 0,
                    'Unexpected synthetic staff relation; refusing cleanup');
                $db->prepare('DELETE FROM YOSHOP_HABIT WHERE STAFF_ID=? AND CREATE_BY=?')
                    ->execute([$staffId, $marker]);
                $db->prepare('DELETE FROM YOSHOP_STAFF WHERE ID=? AND NAME=?')
                    ->execute([$staffId, $marker]);
            }
            if ($roleId > 0) {
                $db->prepare('DELETE FROM YOSHOP_STORE_ROLE WHERE ROLE_ID=? AND ROLE_NAME=?')
                    ->execute([$roleId, $marker]);
            }
            if ($operatorId > 0) {
                $db->prepare('DELETE FROM YOSHOP_STORE_USER WHERE STORE_USER_ID=? AND USER_NAME=?')
                    ->execute([$operatorId, $marker]);
            }
            $cleanup = array_merge($cleanup, [
                'operator_zero' => $count('SELECT COUNT(*) FROM YOSHOP_STORE_USER WHERE USER_NAME=?', [$marker]) === 0,
                'role_zero' => $count('SELECT COUNT(*) FROM YOSHOP_STORE_ROLE WHERE ROLE_NAME=?', [$marker]) === 0,
                'staff_zero' => $count('SELECT COUNT(*) FROM YOSHOP_STAFF WHERE NAME=?', [$marker]) === 0,
                'habit_zero' => $staffId <= 0 || $count('SELECT COUNT(*) FROM YOSHOP_HABIT WHERE STAFF_ID=?', [$staffId]) === 0,
                'rpe_zero' => $staffId <= 0 || $count('SELECT COUNT(*) FROM YOSHOP_HABIT_RPE WHERE STAFF_ID=?', [$staffId]) === 0,
                'rpe_info_zero' => $rpeId <= 0 || $count('SELECT COUNT(*) FROM YOSHOP_HABIT_RPE_INFO WHERE RPE_ID=?', [$rpeId]) === 0,
                'user_role_zero' => $roleId <= 0 || $count('SELECT COUNT(*) FROM YOSHOP_STORE_USER_ROLE WHERE ROLE_ID=?', [$roleId]) === 0,
                'role_menu_zero' => $roleId <= 0 || $count('SELECT COUNT(*) FROM YOSHOP_STORE_ROLE_MENU WHERE ROLE_ID=?', [$roleId]) === 0,
            ]);
        }
    } catch (Throwable $error) {
        $cleanup['error_class'] = get_class($error);
        $cleanup['error_sha256'] = hash('sha256', $error->getMessage());
    }
}
$clean = !in_array(false, $cleanup, true)
    && !isset($cleanup['error_class']) && !isset($cleanup['cache_error_class'])
    && empty($cleanup['deferred_actor_and_business_cleanup']);
echo json_encode(['suite' => 'ai-store-habit3-C', 'marker' => $marker,
    'results' => $results, 'failure' => $failure, 'cleanup' => $cleanup],
    JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES) . PHP_EOL;
exit($failure === '' && $clean ? 0 : 1);
