<?php

declare(strict_types=1);

// REVIEW DRAFT: five existing, unauthenticated Restaurant read routes only.
// No DML, no permission fixture, no business names/values in output.
if ((string)getenv('CDXRESTCONTRACT_EXECUTE')!=='ROOT_APPROVED_EXACT_SHA') {
    fwrite(STDERR,"Review draft; execution disabled\n");
    exit(2);
}
$lane=(string)getenv('CDXRESTCONTRACT_LANE');
$runtime=[
    'A'=>['/tmp/zhct-http-store-a.l5omH8','http://127.0.0.1:18097',
        'af3250f90a91','62ac5b7c9e3f',
        'ed292b0e4eadc4d50c66409276d3a65e1f55d627d0526826d8ca272131078222',
        'cd7146232df2e70361f61a38c179e0dc8e20a1fb975a90d60035b2e26f461a5e'],
    'B'=>['/tmp/zhct-http-store-mysql.dQTTBP','http://127.0.0.1:18081',
        'af3250f90a91','62ac5b7c9e3f',
        '7add87f50896ae3b94c00017f485eb3efaf473d5ee2f81c0c6120d4e85a287ab',
        'dbe6b5464c40ca6cb732fab45c96cd54c05834c071234aba11b5af48af89b871'],
    'C'=>['/tmp/zhct-dm-poc-20260928.fcpiiN/store','http://127.0.0.1:18080',
        '2a23c2721fd0','fee7778c69b6',
        '7add87f50896ae3b94c00017f485eb3efaf473d5ee2f81c0c6120d4e85a287ab',
        'dbe6b5464c40ca6cb732fab45c96cd54c05834c071234aba11b5af48af89b871'],
];
if (!isset($runtime[$lane])
    || (string)getenv('POC_APP_ROOT')!==$runtime[$lane][0]
    || rtrim((string)getenv('POC_BASE_URL'),'/')!==$runtime[$lane][1]
    || (string)getenv('POC_DIALECT')!==($lane==='C'?'dm':'mysql')) {
    fwrite(STDERR,"Approved isolated lane required\n");
    exit(2);
}
function cdxRestRows(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 cdxRestCount(PDO $db,string $sql,array $args=[]): int
{
    return (int)(cdxRestRows($db,$sql,$args)[0]['n']??-1);
}
function cdxRestPost(string $base,string $route,array $form): array
{
    $context=stream_context_create(['http'=>[
        'method'=>'POST',
        'header'=>"Content-Type: application/x-www-form-urlencoded\r\n",
        'content'=>http_build_query($form,'','&',PHP_QUERY_RFC3986),
        'timeout'=>20,'ignore_errors'=>true,
    ]]);
    $body=@file_get_contents($base.$route,false,$context);
    $headers=$http_response_header??[];
    preg_match('/HTTP\/\S+\s+(\d+)/',(string)($headers[0]??''),$match);
    $json=is_string($body)?json_decode($body,true):null;
    return ['http_status'=>isset($match[1])?(int)$match[1]:0,
        'json'=>is_array($json)?$json:null,
        'body_bytes'=>is_string($body)?strlen($body):null];
}
function cdxRestCanonical($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]=cdxRestCanonical($value);
    return $node;
}
function cdxRestTypes($node,string $path,array &$out,int $depth=0): void
{
    if ($depth>12) return;
    $type=gettype($node);
    $out[$path][$type]=true;
    if (!is_array($node)) return;
    $safeKeys=['code','message','data','map','datex','table','sum','avg',
        'title','detail','long','map_val','x','ehcarts','name','energy',
        'weight','file_path','popularity','dishes_uuid','menu_date',
        'meal_times','type','category','number','restaurant_id',
        'staff_num','meal_type_num','uuid'];
    foreach ($node as $key=>$value) {
        if (is_int($key) || ctype_digit((string)$key)) {
            $segment='*';
        } elseif (in_array((string)$key,$safeKeys,true)) {
            $segment=(string)$key;
        } else {
            // Some chart keys are actual dish names; never emit them.
            $segment='{dynamic_key}';
        }
        cdxRestTypes($value,$path.'.'.$segment,$out,$depth+1);
    }
}
function cdxRestContains($node,string $needle): bool
{
    if (is_string($node)) return $node===$needle;
    if (!is_array($node)) return false;
    foreach ($node as $value) {
        if (cdxRestContains($value,$needle)) return true;
    }
    return false;
}
function cdxRestFindName($node,string $needle): ?array
{
    if (!is_array($node)) return null;
    if (($node['name']??null)===$needle) return $node;
    foreach ($node as $value) {
        $found=cdxRestFindName($value,$needle);
        if ($found!==null) return $found;
    }
    return null;
}

$root=$runtime[$lane][0];
$base=$runtime[$lane][1];
$result=['lane'=>$lane,'routes'=>[],'business_dml'=>0,
    'permission_fixture_dml'=>0];
try {
    foreach ([
        $root.'/application/model/Restaurant.php'=>$runtime[$lane][4],
        $root.'/application/p/logic/Restaurant.php'=>$runtime[$lane][5],
    ] as $file=>$sha) {
        if (!is_file($file) || hash_file('sha256',$file)!==$sha) {
            throw new RuntimeException('Unexpected Restaurant source revision');
        }
    }
    if ($lane==='C') {
        $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/docroot mismatch');
        }
    }
    $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');
        }
        $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 {
        $path=realpath(__DIR__
            . '/../../../../../zhctproject/store/application/config/local/database.php');
        if (!$path || strpos($path,
            '/zhctproject/store/application/config/local/')===false) {
            throw new RuntimeException('Approved code local config missing');
        }
        $load=static function (string $file): array { return require $file; };
        $local=$load($path);
        $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 development 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');
    }
    foreach (['user'=>'account','role'=>'name','user_role'=>'create_by',
        'role_menu'=>'create_by'] as $table=>$column) {
        if (cdxRestCount($db,'SELECT COUNT(*) N FROM '.$prefix.$table
            .' WHERE '.$column.' LIKE ?',['CDXPARCFRO%'])!==0) {
            throw new RuntimeException('Prior synthetic permission marker not clean');
        }
    }
    $valid='1,2,3,4,5,7,8,9,10,11,12,13,14,15,16,17,18,19,20,'
        .'21,22,23,24,25,26,27,28,29,34,35,36';
    $sql='SELECT DISTINCT d.UUID D_UUID,d.NAME D_NAME,'
        .' r.MENU_DATE R_DATE,r.RESTAURANT_ID R_ID'
        .' 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'
        .' JOIN '.$prefix.'recipes r ON r.DISHES_UUID=d.UUID'
        .' WHERE d.STATUS=1 AND d.IS_DEL=1 AND d.IS_DELETE=0'
        .' AND d.STORE_ID=10001 AND d.WEIGHT>0 AND d.ENERGY>0'
        .' AND d.CATEGORY BETWEEN 1 AND 6 AND c.IS_DELETE=0'
        .' AND f.IS_DELETE=0 AND c.MINUTE_SORT IN ('.$valid.')'
        .' AND r.MEAL_TIMES=1 AND EXISTS(SELECT 1 FROM '.$prefix.'date_menu dm'
        .' WHERE dm.DISHES_UUID=r.DISHES_UUID'
        .' AND dm.MENU_DATE=r.MENU_DATE'
        .' AND dm.RESTAURANT_ID=r.RESTAURANT_ID'
        .' AND dm.MEAL_TIMES=1 AND dm.MEAL_TYPE=3)';
    $matches=[];
    foreach (cdxRestRows($db,$sql) as $row) {
        $uuid=(string)$row['d_uuid'];
        $day=substr((string)$row['r_date'],0,10);
        if (substr(hash('sha256',$uuid),0,12)===$runtime[$lane][2]
            && substr(hash('sha256',$day),0,12)===$runtime[$lane][3]) {
            $matches[]=['uuid'=>$uuid,'day'=>$day,
                'restaurant_id'=>(int)$row['r_id'],
                'name'=>(string)$row['d_name']];
        }
    }
    if (count($matches)!==1
        || !preg_match('/^\d{4}-\d{2}-\d{2}$/',$matches[0]['day'])
        || $matches[0]['restaurant_id']<=0 || $matches[0]['name']==='') {
        throw new RuntimeException('Original anonymous context changed/ambiguous');
    }
    $source=$matches[0];
    $recipes=cdxRestCount($db,'SELECT COUNT(*) N FROM '.$prefix.'recipes'
        .' WHERE DISHES_UUID=? AND MENU_DATE=? AND RESTAURANT_ID=?'
        .' AND MEAL_TIMES=1',
        [$source['uuid'],$source['day'],$source['restaurant_id']]);
    $menus=cdxRestCount($db,'SELECT COUNT(*) N FROM '.$prefix.'date_menu'
        .' WHERE DISHES_UUID=? AND MENU_DATE=? AND RESTAURANT_ID=?'
        .' AND MEAL_TIMES=1 AND MEAL_TYPE=3',
        [$source['uuid'],$source['day'],$source['restaurant_id']]);
    if ($recipes<=0 || $menus<=0) {
        throw new RuntimeException('Natural relation no longer nonempty');
    }
    $routes=[
        ['/p/restaurant/richness',[
            'start_time'=>$source['day'],'end_time'=>$source['day'],
            'restaurant_id'=>$source['restaurant_id'],'meal_times'=>1]],
        ['/p/restaurant/foodStructure',[
            'start_time'=>$source['day'],'end_time'=>$source['day'],
            'restaurant_id'=>$source['restaurant_id'],'meal_times'=>1]],
        ['/p/restaurant/nutrition',[
            'start_time'=>$source['day'],'end_time'=>$source['day'],
            'restaurant_id'=>$source['restaurant_id'],'meal_times'=>1,'status'=>1]],
        ['/p/restaurant/daypopularity',[
            'menu_date'=>$source['day'],'restaurant_id'=>$source['restaurant_id'],
            'meal_times'=>1]],
        ['/p/restaurant/morepopularity',[
            'start_time'=>$source['day'],'end_time'=>$source['day'],
            'restaurant_id'=>$source['restaurant_id'],'meal_times'=>1]],
    ];
    foreach ($routes as $routeSpec) {
        [$route,$form]=$routeSpec;
        $response=cdxRestPost($base,$route,$form);
        $json=$response['json'];
        $data=is_array($json)?($json['data']??null):null;
        if ($route==='/p/restaurant/daypopularity') {
            $index=is_array($data['x']??null)
                ? array_search($source['name'],$data['x'],true):false;
            $target=$index!==false
                && is_numeric($data['map_val'][$index]??null)
                && (float)$data['map_val'][$index]>0;
        } elseif ($route==='/p/restaurant/morepopularity') {
            $item=cdxRestFindName($data['table']??[],$source['name']);
            $target=$item!==null && is_numeric($item['popularity']??null)
                && (float)$item['popularity']>0;
        } elseif ($route==='/p/restaurant/richness') {
            $target=is_array($data)&&isset($data['map'],$data['datex'],$data['table']);
        } else {
            $target=cdxRestContains($data,$source['name']);
        }
        $types=[];
        cdxRestTypes($json,'root',$types);
        ksort($types,SORT_STRING);
        foreach ($types as $path=>$set) {
            $values=array_keys($set);
            sort($values,SORT_STRING);
            $types[$path]=$values;
        }
        $result['routes'][]=[
            'route'=>$route,
            'status'=>$response['http_status']===200
                && is_array($json) && ($json['code']??null)===0 && $target
                ?'tested-pass':'tested-fail',
            'http_status'=>$response['http_status'],
            'business_code'=>is_array($json)?($json['code']??null):null,
            'target_in_response'=>$target,
            'body_bytes'=>$response['body_bytes'],
            'canonical_response_sha256'=>is_array($json)
                ?hash('sha256',json_encode(cdxRestCanonical($json),
                    JSON_UNESCAPED_SLASHES|JSON_UNESCAPED_UNICODE)):null,
            'type_paths'=>$types,
        ];
        if ($response['http_status']!==200 || !is_array($json)) break;
    }
    $verify=$open();
    $afterRecipes=cdxRestCount($verify,'SELECT COUNT(*) N FROM '.$prefix.'recipes'
        .' WHERE DISHES_UUID=? AND MENU_DATE=? AND RESTAURANT_ID=?'
        .' AND MEAL_TIMES=1',
        [$source['uuid'],$source['day'],$source['restaurant_id']]);
    $afterMenus=cdxRestCount($verify,'SELECT COUNT(*) N FROM '.$prefix.'date_menu'
        .' WHERE DISHES_UUID=? AND MENU_DATE=? AND RESTAURANT_ID=?'
        .' AND MEAL_TIMES=1 AND MEAL_TYPE=3',
        [$source['uuid'],$source['day'],$source['restaurant_id']]);
    $result['source_counts_unchanged']=$afterRecipes===$recipes
        && $afterMenus===$menus;
    foreach (['user'=>'account','role'=>'name','user_role'=>'create_by',
        'role_menu'=>'create_by'] as $table=>$column) {
        if (cdxRestCount($verify,'SELECT COUNT(*) N FROM '.$prefix.$table
            .' WHERE '.$column.' LIKE ?',['CDXPARCFRO%'])!==0) {
            throw new RuntimeException('Synthetic permission marker appeared');
        }
    }
    echo json_encode($result,JSON_UNESCAPED_SLASHES|JSON_PRETTY_PRINT).PHP_EOL;
    if (!$result['source_counts_unchanged']) exit(1);
    foreach ($result['routes'] as $item) {
        if ($item['status']!=='tested-pass') exit(1);
    }
} catch (Throwable $error) {
    echo json_encode(['lane'=>$lane,'status'=>'error',
        'class'=>get_class($error),'code'=>$error->getCode()]).PHP_EOL;
    exit(1);
}
