<?php

declare(strict_types=1);

// C nonproduction SELECT/metadata only. No business HTTP, actor or DML.
if ($argc!==2 || $argv[1]!=='--preflight-c-bill4') {
    fwrite(STDERR,"Explicit C Bill read-only preflight flag required\n");
    exit(2);
}
$phase='runtime-boundary';
try {
    $pid=(string)getenv('POC_RUNTIME_PID');
    $root='/tmp/zhct-dm-poc-20260928.fcpiiN/store';
    if ($pid!=='112175') throw new RuntimeException('Wrong C runtime PID');
    $cmd=str_replace("\0",' ',(string)file_get_contents('/proc/'.$pid.'/cmdline'));
    if (strpos($cmd,' -S 127.0.0.1:18080 ')===false
        || strpos($cmd,$root.'/public')===false
        || substr_count($cmd,' -S ')!==1) {
        throw new RuntimeException('Wrong C isolated HTTP process');
    }
    $env=array_fill_keys(['POC_HOST','POC_PORT','POC_SCHEMA',
        'POC_USER','POC_PASSWORD','PHP_CLI_SERVER_WORKERS'],'');
    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']===''
        || !in_array($env['PHP_CLI_SERVER_WORKERS'],['','1'],true)) {
        throw new RuntimeException('Wrong nonproduction C DB/worker');
    }
    $db=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,
        ]);
    if (strtoupper((string)$db->query(
        "SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') FROM DUAL")
        ->fetchColumn())!=='ZHCT_APP') {
        throw new RuntimeException('Wrong current schema');
    }
    $scalar=static function(string $sql,array $args=[]) use ($db): int {
        $stmt=$db->prepare($sql);
        $stmt->execute($args);
        return (int)$stmt->fetchColumn();
    };

    $phase='columns';
    $required=[
        'YDY_RECIPES'=>['ID','UUID','DISHES_UUID','MENU_DATE',
            'RESTAURANT_ID','MEAL_TIMES','LIMIT_NUM'],
        'YDY_DISHES'=>['UUID','NAME','CATEGORY','FILE_PATH','PRICE',
            'IS_DEL','STATUS','STORE_ID'],
        'YDY_DATE_MENU'=>['DISHES_UUID','MENU_DATE','RESTAURANT_ID',
            'MEAL_TIMES','MEAL_TYPE'],
        'YDY_RESTAURANTS'=>['ID','UUID','STATUS','IS_DELETE'],
        'YDY_MENU'=>['UUID','FILTER','LINK'],
        'YDY_USER'=>['ID','UUID','ACCOUNT','TYPE','STATUS'],
        'YDY_ROLE'=>['ID','UUID','NAME','STATUS'],
        'YDY_USER_ROLE'=>['USER_UUID','ROLE_UUID'],
        'YDY_ROLE_MENU'=>['ROLE_UUID','MENU_UUID'],
        'YDY_USER_DATA'=>['USER_UUID','DATA_UUID','TYPE'],
    ];
    $columnStmt=$db->prepare('SELECT COLUMN_NAME FROM USER_TAB_COLUMNS'
        .' WHERE TABLE_NAME=?');
    $missing=[];
    foreach ($required as $table=>$columns) {
        $columnStmt->execute([$table]);
        $actual=array_map('strtoupper',$columnStmt->fetchAll(PDO::FETCH_COLUMN));
        $delta=array_values(array_diff($columns,$actual));
        if ($delta!==[]) $missing[$table]=$delta;
    }
    if ($missing!==[]) {
        echo json_encode(['status'=>'blocked-schema','missing'=>$missing],
            JSON_UNESCAPED_SLASHES).PHP_EOL;
        exit(3);
    }
    $permissionTables=['YDY_USER','YDY_ROLE','YDY_USER_ROLE',
        'YDY_ROLE_MENU','YDY_USER_DATA'];
    $permission=[];
    foreach ($permissionTables as $table) {
        $permission[$table]=[
            'triggers'=>$scalar('SELECT COUNT(*) FROM USER_TRIGGERS'
                .' WHERE TABLE_NAME=?',[$table]),
            'foreign_keys'=>$scalar('SELECT COUNT(*) FROM USER_CONSTRAINTS'
                .' WHERE TABLE_NAME=? AND CONSTRAINT_TYPE=\'R\'',[$table]),
        ];
    }
    $expectedUnique=[
        'YDY_ROLE'=>['NAME'],
        'YDY_USER_ROLE'=>['USER_UUID','ROLE_UUID'],
        'YDY_ROLE_MENU'=>['ROLE_UUID','MENU_UUID'],
        'YDY_USER_DATA'=>['USER_UUID','DATA_UUID','TYPE'],
    ];
    $indexStmt=$db->prepare('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');
    foreach ($expectedUnique as $table=>$columns) {
        $indexStmt->execute([$table]);
        $indexes=[];
        foreach ($indexStmt->fetchAll(PDO::FETCH_ASSOC) as $row) {
            $row=array_change_key_case($row,CASE_LOWER);
            $name=(string)$row['index_name'];
            $indexes[$name]['unique']=strtoupper((string)$row['uniqueness'])==='UNIQUE';
            $indexes[$name]['columns'][]=strtoupper((string)$row['column_name']);
        }
        $found=false;
        foreach ($indexes as $index) {
            if ($index['unique'] && $index['columns']===$columns) $found=true;
        }
        $permission[$table]['required_unique_present']=$found;
    }
    $markerCounts=[];
    foreach (['YDY_USER'=>'ACCOUNT','YDY_ROLE'=>'NAME',
        'YDY_USER_ROLE'=>'CREATE_BY','YDY_ROLE_MENU'=>'CREATE_BY'] as $table=>$column) {
        $markerCounts[$table]=$scalar('SELECT COUNT(*) FROM '.$table
            .' WHERE '.$column.' LIKE ?', ['CDXPARBILL%']);
    }

    $phase='natural-recipe';
    $candidateSql='SELECT r.MENU_DATE MENU_DATE,r.RESTAURANT_ID RESTAURANT_ID,'
        .' r.MEAL_TIMES MEAL_TIMES,r.DISHES_UUID DISHES_UUID,'
        .' st.UUID RESTAURANT_UUID FROM YDY_RECIPES r'
        .' JOIN YDY_DISHES d ON d.UUID=r.DISHES_UUID'
        .' JOIN YDY_RESTAURANTS st ON st.ID=r.RESTAURANT_ID'
        .' WHERE d.IS_DEL=1 AND d.STATUS=1 AND d.STORE_ID=10001'
        .' AND st.STATUS=1 AND st.IS_DELETE=2'
        .' AND r.MEAL_TIMES BETWEEN 1 AND 4 AND EXISTS('
        .' SELECT 1 FROM YDY_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=r.MEAL_TIMES AND dm.MEAL_TYPE=3)'
        .' ORDER BY r.MENU_DATE DESC FETCH FIRST 1 ROWS ONLY';
    $row=$db->query($candidateSql)->fetch(PDO::FETCH_ASSOC);
    $row=$row?array_change_key_case($row,CASE_LOWER):null;
    if (!$row) throw new RuntimeException('No natural recipe/actual menu');
    $date=substr((string)$row['menu_date'],0,10);
    if (!preg_match('/^\d{4}-\d{2}-\d{2}$/',$date)) {
        throw new RuntimeException('Invalid natural menu date');
    }
    $restaurant=(int)$row['restaurant_id'];
    $meal=(int)$row['meal_times'];
    $counts=[
        'recipe_day_restaurant_meal'=>$scalar('SELECT COUNT(*) FROM YDY_RECIPES'
            .' WHERE MENU_DATE=? AND RESTAURANT_ID=? AND MEAL_TIMES=?',
            [$date,$restaurant,$meal]),
        'distinct_recipe_dates_restaurant'=>$scalar('SELECT COUNT(DISTINCT MENU_DATE)'
            .' FROM YDY_RECIPES WHERE RESTAURANT_ID=?',[$restaurant]),
        'active_copyable_recipe_day'=>$scalar('SELECT COUNT(*)'
            .' FROM YDY_RECIPES r JOIN YDY_DISHES d ON d.UUID=r.DISHES_UUID'
            .' WHERE r.MENU_DATE=? AND r.RESTAURANT_ID=?'
            .' AND r.MEAL_TIMES=? AND d.STORE_ID=10001'
            .' AND d.IS_DEL=1 AND d.STATUS=1',
            [$date,$restaurant,$meal]),
    ];

    $phase='exact-bill-menu';
    $menuStmt=$db->prepare('SELECT UUID,FILTER,LINK FROM YDY_MENU'
        .' WHERE LOWER(FILTER) LIKE ?');
    $menuStmt->execute(['%p/bill/look_bill_list%']);
    $matches=[];
    foreach ($menuStmt->fetchAll(PDO::FETCH_ASSOC) as $menu) {
        $menu=array_change_key_case($menu,CASE_LOWER);
        $filter='/'.trim(strtolower((string)$menu['filter']),' /');
        if ($filter==='/p/bill/look_bill_list') {
            $matches[]=substr(hash('sha256',(string)$menu['uuid']),0,12);
        }
    }
    echo json_encode([
        'status'=>'read-only-preflight','schema'=>'ZHCT_APP',
        'required_tables'=>count($required),'missing_columns'=>0,
        'permission_metadata'=>$permission,
        'permission_marker_counts'=>$markerCounts,
        'candidate'=>[
            'relation_sha12'=>substr(hash('sha256',
                (string)$row['dishes_uuid'].'|'.$date.'|'.$restaurant.'|'.$meal),0,12),
            'date_sha12'=>substr(hash('sha256',$date),0,12),
            'meal_times'=>$meal,
            'restaurant_sha12'=>substr(hash('sha256',(string)$row['restaurant_uuid']),0,12),
        ],
        'counts'=>$counts,
        'bill_menu_exact_count'=>count($matches),
        'bill_menu_sha12'=>$matches,
    ],JSON_UNESCAPED_SLASHES).PHP_EOL;
    if ($counts['recipe_day_restaurant_meal']<1
        || $counts['distinct_recipe_dates_restaurant']<1
        || $counts['active_copyable_recipe_day']<1
        || count($matches)!==1 || array_sum($markerCounts)!==0) exit(1);
} catch (Throwable $error) {
    echo json_encode(['status'=>'blocked','phase'=>$phase,
        'class'=>get_class($error),
        'code'=>(string)$error->getCode()],JSON_UNESCAPED_SLASHES).PHP_EOL;
    exit(1);
}
