<?php
declare(strict_types=1);date_default_timezone_set('Asia/Shanghai');$lane=getenv('CORE_LANE');$dm=getenv('POC_DIALECT')==='dm';$out=__DIR__.'/private';if(!is_dir($out))mkdir($out,0700);$file=$out.'/preflight-'.$lane.'.json';if(file_exists($file))throw new RuntimeException('Never overwrite readonly preflight');$events=[];$pdo=[];$errors=[];$check=static function($ok,$m){if(!$ok)throw new RuntimeException($m);};
foreach(['AI','ZHCT'] as $db){$prefix=getenv('POC_'.$db.'_PREFIX');$check($prefix===($db==='AI'?'yoshop_':'ydy_'),'Exact two business prefixes');$dsn=$dm?'dm:host=127.0.0.1;port='.getenv('POC_'.$db.'_PORT').';schema='.getenv('POC_'.$db.'_SCHEMA'):'mysql:host='.getenv('POC_'.$db.'_HOST').';port='.getenv('POC_'.$db.'_PORT').';dbname='.getenv('POC_'.$db.'_SCHEMA').';charset='.getenv('POC_'.$db.'_CHARSET');$pdo[$db]=new PDO($dsn,getenv('POC_'.$db.'_USER'),getenv('POC_'.$db.'_PASSWORD'),[PDO::ATTR_CASE=>PDO::CASE_LOWER,PDO::ATTR_ERRMODE=>PDO::ERRMODE_EXCEPTION,PDO::ATTR_ORACLE_NULLS=>PDO::NULL_NATURAL,PDO::ATTR_STRINGIFY_FETCHES=>false,PDO::ATTR_EMULATE_PREPARES=>false]);}
$q=static function($db,$sql,$params=[],$secret=false)use(&$events,$pdo,$check){$check(preg_match('/^SELECT\b/',$sql)===1&&!preg_match('/;|--|\/\*|\b(?:INSERT|UPDATE|DELETE|REPLACE|MERGE|CREATE|ALTER|DROP|TRUNCATE|INTO|CALL|GRANT|REVOKE|SLEEP)\b/i',$sql),'SELECT-only native gate');$s=$pdo[$db]->prepare($sql);$bind=[];foreach(array_values($params) as $i=>$v){$s->bindValue($i+1,(string)$v,PDO::PARAM_STR);$bind[]=['position'=>$i+1,'value'=>(string)$v,'pdo_type'=>PDO::PARAM_STR];}$s->execute();$rows=$s->fetchAll(PDO::FETCH_ASSOC);foreach($rows as &$r){foreach($r as &$v)if(is_resource($v))$v=stream_get_contents($v);unset($v);ksort($r);}unset($r);$events[]=['connection'=>$db,'operation'=>'SELECT','actual_native_queryString'=>$s->queryString,'bindValue'=>$bind,'execute_success'=>true,'rowCount'=>$s->rowCount(),'rows'=>$secret?'REDACTED_CONFIG_VALUE_NOT_PERSISTED':$rows];return $rows;};
$identity=[];$schema=[];$tables=['AI'=>['user','user_balance_log','account_settlement','user_subsidy','user_gift_log'],'ZHCT'=>['user','staff','restaurants','prepay_order','meal_order','meal_order_pickup','staff_department','department','config','one_card_account_map','consume_limit','consume_limit_department','consume_limit_scenario','consume_limit_scope','consume_limit_account','consume_limit_block_log','marketing_strategy']];
foreach($tables as $db=>$list){$identity[$db]=$q($db,$dm?"SELECT SYS_CONTEXT('USERENV','CURRENT_SCHEMA') schema_name FROM DUAL":'SELECT DATABASE() schema_name,VERSION() version,@@SESSION.sql_mode sql_mode,@@character_set_connection connection_charset');$check($identity[$db][0]['schema_name']===getenv('POC_'.$db.'_SCHEMA'),'Exact approved database identity');if(!$dm)$check(strpos($identity[$db][0]['version'],'5.6.')===0,'Original source MySQL56');foreach($list as $t){$n=getenv('POC_'.$db.'_PREFIX').$t;$u=strtoupper($n);$col=$q($db,$dm?'SELECT COLUMN_NAME,DATA_TYPE,NULLABLE,DATA_DEFAULT,DATA_LENGTH,DATA_PRECISION,DATA_SCALE FROM USER_TAB_COLUMNS WHERE TABLE_NAME=? ORDER BY COLUMN_ID':'SELECT COLUMN_NAME,DATA_TYPE,COLUMN_TYPE,IS_NULLABLE,COLUMN_DEFAULT,CHARACTER_MAXIMUM_LENGTH,NUMERIC_PRECISION,NUMERIC_SCALE,EXTRA FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=? ORDER BY ORDINAL_POSITION',[$dm?$u:$n]);$s=['columns'=>$col];$s['indexes']=$q($db,$dm?'SELECT INDEX_NAME,COLUMN_NAME,COLUMN_POSITION,DESCEND FROM USER_IND_COLUMNS WHERE TABLE_NAME=? ORDER BY INDEX_NAME,COLUMN_POSITION':'SELECT INDEX_NAME,NON_UNIQUE,SEQ_IN_INDEX,COLUMN_NAME,COLLATION,SUB_PART FROM INFORMATION_SCHEMA.STATISTICS WHERE TABLE_SCHEMA=DATABASE() AND TABLE_NAME=? ORDER BY INDEX_NAME,SEQ_IN_INDEX',[$dm?$u:$n]);$s['triggers']=$q($db,$dm?'SELECT * FROM USER_TRIGGERS WHERE TABLE_NAME=?':'SELECT TRIGGER_NAME,EVENT_MANIPULATION,ACTION_TIMING,ACTION_STATEMENT FROM INFORMATION_SCHEMA.TRIGGERS WHERE TRIGGER_SCHEMA=DATABASE() AND EVENT_OBJECT_TABLE=?',[$dm?$u:$n]);$s['FK']=$q($db,$dm?"SELECT CONSTRAINT_NAME,TABLE_NAME,R_CONSTRAINT_NAME FROM USER_CONSTRAINTS WHERE CONSTRAINT_TYPE='R' AND (TABLE_NAME=? OR R_CONSTRAINT_NAME IN (SELECT CONSTRAINT_NAME FROM USER_CONSTRAINTS WHERE TABLE_NAME=?))":'SELECT TABLE_NAME,CONSTRAINT_NAME,COLUMN_NAME,REFERENCED_TABLE_NAME,REFERENCED_COLUMN_NAME FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE TABLE_SCHEMA=DATABASE() AND REFERENCED_TABLE_NAME IS NOT NULL AND (TABLE_NAME=? OR REFERENCED_TABLE_NAME=?)',[$dm?$u:$n,$dm?$u:$n]);$schema[$db][$t]=$s;}}
$collisions=[];foreach([['AI','user','nick_name'],['ZHCT','user','name'],['ZHCT','staff','name'],['ZHCT','restaurants','name'],['ZHCT','prepay_order','goods_name']] as [$db,$t,$field]){try{$collisions[$db.'.'.$t]=$q($db,'SELECT COUNT(*) n FROM '.getenv('POC_'.$db.'_PREFIX').$t.' WHERE '.$field.' LIKE ?',['CDXCORE11S1%'])[0]['n'];}catch(Throwable $e){$errors[]=['scope'=>$db.'.'.$t,'message'=>$e->getMessage()];}}
$config=$q('ZHCT','SELECT config_value FROM ydy_config WHERE config_key=? AND status=1 ORDER BY id',['one_card'],true);$value=$config[0]['config_value']??null;$decoded=is_string($value)?json_decode($value,true):null;$raw=$decoded['enable']??false;$enabled=is_bool($raw)?$raw:(is_int($raw)?$raw===1:in_array(strtolower(trim((string)$raw)),['1','true','yes','on'],true));$configFacts=['one_card_row_count'=>count($config),'one_card_json_valid'=>is_array($decoded),'one_card_enable_normalized'=>$enabled,'one_card_business_nonempty'=>trim((string)($decoded['business']??''))!==''];unset($value,$decoded,$config,$raw);
$order=$q('ZHCT','SELECT config_value FROM ydy_config WHERE config_key=? AND restaurant_id=? AND status=1',['account_deduction_order',1],true);$seq=json_decode($order[0]['config_value']??'',true);$valid=is_array($seq)&&count($seq)===3&&count(array_unique($seq))===3&&!array_diff($seq,['subsidy','cash','gift']);$configFacts['deduction_order']=$valid?array_values($seq):['subsidy','cash','gift'];$configFacts['deduction_order_uses_fallback']=!$valid;unset($seq,$order);
$ids=$lane==='B'?[44676,97274,107491]:[1836,2642,2182];$old=[];$old['ai_user']=$q('AI','SELECT user_id,store_id,nick_name,balance,gift_balance,pay_money,is_delete FROM yoshop_user WHERE user_id=? AND nick_name LIKE ?',[$ids[0],'CDXPARMOB%']);$old['store_user']=$q('ZHCT','SELECT id,ai_user_id,staff_uuid,is_delete FROM ydy_user WHERE id=? AND ai_user_id=?',[$ids[1],$ids[0]]);$old['store_staff']=$q('ZHCT','SELECT id,uuid,ai_user_id,is_show,identity_category_id,name FROM ydy_staff WHERE id=? AND ai_user_id=?',[$ids[2],$ids[0]]);$uuid=$old['store_staff'][0]['uuid']??'';
if($uuid!==''){$old['departments']=$q('ZHCT','SELECT staff_uuid,department_uuid FROM ydy_staff_department WHERE staff_uuid=?',[$uuid]);$old['one_card_maps']=$q('ZHCT','SELECT COUNT(*) n FROM ydy_one_card_account_map WHERE (user_id=? OR staff_uuid=?) AND acc_num<>?',[$ids[1],$uuid,'']);}
$old['active_subsidy']=$q('AI','SELECT id,user_id,store_id,money,used_money,expiration_start_time,expiration_end_time FROM yoshop_user_subsidy WHERE user_id=? AND is_delete=0 AND expiration_start_time<=? AND expiration_end_time>=? ORDER BY id',[$ids[0],date('Y-m-d H:i:s'),date('Y-m-d H:i:s')]);$old['settlements']=$q('AI','SELECT settlement_id,settlement_no,biz_order_no,amount,cash_amount,gift_amount,subsidy_amount,status,user_id,store_id FROM yoshop_account_settlement WHERE user_id=? ORDER BY settlement_id',[$ids[0]]);
$result=['lane'=>$lane,'HTTP'=>0,'DML'=>0,'DDL'=>0,'SELECT'=>count($events),'identities'=>$identity,'schema'=>$schema,'marker_collision_counts'=>$collisions,'config_facts'=>$configFacts,'old_marked_mobile_candidate_readonly'=>$old,'errors'=>$errors,'native_SELECT_events'=>$events,'new_actor_OneCard_safe_not_yet_proven'=>true];file_put_contents($file,json_encode($result,JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE|JSON_UNESCAPED_SLASHES|JSON_PRESERVE_ZERO_FRACTION));chmod($file,0600);echo json_encode(['lane'=>$lane,'HTTP'=>0,'DML'=>0,'DDL'=>0,'SELECT'=>count($events),'tables'=>count($tables['AI'])+count($tables['ZHCT']),'marker_collision_counts'=>$collisions,'one_card_enabled'=>$configFacts['one_card_enable_normalized'],'metadata_errors'=>count($errors),'new_actor_not_created'=>true]),"\n";
