<?php

declare(strict_types=1);

// REVIEW DRAFT: never run without a separate root-agent approval of this exact SHA.
if ((string) getenv('CDXPARMENU_EXECUTE') !== 'ROOT_APPROVED_EXACT_SHA') {
    fwrite(STDERR, "Review draft; execution disabled\n");
    exit(2);
}
set_exception_handler(static function (Throwable $error): void {
    fwrite(STDERR, 'ROLE_MENU_BATCH_PRECHECK_FAILED ' . get_class($error)
        . ' code=' . (string) $error->getCode() . PHP_EOL);
    exit(2);
});

$lane = (string) getenv('CDXPARMENU_LANE');
$root = (string) getenv('POC_APP_ROOT');
$base = rtrim((string) getenv('POC_BASE_URL'), '/');
$runtime = [
    'A' => ['/tmp/zhct-http-store-a.l5omH8', 'http://127.0.0.1:18097'],
    'B' => ['/tmp/zhct-http-store-mysql.dQTTBP', 'http://127.0.0.1:18081'],
    'C' => ['/tmp/zhct-dm-poc-20260928.fcpiiN/store', 'http://127.0.0.1:18080'],
];
if (!isset($runtime[$lane]) || $root !== $runtime[$lane][0]
    || $base !== $runtime[$lane][1]
    || !is_file($root . '/application/base/Security.php')) {
    throw new RuntimeException('Approved Store isolation runtime required');
}
$boundary = json_decode((string) file_get_contents($base . '/__poc_boundary'), true);
if (!is_array($boundary) || empty($boundary['external_stubs'])
    || empty($boundary['redis_isolated']) || !empty($boundary['real_queue'])) {
    throw new RuntimeException('Store isolation boundary missing');
}

if ($lane === 'C') {
    if ((string) getenv('POC_DIALECT') !== 'dm') {
        throw new RuntimeException('Nonproduction DM dialect required');
    }
    $pid = (string) getenv('POC_RUNTIME_PID');
    $cmdline=ctype_digit($pid)
        ? (string) file_get_contents('/proc/' . $pid . '/cmdline') : '';
    if (!ctype_digit($pid)
        || strpos($cmdline,'127.0.0.1:18080') === false
        || strpos($cmdline,$root . '/public') === false) {
        throw new RuntimeException('C PID/port/docroot mismatch');
    }
    $context = array_fill_keys(
        ['POC_HOST','POC_PORT','POC_SCHEMA','POC_USER','POC_PASSWORD'], '');
    foreach ($context as $key => $_) $context[$key] = (string) getenv($key);
    if ($context['POC_HOST'] === '') {
        foreach (explode("\0", (string) file_get_contents('/proc/' . $pid . '/environ')) as $entry) {
            $pair = explode('=', $entry, 2);
            if (count($pair) === 2 && array_key_exists($pair[0], $context)) {
                $context[$pair[0]] = $pair[1];
            }
        }
    }
    foreach ($context as $value) {
        if ($value === '') throw new RuntimeException('C POC context incomplete');
    }
    if ($context['POC_HOST'] !== '127.0.0.1'
        || $context['POC_PORT'] !== '5237'
        || strtoupper($context['POC_SCHEMA']) !== 'ZHCT_APP'
        || strtoupper($context['POC_USER']) !== 'ZHCT_APP') {
        throw new RuntimeException('Unexpected C target');
    }
    $dsn = 'dm:host=' . $context['POC_HOST'] . ';port=' . $context['POC_PORT']
        . ';schema=' . $context['POC_SCHEMA'];
    $open = static function () use ($dsn, $context): PDO {
        return new PDO($dsn, $context['POC_USER'], $context['POC_PASSWORD'], [
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
        ]);
    };
    $table = static function (string $name): string { return strtoupper($name); };
} else {
    if ((string) getenv('POC_DIALECT') !== 'mysql') {
        throw new RuntimeException('Local development MySQL dialect required');
    }
    $localPath = realpath(__DIR__
        . '/../../../../../zhctproject/store/application/config/local/database.php');
    if (!$localPath || strpos($localPath,
        '/zhctproject/store/application/config/local/') === false) {
        throw new RuntimeException('Approved code local database config unavailable');
    }
    $loadConfig=static function (string $path): array {
        $config=require $path;
        if (!is_array($config)) throw new RuntimeException('Invalid DB config');
        return $config;
    };
    $local = $loadConfig($localPath);
    $selected = $loadConfig($root . '/application/database.php');
    foreach (['hostname','database','username','password'] as $field) {
        if (!isset($local[$field],$selected[$field])
            || $local[$field] !== $selected[$field]) {
            throw new RuntimeException('Runtime differs from code local DB');
        }
    }
    if ((int) ($local['hostport'] ?? 3306)
        !== (int) ($selected['hostport'] ?? 3306)) {
        throw new RuntimeException('Runtime differs from code local DB port');
    }
    $dsn = 'mysql:host=' . $local['hostname'] . ';port='
        . (int) ($local['hostport'] ?? 3306) . ';dbname='
        . $local['database'] . ';charset=utf8mb4';
    $open = static function () use ($dsn,$local): PDO {
        return new PDO($dsn,(string)$local['username'],(string)$local['password'],[
            PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION,
            PDO::ATTR_EMULATE_PREPARES => false,
            PDO::ATTR_DEFAULT_FETCH_MODE => PDO::FETCH_ASSOC,
            PDO::ATTR_TIMEOUT => 8,
        ]);
    };
    $table = static function (string $name): string { return strtolower($name); };
}

function cdxRoleMenuUuid(): string
{
    $bytes = random_bytes(16);
    $bytes[6] = chr((ord($bytes[6]) & 0x0f) | 0x40);
    $bytes[8] = chr((ord($bytes[8]) & 0x3f) | 0x80);
    $hex = strtoupper(bin2hex($bytes));
    return substr($hex,0,8).'-'.substr($hex,8,4).'-'.substr($hex,12,4)
        .'-'.substr($hex,16,4).'-'.substr($hex,20,12);
}
function cdxRoleMenuRows(PDO $db, string $sql, array $args = []): array
{
    $stmt = $db->prepare($sql);
    $stmt->execute($args);
    return array_map(static function (array $row): array {
        return array_change_key_case($row, CASE_LOWER);
    }, $stmt->fetchAll(PDO::FETCH_ASSOC));
}
function cdxRoleMenuCount(PDO $db, string $sql, array $args = []): int
{
    return (int) (cdxRoleMenuRows($db,$sql,$args)[0]['n'] ?? -1);
}
function cdxRoleMenuFind(array $tree, string $uuid): array
{
    $found = [];
    foreach ($tree as $row) {
        if (!is_array($row)) continue;
        if (($row['uuid'] ?? null) === $uuid) $found[] = $row;
        if (isset($row['children']) && is_array($row['children'])) {
            $found = array_merge($found, cdxRoleMenuFind($row['children'],$uuid));
        }
    }
    return $found;
}
function cdxRoleMenuPost(string $base,string $path,array $form,string $token): array
{
    $headers = "Content-Type: application/x-www-form-urlencoded\r\n"
        . 'token: ' . $token . "\r\n";
    $context = stream_context_create(['http'=>[
        'method'=>'POST','header'=>$headers,
        'content'=>http_build_query($form,'','&',PHP_QUERY_RFC3986),
        'timeout'=>20,'ignore_errors'=>true,
    ]]);
    $body = @file_get_contents($base.$path,false,$context);
    $responseHeaders = $http_response_header ?? [];
    if (strpos((string)($responseHeaders[0] ?? ''),' 200 ') === false
        || !is_string($body)) {
        throw new RuntimeException('Isolated role/menu HTTP transport failed');
    }
    $json = json_decode($body,true);
    if (!is_array($json)) throw new RuntimeException('Role/menu HTTP response is not JSON');
    return $json;
}
function cdxRoleMenuFilterContains(string $filter,string $path): bool
{
    foreach (explode('|',strtolower($filter)) as $item) {
        if (trim($item,' /') === $path) return true;
    }
    return false;
}
function cdxRoleMenuHasUnique(PDO $db,string $lane,string $schema,
    string $table,array $expected): bool
{
    if ($lane === 'C') {
        $rows=cdxRoleMenuRows($db,
            'SELECT i.INDEX_NAME,i.UNIQUENESS,ic.COLUMN_NAME,ic.COLUMN_POSITION'
            . ' FROM USER_INDEXES i JOIN USER_IND_COLUMNS ic'
            . ' ON i.INDEX_NAME=ic.INDEX_NAME WHERE i.TABLE_NAME=?'
            . ' ORDER BY i.INDEX_NAME,ic.COLUMN_POSITION',[strtoupper($table)]);
    } else {
        $rows=cdxRoleMenuRows($db,
            'SELECT INDEX_NAME,NON_UNIQUE,SEQ_IN_INDEX,COLUMN_NAME'
            . ' FROM information_schema.STATISTICS'
            . ' WHERE TABLE_SCHEMA=? AND TABLE_NAME=?'
            . ' ORDER BY INDEX_NAME,SEQ_IN_INDEX',[$schema,strtolower($table)]);
    }
    $indexes=[];
    foreach ($rows as $row) {
        $name=(string)$row['index_name'];
        $indexes[$name]['unique']=$lane==='C'
            ? strtoupper((string)$row['uniqueness'])==='UNIQUE'
            : (int)$row['non_unique']===0;
        $indexes[$name]['columns'][]=strtolower((string)$row['column_name']);
    }
    foreach ($indexes as $index) {
        if ($index['unique'] && $index['columns']===$expected) return true;
    }
    return false;
}

$db = $open();
if ($lane === 'C') {
    $schema = strtoupper((string)$db->query(
        "SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")->fetchColumn());
    if ($schema !== 'ZHCT_APP') throw new RuntimeException('Wrong C schema');
} elseif (strpos((string)$db->query('SELECT VERSION()')->fetchColumn(),'5.6.') === false) {
    throw new RuntimeException('Wrong A/B MySQL engine');
}
$marker = 'CDXPARMENU' . strtoupper(bin2hex(random_bytes(4)));
$userUuid = cdxRoleMenuUuid();
$roleUuid = cdxRoleMenuUuid();
$categoryMenu = '8E1F0A4D-AE59-4D44-A75D-4F98A6714B31';
$roleListMenu = 'D9005A93-AB6A-9840-02D3-32F3111AC5E7';
$menuListMenu = '';
$menuParent = '';
$menuName = '';
$userId = 0;
$roleId = 0;
$granted = [];
$phase = 'preflight';
$result = ['lane'=>$lane,'marker'=>$marker,'events'=>[],'cleanup'=>[]];
$userTable = $table('ydy_user');
$roleTable = $table('ydy_role');
$userRoleTable = $table('ydy_user_role');
$roleMenuTable = $table('ydy_role_menu');
$menuTable = $table('ydy_menu');

try {
    $required = [
        $userTable=>['id','uuid','account','pwd','salt','name','type',
            'status','create_time','create_by','is_delete'],
        $roleTable=>['id','uuid','name','status','create_time','create_by'],
        $userRoleTable=>['id','user_uuid','role_uuid','create_time','create_by'],
        $roleMenuTable=>['id','role_uuid','menu_uuid','create_time','create_by'],
        $menuTable=>['id','uuid','puuid','name','type','filter','menu_path'],
    ];
    $inserted = [
        $userTable=>['uuid','account','pwd','salt','name','type','status',
            'create_time','create_by','is_delete'],
        $roleTable=>['uuid','name','status','create_time','create_by'],
        $userRoleTable=>['user_uuid','role_uuid','create_time','create_by'],
        $roleMenuTable=>['role_uuid','menu_uuid','create_time','create_by'],
    ];
    foreach ($required as $name=>$fields) {
        if ($lane === 'C') {
            $columns = cdxRoleMenuRows($db,
                'SELECT COLUMN_NAME,NULLABLE,DATA_DEFAULT FROM USER_TAB_COLUMNS'
                . ' WHERE TABLE_NAME=?',
                [strtoupper($name)]);
            $triggers = cdxRoleMenuCount($db,
                'SELECT COUNT(*) N FROM USER_TRIGGERS WHERE TABLE_NAME=?',
                [strtoupper($name)]);
            $fks = cdxRoleMenuCount($db,
                "SELECT COUNT(*) N FROM USER_CONSTRAINTS WHERE TABLE_NAME=? AND CONSTRAINT_TYPE='R'",
                [strtoupper($name)]);
        } else {
            $columns = cdxRoleMenuRows($db,
                'SELECT COLUMN_NAME,IS_NULLABLE,COLUMN_DEFAULT'
                . ' FROM information_schema.COLUMNS'
                . ' WHERE TABLE_SCHEMA=? AND TABLE_NAME=?',
                [$local['database'],$name]);
            $triggers = cdxRoleMenuCount($db,
                'SELECT COUNT(*) N FROM information_schema.TRIGGERS'
                . ' WHERE TRIGGER_SCHEMA=? AND EVENT_OBJECT_TABLE=?',
                [$local['database'],$name]);
            $fks = cdxRoleMenuCount($db,
                'SELECT COUNT(*) N FROM information_schema.KEY_COLUMN_USAGE'
                . ' WHERE TABLE_SCHEMA=? AND TABLE_NAME=?'
                . ' AND REFERENCED_TABLE_NAME IS NOT NULL',
                [$local['database'],$name]);
        }
        $present = array_map(static function (array $row): string {
            return strtolower((string)$row['column_name']);
        },$columns);
        if (array_diff($fields,$present) || $triggers !== 0 || $fks !== 0) {
            throw new RuntimeException('Required schema or side-effect gate failed');
        }
        if (isset($inserted[$name])) {
            foreach ($columns as $column) {
                $columnName=strtolower((string)$column['column_name']);
                $notNull=$lane==='C'
                    ? (string)$column['nullable']==='N'
                    : (string)$column['is_nullable']==='NO';
                $default=$lane==='C' ? $column['data_default']
                    : $column['column_default'];
                if ($columnName!=='id' && $notNull && $default===null
                    && !in_array($columnName,$inserted[$name],true)) {
                    throw new RuntimeException('Synthetic insert omits required column');
                }
            }
        }
    }
    $indexSchema=$lane==='C' ? 'ZHCT_APP' : (string)$local['database'];
    if (!cdxRoleMenuHasUnique($db,$lane,$indexSchema,$roleTable,['name'])
        || !cdxRoleMenuHasUnique($db,$lane,$indexSchema,
            $userRoleTable,['user_uuid','role_uuid'])
        || !cdxRoleMenuHasUnique($db,$lane,$indexSchema,
            $roleMenuTable,['role_uuid','menu_uuid'])) {
        throw new RuntimeException('Required unique-key gate failed');
    }
    $menus = cdxRoleMenuRows($db,'SELECT UUID,PUUID,NAME,TYPE,FILTER,MENU_PATH'
        . ' FROM '.$menuTable.' WHERE UUID IN (?,?)',[$categoryMenu,$roleListMenu]);
    $byUuid = [];
    foreach ($menus as $row) $byUuid[(string)$row['uuid']] = $row;
    if (count($menus)!==2
        || !isset($byUuid[$categoryMenu],$byUuid[$roleListMenu])
        || !cdxRoleMenuFilterContains((string)$byUuid[$categoryMenu]['filter'],
            'p/dishcustomcategory/list')
        || !cdxRoleMenuFilterContains((string)$byUuid[$roleListMenu]['filter'],
            'p/role/lists')
        || (int)$byUuid[$categoryMenu]['type']!==2
        || (int)$byUuid[$roleListMenu]['type']!==2) {
        throw new RuntimeException('Required existing menu identity/filter absent');
    }
    $menuParent = (string)$byUuid[$categoryMenu]['puuid'];
    $menuName = (string)$byUuid[$categoryMenu]['name'];
    if ($menuParent==='' || $menuName===''
        || cdxRoleMenuCount($db,'SELECT COUNT(*) N FROM '.$menuTable.' WHERE UUID=?',
            [$menuParent])!==1
        || strpos((string)$byUuid[$categoryMenu]['menu_path'],$menuParent)===false) {
        throw new RuntimeException('Known menu parent/path gate failed');
    }
    $menuListRows = cdxRoleMenuRows($db,
        'SELECT UUID,FILTER FROM '.$menuTable.' WHERE LOWER(FILTER) LIKE ?',
        ['%p/menu/lists%']);
    $matches = array_values(array_filter($menuListRows,
        static function (array $row): bool {
            return cdxRoleMenuFilterContains((string)$row['filter'],'p/menu/lists');
        }));
    if (count($matches)>1) throw new RuntimeException('Ambiguous menu-list permission');
    if ($matches) $menuListMenu=(string)$matches[0]['uuid'];
    if (cdxRoleMenuCount($db,'SELECT COUNT(*) N FROM '.$userTable
            .' WHERE UUID=? OR ACCOUNT=?',[$userUuid,$marker])!==0
        || cdxRoleMenuCount($db,'SELECT COUNT(*) N FROM '.$roleTable
            .' WHERE UUID=? OR NAME=?',[$roleUuid,$marker])!==0
        || cdxRoleMenuCount($db,'SELECT COUNT(*) N FROM '.$userTable
            .' WHERE ACCOUNT LIKE ?', ['CDXPARMENU%'])!==0
        || cdxRoleMenuCount($db,'SELECT COUNT(*) N FROM '.$roleTable
            .' WHERE NAME LIKE ?', ['CDXPARMENU%'])!==0) {
        throw new RuntimeException('Synthetic identity prefix not clean');
    }
    $result['events'][]=['step'=>'preflight','existing_menu_exact'=>2,
        'menu_lists_permission_available'=>$menuListMenu!=='',
        'marker_prefix_zero'=>true];

    $phase='type1_no_menu';
    $now=date('Y-m-d H:i:s');
    $db->prepare('INSERT INTO '.$userTable
        .' (UUID,ACCOUNT,PWD,SALT,NAME,TYPE,STATUS,CREATE_TIME,CREATE_BY,IS_DELETE)'
        .' VALUES (?,?,?,?,?,?,?,?,?,?)')
        ->execute([$userUuid,$marker,'DISABLED',strtoupper(bin2hex(random_bytes(8))),
            $marker,1,1,$now,'CODEX',0]);
    $userRows = cdxRoleMenuRows($open(),'SELECT ID,TYPE FROM '.$userTable
        .' WHERE UUID=? AND ACCOUNT=?',[$userUuid,$marker]);
    if (count($userRows)!==1 || (int)$userRows[0]['type']!==1) {
        throw new RuntimeException('Own type1 actor not persisted');
    }
    $userId=(int)$userRows[0]['id'];
    require_once $root.'/application/base/Security.php';
    $token=\app\base\Security::encrypt(implode("\t",[
        (string)$userId,$userUuid,$marker,'1','','',$marker,
    ]));
    if (!is_string($token) || $token==='') {
        throw new RuntimeException('Synthetic token unavailable');
    }
    $denied=cdxRoleMenuPost($base,'/p/role/lists',
        ['keyword'=>$marker,'page'=>1],$token);
    if (($denied['code']??null)!==1) {
        throw new RuntimeException('No-menu type1 role-list negative failed');
    }
    $result['events'][]=['route'=>'/p/role/lists','case'=>'no_menu',
        'business_code'=>1];

    $phase='minimal_role';
    $db->prepare('INSERT INTO '.$roleTable
        .' (UUID,NAME,STATUS,CREATE_TIME,CREATE_BY) VALUES (?,?,?,?,?)')
        ->execute([$roleUuid,$marker,1,$now,'CODEX']);
    $roleRows=cdxRoleMenuRows($open(),'SELECT ID,STATUS FROM '.$roleTable
        .' WHERE UUID=? AND NAME=?',[$roleUuid,$marker]);
    if (count($roleRows)!==1 || (int)$roleRows[0]['status']!==1) {
        throw new RuntimeException('Own role not persisted');
    }
    $roleId=(int)$roleRows[0]['id'];
    $db->prepare('INSERT INTO '.$userRoleTable
        .' (USER_UUID,ROLE_UUID,CREATE_TIME,CREATE_BY) VALUES (?,?,?,?)')
        ->execute([$userUuid,$roleUuid,$now,$marker]);
    $granted=[$categoryMenu,$roleListMenu];
    if ($menuListMenu!=='') $granted[]=$menuListMenu;
    foreach ($granted as $menuUuid) {
        $db->prepare('INSERT INTO '.$roleMenuTable
            .' (ROLE_UUID,MENU_UUID,CREATE_TIME,CREATE_BY) VALUES (?,?,?,?)')
            ->execute([$roleUuid,$menuUuid,$now,$marker]);
    }
    $verify=$open();
    if (cdxRoleMenuCount($verify,'SELECT COUNT(*) N FROM '.$userRoleTable
            .' WHERE USER_UUID=? AND ROLE_UUID=?',[$userUuid,$roleUuid])!==1
        || cdxRoleMenuCount($verify,'SELECT COUNT(*) N FROM '.$roleMenuTable
            .' WHERE ROLE_UUID=? AND CREATE_BY=?',[$roleUuid,$marker])!==count($granted)) {
        throw new RuntimeException('Own permission link readback failed');
    }
    $result['events'][]=['step'=>'minimal_role','own_menu_links'=>count($granted)];

    $phase='formal_role_reads';
    $json=cdxRoleMenuPost($base,'/p/role/lists',
        ['keyword'=>$marker,'page'=>1],$token);
    $items=$json['data']['data']??null;
    if (($json['code']??null)!==0 || !is_array($items)
        || count($items)!==1 || ($items[0]['uuid']??null)!==$roleUuid
        || ($items[0]['name']??null)!==$marker
        || (int)($items[0]['user_num']??-1)!==1) {
        throw new RuntimeException('Formal role/lists own row mismatch');
    }
    $result['events'][]=['route'=>'/p/role/lists','business_code'=>0,
        'own_rows'=>1,'own_row_field_types'=>array_map('gettype',$items[0])];
    $json=cdxRoleMenuPost($base,'/p/role/roles',
        ['keyword'=>$marker,'user_uuid'=>$userUuid],$token);
    $items=$json['data']??null;
    if (($json['code']??null)!==0 || !is_array($items)
        || count($items)!==1 || ($items[0]['uuid']??null)!==$roleUuid
        || (int)($items[0]['checked']??-1)!==1) {
        throw new RuntimeException('Formal role/roles own row mismatch');
    }
    $result['events'][]=['route'=>'/p/role/roles','business_code'=>0,
        'own_rows'=>1,'own_row_field_types'=>array_map('gettype',$items[0])];
    $json=cdxRoleMenuPost($base,'/p/role/info',['uuid'=>$roleUuid],$token);
    $item=$json['data']??null;
    if (($json['code']??null)!==0 || !is_array($item)
        || ($item['uuid']??null)!==$roleUuid
        || ($item['name']??null)!==$marker
        || (int)($item['status']??-1)!==1
        || ($item['user_uuid']??null)!==[$userUuid]) {
        throw new RuntimeException('Formal role/info own row mismatch');
    }
    $result['events'][]=['route'=>'/p/role/info','business_code'=>0,
        'own_rows'=>1,'own_row_field_types'=>array_map('gettype',$item)];
    $json=cdxRoleMenuPost($base,'/p/role/functionalLists',
        ['uuid'=>$roleUuid],$token);
    $tree=$json['data']??null;
    $own=is_array($tree)?cdxRoleMenuFind($tree,$categoryMenu):[];
    if (($json['code']??null)!==0 || count($own)!==1
        || ($own[0]['is_check']??null)!==true) {
        throw new RuntimeException('Formal role/functionalLists own checked mismatch');
    }
    $result['events'][]=['route'=>'/p/role/functionalLists',
        'business_code'=>0,'own_checked_count'=>1,
        'own_row_field_types'=>array_map('gettype',$own[0])];

    $phase='formal_menu_reads';
    $json=cdxRoleMenuPost($base,'/p/menu/info',['uuid'=>$categoryMenu],$token);
    $item=$json['data']??null;
    if (($json['code']??null)!==0 || !is_array($item)
        || ($item['uuid']??null)!==$categoryMenu
        || ($item['puuid']??null)!==$menuParent
        || (int)($item['type']??-1)!==2) {
        throw new RuntimeException('Formal menu/info known row mismatch');
    }
    $result['events'][]=['route'=>'/p/menu/info','business_code'=>0,
        'known_row_count'=>1,'known_row_field_types'=>array_map('gettype',$item)];
    $json=cdxRoleMenuPost($base,'/p/menu/treeMenu',[],$token);
    $tree=$json['data']??null;
    $own=is_array($tree)?cdxRoleMenuFind($tree,$categoryMenu):[];
    if (($json['code']??null)!==0 || count($own)!==1
        || ($own[0]['puuid']??null)!==$menuParent
        || !array_key_exists('iconText',$own[0])
        || array_key_exists('icontext',$own[0])) {
        throw new RuntimeException('Formal menu/treeMenu own page or iconText contract mismatch');
    }
    $result['events'][]=['route'=>'/p/menu/treeMenu','business_code'=>0,
        'known_page_count'=>1,'known_page_field_types'=>array_map('gettype',$own[0])];
    $json=cdxRoleMenuPost($base,'/p/menu/getButtons',
        ['menu_uuid'=>$menuParent],$token);
    $items=$json['data']??null;
    $own=is_array($items)?array_values(array_filter($items,
        static function ($row) use ($categoryMenu): bool {
            return is_array($row) && ($row['uuid']??null)===$categoryMenu;
        })):[];
    if (($json['code']??null)!==0 || count($own)!==1
        || ($own[0]['puuid']??null)!==$menuParent) {
        throw new RuntimeException('Formal menu/getButtons own child missing');
    }
    $result['events'][]=['route'=>'/p/menu/getButtons','business_code'=>0,
        'known_child_count'=>1,'known_child_field_types'=>array_map('gettype',$own[0])];
    if ($menuListMenu!=='') {
        $json=cdxRoleMenuPost($base,'/p/menu/lists',
            ['name'=>$menuName,'page'=>1],$token);
        // Menu::lists returns MenuLogic::lists()['data'] with rows/total.
        $items=$json['data']['rows']??null;
        $own=is_array($items)?array_values(array_filter($items,
            static function ($row) use ($categoryMenu): bool {
                return is_array($row) && ($row['uuid']??null)===$categoryMenu;
            })):[];
        if (($json['code']??null)!==0 || count($own)!==1) {
            throw new RuntimeException('Formal menu/lists known row missing');
        }
        $result['events'][]=['route'=>'/p/menu/lists','business_code'=>0,
            'known_row_count'=>1,'known_row_field_types'=>array_map('gettype',$own[0])];
    } else {
        $result['events'][]=['route'=>'/p/menu/lists',
            'status'=>'blocked-permission','reason'=>'no exact menu filter'];
    }
} catch (Throwable $error) {
    $result['failure']=['phase'=>$phase,'class'=>get_class($error),
        'code'=>$error->getCode()];
} finally {
    try {
        $cleanup=$open();
        $links=cdxRoleMenuRows($cleanup,'SELECT ID,MENU_UUID,CREATE_BY FROM '
            .$roleMenuTable.' WHERE ROLE_UUID=?',[$roleUuid]);
        if (count($links)>count($granted)) {
            throw new RuntimeException('Unexpected role-menu link count; refuse cleanup');
        }
        foreach ($links as $link) {
            if (!in_array((string)$link['menu_uuid'],$granted,true)
                || (string)$link['create_by']!==$marker) {
                throw new RuntimeException('Unexpected role-menu link; refuse cleanup');
            }
            $delete=$cleanup->prepare('DELETE FROM '.$roleMenuTable
                .' WHERE ID=? AND ROLE_UUID=? AND MENU_UUID=? AND CREATE_BY=?');
            $delete->execute([(int)$link['id'],$roleUuid,
                (string)$link['menu_uuid'],$marker]);
            $result['cleanup']['role_menu_deleted'] =
                ($result['cleanup']['role_menu_deleted']??0)+$delete->rowCount();
        }
        if (cdxRoleMenuCount($cleanup,'SELECT COUNT(*) N FROM '.$userRoleTable
            .' WHERE ROLE_UUID=? AND USER_UUID<>?',[$roleUuid,$userUuid])!==0) {
            throw new RuntimeException('Other user linked to test role; refuse cleanup');
        }
        $delete=$cleanup->prepare('DELETE FROM '.$userRoleTable
            .' WHERE USER_UUID=? AND ROLE_UUID=? AND CREATE_BY=?');
        $delete->execute([$userUuid,$roleUuid,$marker]);
        $result['cleanup']['user_role_deleted']=$delete->rowCount();
        if ($roleId<=0) {
            $found=cdxRoleMenuRows($cleanup,'SELECT ID FROM '.$roleTable
                .' WHERE UUID=? AND NAME=? AND CREATE_BY=?',
                [$roleUuid,$marker,'CODEX']);
            if (count($found)>1) throw new RuntimeException('Role cardinality unsafe');
            $roleId=$found?(int)$found[0]['id']:0;
        }
        if ($userId<=0) {
            $found=cdxRoleMenuRows($cleanup,'SELECT ID FROM '.$userTable
                .' WHERE UUID=? AND ACCOUNT=? AND TYPE=1',[$userUuid,$marker]);
            if (count($found)>1) throw new RuntimeException('User cardinality unsafe');
            $userId=$found?(int)$found[0]['id']:0;
        }
        if ($roleId>0) {
            $delete=$cleanup->prepare('DELETE FROM '.$roleTable
                .' WHERE ID=? AND UUID=? AND NAME=? AND CREATE_BY=?');
            $delete->execute([$roleId,$roleUuid,$marker,'CODEX']);
            $result['cleanup']['role_deleted']=$delete->rowCount();
        }
        if ($userId>0) {
            $delete=$cleanup->prepare('DELETE FROM '.$userTable
                .' WHERE ID=? AND UUID=? AND ACCOUNT=? AND TYPE=1');
            $delete->execute([$userId,$userUuid,$marker]);
            $result['cleanup']['user_deleted']=$delete->rowCount();
        }
        $result['cleanup']['role_zero']=cdxRoleMenuCount($cleanup,
            'SELECT COUNT(*) N FROM '.$roleTable.' WHERE UUID=? OR NAME=?',
            [$roleUuid,$marker])===0;
        $result['cleanup']['user_zero']=cdxRoleMenuCount($cleanup,
            'SELECT COUNT(*) N FROM '.$userTable.' WHERE UUID=? OR ACCOUNT=?',
            [$userUuid,$marker])===0;
        $result['cleanup']['links_zero']=cdxRoleMenuCount($cleanup,
            'SELECT COUNT(*) N FROM '.$roleMenuTable.' WHERE ROLE_UUID=?',
            [$roleUuid])===0
            && cdxRoleMenuCount($cleanup,'SELECT COUNT(*) N FROM '.$userRoleTable
                .' WHERE USER_UUID=? AND ROLE_UUID=?',[$userUuid,$roleUuid])===0;
    } catch (Throwable $error) {
        $result['cleanup_error']=['class'=>get_class($error),
            'code'=>$error->getCode()];
    }
}
echo json_encode($result,JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT).PHP_EOL;
if (isset($result['failure']) || isset($result['cleanup_error'])
    || empty($result['cleanup']['role_zero'])
    || empty($result['cleanup']['user_zero'])
    || empty($result['cleanup']['links_zero'])) exit(1);
