<?php

declare(strict_types=1);

// REVIEW DRAFT: five existing read routes; only synthetic type-1 permission
// rows may be written and then precisely removed. No business-table DML.
if ((string)getenv('CDXDEC5_EXECUTE')!=='ROOT_APPROVED_EXACT_SHA') {
    fwrite(STDERR,"Review draft; execution disabled\n");
    exit(2);
}
$lane=(string)getenv('CDXDEC5_LANE');
$runtimes=[
    'A'=>['/tmp/zhct-http-store-a.l5omH8','http://127.0.0.1:18097',
        'af3250f90a91','c17edaae86e4',
        'ef66402980430ecb9875969f91c94e009c36c303e42bc910a5747628ce823563',
        '5df8d4831fb02dcb1188c21de186915760972806a67e97490c5cfb457d885be8',
        null,21023],
    'B'=>['/tmp/zhct-http-store-mysql.dQTTBP','http://127.0.0.1:18081',
        'af3250f90a91','c17edaae86e4',
        '5fd50f1f81e3d1826f2c24997671ade216b55e1e7a3d15aa820fce1cf4dd8089',
        'cca46e5480723aa71e338c7ffc607cd49875f7180fc8d20145a338810c36ee1d',
        '337c4e404f2c185b61e809e454acaca24f551f8917fcd08160c0b026495e5ff1',
        10001],
    'C'=>['/tmp/zhct-dm-poc-20260928.fcpiiN/store','http://127.0.0.1:18080',
        '2a23c2721fd0','766cb53c753b',
        '5fd50f1f81e3d1826f2c24997671ade216b55e1e7a3d15aa820fce1cf4dd8089',
        'cca46e5480723aa71e338c7ffc607cd49875f7180fc8d20145a338810c36ee1d',
        '337c4e404f2c185b61e809e454acaca24f551f8917fcd08160c0b026495e5ff1',
        112175],
];
if (!isset($runtimes[$lane])
    || (string)getenv('POC_APP_ROOT')!==$runtimes[$lane][0]
    || rtrim((string)getenv('POC_BASE_URL'),'/')!==$runtimes[$lane][1]
    || (string)getenv('POC_DIALECT')!==($lane==='C'?'dm':'mysql')) {
    fwrite(STDERR,"Approved isolated lane required\n");
    exit(2);
}
function cdxDec5Uuid(): 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 cdxDec5Rows(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 cdxDec5Count(PDO $db,string $sql,array $args=[]): int
{
    return (int)(cdxDec5Rows($db,$sql,$args)[0]['n']??-1);
}
function cdxDec5Post(string $base,string $route,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.$route,false,$context);
    $heads=$http_response_header??[];
    preg_match('/HTTP\/\S+\s+(\d+)/',(string)($heads[0]??''),$match);
    $json=is_string($body)?json_decode($body,true):null;
    return ['http_status'=>isset($match[1])?(int)$match[1]:0,
        'body_bytes'=>is_string($body)?strlen($body):null,
        'json'=>is_array($json)?$json:null];
}
function cdxDec5BoundaryBarrier(string $base): bool
{
    $context=stream_context_create(['http'=>['method'=>'GET',
        'timeout'=>25,'ignore_errors'=>true]]);
    $body=@file_get_contents($base.'/__poc_boundary',false,$context);
    $headers=$http_response_header??[];
    if (strpos((string)($headers[0]??''),' 200 ')===false
        || !is_string($body)) return false;
    $json=json_decode($body,true);
    return is_array($json) && !empty($json['external_stubs'])
        && !empty($json['redis_isolated']) && empty($json['real_queue']);
}
function cdxDec5Find($node,string $field,string $target): ?array
{
    if (!is_array($node)) return null;
    if (isset($node[$field]) && (string)$node[$field]===$target) return $node;
    foreach ($node as $child) {
        $found=cdxDec5Find($child,$field,$target);
        if ($found!==null) return $found;
    }
    return null;
}
function cdxDec5SafeName(string $name): bool
{
    if ($name==='' || mb_strlen($name)>50
        || preg_match('/[\x00-\x1f]/u',$name)) return false;
    foreach (["'",'"','\\','%','_'] as $unsafe) {
        if (strpos($name,$unsafe)!==false) return false;
    }
    return true;
}
function cdxDec5Canon($node)
{
    if (!is_array($node)) return $node;
    if (array_keys($node)!==range(0,count($node)-1)) ksort($node,SORT_STRING);
    foreach ($node as $key=>$value) $node[$key]=cdxDec5Canon($value);
    return $node;
}
function cdxDec5Types($node,string $path,array $safe,array &$out,int $depth=0): void
{
    if ($depth>12) return;
    $out[$path][gettype($node)]=true;
    if (!is_array($node)) return;
    foreach ($node as $key=>$value) {
        if (is_int($key) || ctype_digit((string)$key)) {
            $segment='*';
        } elseif (isset($safe[(string)$key])) {
            $segment=(string)$key;
        } else {
            $segment='{dynamic_key}';
        }
        cdxDec5Types($value,$path.'.'.$segment,$safe,$out,$depth+1);
    }
}
function cdxDec5DecimalShape(array $row,array $fields): array
{
    $result=['checked'=>0,'fixed_2dp'=>0,'not_fixed_2dp'=>[]];
    foreach ($fields as $field) {
        if (!array_key_exists($field,$row)) continue;
        // Legacy findCater deliberately maps zero raw_flag_weight to ''.
        if ($field==='raw_flag_weight' && $row[$field]==='') continue;
        $result['checked']++;
        if (is_string($row[$field])
            && preg_match('/^-?\d+\.\d{2}$/',$row[$field])) {
            $result['fixed_2dp']++;
        } else {
            $result['not_fixed_2dp'][]=$field;
        }
    }
    return $result;
}
function cdxDec5HasUnique(PDO $db,string $lane,string $schema,
    string $table,array $expected): bool
{
    if ($lane==='C') {
        $rows=cdxDec5Rows($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=cdxDec5Rows($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;
}

$root=$runtimes[$lane][0];
$base=$runtimes[$lane][1];
$result=['lane'=>$lane,'marker'=>null,'cases'=>[],
    'business_dml'=>0,'cleanup'=>[]];
$userUuid=cdxDec5Uuid();
$roleUuid=cdxDec5Uuid();
$marker='CDXPARDEC'.strtoupper(bin2hex(random_bytes(4)));
$result['marker']=$marker;
$userId=0;
$roleId=0;
$phase='preflight';
$ambiguousHttp=false;
$menus=[
    'C5E8BD15-C890-11DF-183B-0A5444A528EF'=>'p/cater/select',
    '2CBDEFF2-EADF-628F-7CBD-05A16D3183C7'=>'p/cater/find',
    '39288C13-FDD4-7F47-35E6-3D9B35F9CB2C'=>'p/food/lists',
    '352B7629-DC3D-4C69-9032-DE025F25090C'=>'p/food/readbyid',
];
try {
    $sourceFiles=[
        $root.'/application/p/logic/Cater.php'=>$runtimes[$lane][4],
        $root.'/application/p/logic/Food.php'=>$runtimes[$lane][5],
    ];
    if ($runtimes[$lane][6]!==null) {
        $sourceFiles[$root.'/application/common/service/DecimalResponseFormatter.php']
            =$runtimes[$lane][6];
    } elseif (is_file($root.'/application/common/service/DecimalResponseFormatter.php')) {
        throw new RuntimeException('A original unexpectedly has formatter');
    }
    foreach ($sourceFiles as $file=>$sha) {
        if (!is_file($file) || hash_file('sha256',$file)!==$sha) {
            throw new RuntimeException('Unexpected isolated source revision');
        }
    }
    $pid=(string)getenv('POC_RUNTIME_PID');
    $cmdline=ctype_digit($pid)
        ? (string)file_get_contents('/proc/'.$pid.'/cmdline') : '';
    $port=$lane==='A'?'18097':($lane==='B'?'18081':'18080');
    if (!ctype_digit($pid) || (int)$pid!==$runtimes[$lane][7]
        || strpos($cmdline,'127.0.0.1:'.$port)===false
        || strpos($cmdline,$root.'/public')===false) {
        throw new RuntimeException('Isolated single PHP server PID/port/docroot mismatch');
    }
    $workers='';
    foreach (explode("\0",(string)file_get_contents('/proc/'.$pid.'/environ')) as $entry) {
        if (strpos($entry,'PHP_CLI_SERVER_WORKERS=')===0) {
            $workers=substr($entry,strlen('PHP_CLI_SERVER_WORKERS='));
        }
    }
    if ($workers!=='' && (!ctype_digit($workers) || (int)$workers>1)) {
        throw new RuntimeException('PHP CLI server worker count not serial');
    }
    $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('Isolation boundary missing');
    }
    if ($lane==='C') {
        $env=array_fill_keys(['POC_HOST','POC_PORT','POC_SCHEMA',
            'POC_USER','POC_PASSWORD'],'');
        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],$env)) {
                $env[$pair[0]]=$pair[1];
            }
        }
        if ($env['POC_HOST']!=='127.0.0.1' || $env['POC_PORT']!=='5237'
            || strtoupper($env['POC_SCHEMA'])!=='ZHCT_APP'
            || strtoupper($env['POC_USER'])!=='ZHCT_APP'
            || $env['POC_PASSWORD']==='') {
            throw new RuntimeException('Unexpected C database target');
        }
        $open=static function () use ($env): PDO {
            return new PDO('dm:host=127.0.0.1;port=5237;schema=ZHCT_APP',
                $env['POC_USER'],$env['POC_PASSWORD'],[
                    PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,
                    PDO::ATTR_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC,
                ]);
        };
        $prefix='YDY_';
    } else {
        $localPath=realpath(__DIR__
            . '/../../../../../zhctproject/store/application/config/local/database.php');
        if (!$localPath || strpos($localPath,
            '/zhctproject/store/application/config/local/')===false) {
            throw new RuntimeException('Code local config absent');
        }
        $load=static function(string $path): array { return require $path; };
        $local=$load($localPath);
        $selected=$load($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 not code local DB');
            }
        }
        if ((int)($local['hostport']??3306)
            !==(int)($selected['hostport']??3306)) {
            throw new RuntimeException('Runtime DB port mismatch');
        }
        $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_DEFAULT_FETCH_MODE=>PDO::FETCH_ASSOC,
                    PDO::ATTR_TIMEOUT=>8,
                ]);
        };
        $prefix='ydy_';
    }
    $db=$open();
    if ($lane==='C') {
        if (strtoupper((string)$db->query(
            "SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")
            ->fetchColumn())!=='ZHCT_APP') {
            throw new RuntimeException('Wrong C schema');
        }
    } elseif (strpos((string)$db->query('SELECT VERSION()')->fetchColumn(),'5.6.')===false) {
        throw new RuntimeException('Wrong MySQL version');
    }
    $safe=['code'=>true,'message'=>true,'data'=>true,'rows'=>true,
        'total'=>true,'tag'=>true,'category_name'=>true,
        'minute_sort_name'=>true,'reclassify_name'=>true,
        'custom_category_name'=>true,'custom_category_status'=>true,
        'custom_category_is_system'=>true];
    $decimal=[];
    foreach (['dishes','dish','foods_composition'] as $name) {
        if ($lane==='C') {
            $stmt=$db->prepare('SELECT COLUMN_NAME,DATA_TYPE,DATA_SCALE'
                .' FROM USER_TAB_COLUMNS WHERE TABLE_NAME=?');
            $stmt->execute([strtoupper($prefix.$name)]);
        } else {
            $stmt=$db->prepare('SELECT COLUMN_NAME,DATA_TYPE,NUMERIC_SCALE DATA_SCALE'
                .' FROM information_schema.COLUMNS'
                .' WHERE TABLE_SCHEMA=? AND TABLE_NAME=?');
            $stmt->execute([$local['database'],$prefix.$name]);
        }
        $fields=$stmt->fetchAll(PDO::FETCH_ASSOC);
        if (!$fields) throw new RuntimeException('Required source table absent');
        foreach ($fields as $field) {
            $item=array_change_key_case($field,CASE_LOWER);
            $key=strtolower((string)$item['column_name']);
            $safe[$key]=true;
            if (strtolower((string)$item['data_type'])==='decimal') {
                if ((int)$item['data_scale']!==2) {
                    throw new RuntimeException('Unexpected DECIMAL scale');
                }
                $decimal[$name][]=$key;
            }
        }
    }
    if (count($decimal['dishes']??[])!==39
        || count($decimal['foods_composition']??[])!==37) {
        throw new RuntimeException('DECIMAL whitelist metadata changed');
    }
    $required=[
        'user'=>['id','uuid','account','pwd','salt','name','type','status',
            'create_time','create_by','is_delete'],
        'role'=>['id','uuid','name','status','create_time','create_by'],
        'user_role'=>['id','user_uuid','role_uuid','create_time','create_by'],
        'role_menu'=>['id','role_uuid','menu_uuid','create_time','create_by'],
    ];
    $inserted=[
        'user'=>['uuid','account','pwd','salt','name','type','status',
            'create_time','create_by','is_delete'],
        'role'=>['uuid','name','status','create_time','create_by'],
        'user_role'=>['user_uuid','role_uuid','create_time','create_by'],
        'role_menu'=>['role_uuid','menu_uuid','create_time','create_by'],
    ];
    foreach ($required as $name=>$fields) {
        $full=$prefix.$name;
        if ($lane==='C') {
            $columns=cdxDec5Rows($db,
                'SELECT COLUMN_NAME,NULLABLE,DATA_DEFAULT FROM USER_TAB_COLUMNS'
                .' WHERE TABLE_NAME=?',[strtoupper($full)]);
            $triggers=cdxDec5Count($db,
                'SELECT COUNT(*) N FROM USER_TRIGGERS WHERE TABLE_NAME=?',
                [strtoupper($full)]);
            $fks=cdxDec5Count($db,"SELECT COUNT(*) N FROM USER_CONSTRAINTS"
                ." WHERE TABLE_NAME=? AND CONSTRAINT_TYPE='R'",[strtoupper($full)]);
        } else {
            $columns=cdxDec5Rows($db,
                'SELECT COLUMN_NAME,IS_NULLABLE,COLUMN_DEFAULT'
                .' FROM information_schema.COLUMNS WHERE TABLE_SCHEMA=?'
                .' AND TABLE_NAME=?',[$local['database'],$full]);
            $triggers=cdxDec5Count($db,
                'SELECT COUNT(*) N FROM information_schema.TRIGGERS'
                .' WHERE TRIGGER_SCHEMA=? AND EVENT_OBJECT_TABLE=?',
                [$local['database'],$full]);
            $fks=cdxDec5Count($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'],$full]);
        }
        $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('Permission fixture schema/side-effect gate failed');
        }
        foreach ($columns as $column) {
            $field=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 ($field!=='id' && $notNull && $default===null
                && !in_array($field,$inserted[$name],true)) {
                throw new RuntimeException('Permission insert omits required field');
            }
        }
    }
    $indexSchema=$lane==='C'?'ZHCT_APP':(string)$local['database'];
    if (!cdxDec5HasUnique($db,$lane,$indexSchema,$prefix.'role',['name'])
        || !cdxDec5HasUnique($db,$lane,$indexSchema,
            $prefix.'user_role',['user_uuid','role_uuid'])
        || !cdxDec5HasUnique($db,$lane,$indexSchema,
            $prefix.'role_menu',['role_uuid','menu_uuid'])) {
        throw new RuntimeException('Permission unique-key gate failed');
    }
    foreach ($menus as $uuid=>$route) {
        $rows=cdxDec5Rows($db,'SELECT FILTER FROM '.$prefix.'menu'
            .' WHERE UUID=?',[$uuid]);
        $parts=count($rows)===1
            ? array_map(static function($part): string {
                return trim(strtolower($part),' /');
            },explode('|',(string)$rows[0]['filter'])) : [];
        if (count($rows)!==1 || !in_array($route,$parts,true)) {
            throw new RuntimeException('Exact type1 read menu absent');
        }
    }
    foreach (['user'=>'account','role'=>'name','user_role'=>'create_by',
        'role_menu'=>'create_by'] as $table=>$column) {
        if (cdxDec5Count($db,'SELECT COUNT(*) N FROM '.$prefix.$table
            .' WHERE '.$column.' LIKE ?',['CDXPARDEC%'])!==0) {
            throw new RuntimeException('Prior DEC5 marker residue');
        }
    }
    $pairs=cdxDec5Rows($db,'SELECT DISTINCT d.UUID D_UUID,d.NAME D_NAME,'
        .'f.ID F_ID,f.NAME F_NAME FROM '.$prefix.'dishes d'
        .' JOIN '.$prefix.'dish c ON c.DISHES_UUID=d.UUID'
        .' JOIN '.$prefix.'foods_composition f ON f.ID=c.COMPOSITION_ID'
        .' WHERE d.STATUS=1 AND d.IS_DEL=1 AND d.IS_DELETE=0'
        .' AND d.STORE_ID=10001 AND f.IS_DELETE=0');
    $matches=[];
    foreach ($pairs as $pair) {
        $uuid=(string)$pair['d_uuid'];
        $foodId=(int)$pair['f_id'];
        if (substr(hash('sha256',$uuid),0,12)===$runtimes[$lane][2]
            && substr(hash('sha256',(string)$foodId),0,12)===$runtimes[$lane][3]) {
            $matches[]=['uuid'=>$uuid,'dish_name'=>(string)$pair['d_name'],
                'food_id'=>$foodId,'food_name'=>(string)$pair['f_name']];
        }
    }
    if (count($matches)!==1
        || !cdxDec5SafeName($matches[0]['dish_name'])
        || !cdxDec5SafeName($matches[0]['food_name'])) {
        throw new RuntimeException('Original anonymous natural relation changed');
    }
    $source=$matches[0];
    $sameDish=cdxDec5Count($db,'SELECT COUNT(*) N FROM '.$prefix.'dishes'
        .' WHERE NAME=? AND STATUS=1 AND IS_DEL=1 AND STORE_ID=10001',
        [$source['dish_name']]);
    $like='%'.$source['food_name'].'%';
    $foodMatches=cdxDec5Count($db,'SELECT COUNT(*) N FROM '
        .$prefix.'foods_composition WHERE NAME LIKE ? OR SUB_NAME LIKE ?',
        [$like,$like]);
    if ($sameDish!==1 || $foodMatches<1 || $foodMatches>100) {
        throw new RuntimeException('Natural name filter no longer bounded/unique');
    }
    $before=cdxDec5Count($db,'SELECT COUNT(*) N FROM '.$prefix.'dish'
        .' WHERE DISHES_UUID=? AND COMPOSITION_ID=?',
        [$source['uuid'],$source['food_id']]);
    if ($before<=0) throw new RuntimeException('Source component missing');
    $result['source_hashes']=['dish_uuid_sha12'=>$runtimes[$lane][2],
        'food_id_sha12'=>$runtimes[$lane][3],
        'component_relation_nonempty'=>true];

    $phase='permission_fixture';
    $now=date('Y-m-d H:i:s');
    $db->prepare('INSERT INTO '.$prefix.'user'
        .' (UUID,ACCOUNT,PWD,SALT,NAME,TYPE,STATUS,CREATE_TIME,CREATE_BY,IS_DELETE)'
        .' VALUES (?,?,?,?,?,1,1,?,?,0)')
        ->execute([$userUuid,$marker,'DISABLED',
            strtoupper(bin2hex(random_bytes(8))),$marker,$now,'CODEX']);
    $users=cdxDec5Rows($open(),'SELECT ID,TYPE FROM '.$prefix.'user'
        .' WHERE UUID=? AND ACCOUNT=?',[$userUuid,$marker]);
    if (count($users)!==1 || (int)$users[0]['type']!==1) {
        throw new RuntimeException('Own type1 user readback failed');
    }
    $userId=(int)$users[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=cdxDec5Post($base,'/p/cater/select',
        ['keyword'=>$source['dish_name'],'page'=>1],$token);
    if ($denied['http_status']!==200 || !is_array($denied['json'])) {
        $ambiguousHttp=true;
    }
    if (($denied['json']['code']??null)!==1) {
        throw new RuntimeException('No-menu type1 negative failed');
    }
    $db->prepare('INSERT INTO '.$prefix.'role'
        .' (UUID,NAME,STATUS,CREATE_TIME,CREATE_BY) VALUES (?,?,1,?,?)')
        ->execute([$roleUuid,$marker,$now,'CODEX']);
    $roles=cdxDec5Rows($open(),'SELECT ID FROM '.$prefix.'role'
        .' WHERE UUID=? AND NAME=?',[$roleUuid,$marker]);
    if (count($roles)!==1) throw new RuntimeException('Own role readback failed');
    $roleId=(int)$roles[0]['id'];
    $db->prepare('INSERT INTO '.$prefix.'user_role'
        .' (USER_UUID,ROLE_UUID,CREATE_TIME,CREATE_BY) VALUES (?,?,?,?)')
        ->execute([$userUuid,$roleUuid,$now,$marker]);
    foreach ($menus as $uuid=>$route) {
        $db->prepare('INSERT INTO '.$prefix.'role_menu'
            .' (ROLE_UUID,MENU_UUID,CREATE_TIME,CREATE_BY) VALUES (?,?,?,?)')
            ->execute([$roleUuid,$uuid,$now,$marker]);
    }
    if (cdxDec5Count($open(),'SELECT COUNT(*) N FROM '.$prefix.'role_menu'
        .' WHERE ROLE_UUID=? AND CREATE_BY=?',[$roleUuid,$marker])!==4) {
        throw new RuntimeException('Own menu grants not exact');
    }
    $result['permission']=['type1_no_menu_code'=>1,'read_menu_links'=>4];

    $phase='formal_http5';
    $cases=[
        ['/p/cater/select',['keyword'=>$source['dish_name'],'status'=>1,'page'=>1],
            'dish_list'],
        ['/p/cater/selectAll',['keyword'=>$source['dish_name'],'status'=>1,'simple'=>0],
            'dish_list'],
        ['/p/cater/find',['uuid'=>$source['uuid'],'flag'=>1],'dish_detail'],
        ['/p/food/lists',['name'=>$source['food_name'],'page'=>1,'pagesize'=>100],
            'food_list'],
        ['/p/food/readById',['id'=>$source['food_id']],'food_detail'],
    ];
    foreach ($cases as $case) {
        [$route,$form,$kind]=$case;
        $response=cdxDec5Post($base,$route,$form,$token);
        $json=$response['json'];
        $data=is_array($json)?($json['data']??null):null;
        if ($kind==='dish_list') {
            $row=cdxDec5Find($data['rows']??[],'uuid',$source['uuid']);
            $fields=$decimal['dishes'];
        } elseif ($kind==='dish_detail') {
            $row=is_array($data)&&($data['uuid']??null)===$source['uuid']
                ?$data:null;
            $fields=$decimal['dishes'];
        } elseif ($kind==='food_list') {
            $row=cdxDec5Find($data['rows']??[],'id',(string)$source['food_id']);
            $fields=$decimal['foods_composition'];
        } else {
            $row=is_array($data)&&(int)($data['id']??0)===$source['food_id']
                ?$data:null;
            $fields=$decimal['foods_composition'];
        }
        $types=[];
        cdxDec5Types($json,'root',$safe,$types);
        ksort($types,SORT_STRING);
        foreach ($types as $path=>$set) {
            $types[$path]=array_keys($set);
            sort($types[$path],SORT_STRING);
        }
        $format=is_array($row)?cdxDec5DecimalShape($row,$fields):null;
        $pass=$response['http_status']===200
            && is_array($json) && ($json['code']??null)===0
            && is_array($row) && $format!==null
            && $format['checked']>0 && !$format['not_fixed_2dp'];
        $result['cases'][]=['route'=>$route,
            'status'=>$pass?'tested-pass':'tested-fail',
            'http_status'=>$response['http_status'],
            'business_code'=>is_array($json)?($json['code']??null):null,
            'body_bytes'=>$response['body_bytes'],
            'target_row_found'=>is_array($row),
            'decimal_format'=>$format,
            'response_sha256'=>is_array($json)
                ?hash('sha256',json_encode(cdxDec5Canon($json),
                    JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE)):null,
            'type_paths'=>$types];
        if ($response['http_status']!==200 || !is_array($json)) {
            $ambiguousHttp=true;
            break;
        }
    }
    $verify=$open();
    $after=cdxDec5Count($verify,'SELECT COUNT(*) N FROM '.$prefix.'dish'
        .' WHERE DISHES_UUID=? AND COMPOSITION_ID=?',
        [$source['uuid'],$source['food_id']]);
    $result['source_hashes']['component_count_unchanged']=$after===$before;
} catch (Throwable $error) {
    $result['failure']=['phase'=>$phase,'class'=>get_class($error),
        'code'=>$error->getCode()];
} finally {
    try {
        $cleanup=$open();
        $links=cdxDec5Rows($cleanup,'SELECT ID,MENU_UUID,CREATE_BY FROM '
            .$prefix.'role_menu WHERE ROLE_UUID=?',[$roleUuid]);
        if (count($links)>count($menus)) {
            throw new RuntimeException('Role-menu cardinality unsafe');
        }
        foreach ($links as $link) {
            if (!array_key_exists((string)$link['menu_uuid'],$menus)
                || (string)$link['create_by']!==$marker) {
                throw new RuntimeException('Unexpected role-menu link');
            }
            $stmt=$cleanup->prepare('DELETE FROM '.$prefix.'role_menu'
                .' WHERE ID=? AND ROLE_UUID=? AND MENU_UUID=? AND CREATE_BY=?');
            $stmt->execute([(int)$link['id'],$roleUuid,
                (string)$link['menu_uuid'],$marker]);
            if ($stmt->rowCount()!==1) throw new RuntimeException('Role-menu delete failed');
            $result['cleanup']['role_menu_deleted']=
                ($result['cleanup']['role_menu_deleted']??0)+1;
        }
        if (cdxDec5Count($cleanup,'SELECT COUNT(*) N FROM '.$prefix.'user_role'
            .' WHERE ROLE_UUID=? AND USER_UUID<>?',[$roleUuid,$userUuid])!==0) {
            throw new RuntimeException('Other user linked synthetic role');
        }
        $stmt=$cleanup->prepare('DELETE FROM '.$prefix.'user_role'
            .' WHERE USER_UUID=? AND ROLE_UUID=? AND CREATE_BY=?');
        $stmt->execute([$userUuid,$roleUuid,$marker]);
        $result['cleanup']['user_role_deleted']=$stmt->rowCount();
        if ($ambiguousHttp) {
            // Tokens are self-contained; removing an actor row does not revoke
            // the encrypted token. Remove only this role's grants first so
            // subsequent function-permission checks deny it, then use the
            // verified single-worker server as a request-completion barrier.
            $revoked=cdxDec5Count($cleanup,'SELECT COUNT(*) N FROM '
                .$prefix.'role_menu WHERE ROLE_UUID=?',[$roleUuid])===0
                && cdxDec5Count($cleanup,'SELECT COUNT(*) N FROM '
                    .$prefix.'user_role WHERE USER_UUID=? AND ROLE_UUID=?',
                    [$userUuid,$roleUuid])===0;
            $result['cleanup']['own_menu_capability_revoked']=$revoked;
            if (!$revoked) {
                throw new RuntimeException('Own role grants not revoked; leave actor for audit');
            }
            $barrier=cdxDec5BoundaryBarrier($base);
            $result['cleanup']['single_worker_barrier_passed']=$barrier;
            if (!$barrier) {
                // Keep role/user rows with the unique marker for manual,
                // exact cleanup. Never race an in-flight HTTP read.
                $result['cleanup']['actor_cleanup_deferred']=true;
                throw new RuntimeException('HTTP completion unconfirmed; actor cleanup deferred');
            }
        }
        if ($roleId<=0) {
            $found=cdxDec5Rows($cleanup,'SELECT ID FROM '.$prefix.'role'
                .' 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=cdxDec5Rows($cleanup,'SELECT ID FROM '.$prefix.'user'
                .' 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) {
            $stmt=$cleanup->prepare('DELETE FROM '.$prefix.'role'
                .' WHERE ID=? AND UUID=? AND NAME=? AND CREATE_BY=?');
            $stmt->execute([$roleId,$roleUuid,$marker,'CODEX']);
            if ($stmt->rowCount()!==1) throw new RuntimeException('Role delete failed');
            $result['cleanup']['role_deleted']=1;
        }
        if ($userId>0) {
            $stmt=$cleanup->prepare('DELETE FROM '.$prefix.'user'
                .' WHERE ID=? AND UUID=? AND ACCOUNT=? AND TYPE=1');
            $stmt->execute([$userId,$userUuid,$marker]);
            if ($stmt->rowCount()!==1) throw new RuntimeException('User delete failed');
            $result['cleanup']['user_deleted']=1;
        }
        $result['cleanup']['user_zero']=cdxDec5Count($cleanup,
            'SELECT COUNT(*) N FROM '.$prefix.'user WHERE UUID=? OR ACCOUNT=?',
            [$userUuid,$marker])===0;
        $result['cleanup']['role_zero']=cdxDec5Count($cleanup,
            'SELECT COUNT(*) N FROM '.$prefix.'role WHERE UUID=? OR NAME=?',
            [$roleUuid,$marker])===0;
        $result['cleanup']['links_zero']=cdxDec5Count($cleanup,
            'SELECT COUNT(*) N FROM '.$prefix.'role_menu WHERE ROLE_UUID=?',
            [$roleUuid])===0
            && cdxDec5Count($cleanup,'SELECT COUNT(*) N FROM '
                .$prefix.'user_role 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']['user_zero'])
    || empty($result['cleanup']['role_zero'])
    || empty($result['cleanup']['links_zero'])
    || empty($result['source_hashes']['component_count_unchanged'])) exit(1);
foreach ($result['cases'] as $case) {
    if ($case['status']!=='tested-pass') exit(1);
}
