<?php
declare(strict_types=1);require_once __DIR__.'/contract.php';
function n7Rows(PDO $db,string $table,array $fields,string $where='',array $args=[],string $order='',int $limit=0):array
{
 $meta=smLoad('source-mysql-metadata-private.json')['metadata'];$key=$table==='foods_composition'?'food':$table;$types=[];foreach($meta[$key] as $column){$types[strtolower($column['column_name'])]=$column;}
 if($fields===['*']){$fields=array_keys($types);}$dm=getenv('SM7_LANE')==='C';$select=[];
 foreach($fields as $field){if(!isset($types[$field])||!preg_match('/^[a-z][a-z0-9_]*$/D',$field)){throw new RuntimeException('Frozen source projection');}$q=$dm?'"'.strtoupper($field).'"':'`'.$field.'`';$expression=$dm&&$types[$field]['data_type']==='decimal'?'CAST('.$q.' AS VARCHAR(200))':$q;$select[]=$expression.' AS '.$q;}
 $sql='SELECT '.implode(',',$select).' FROM ydy_'.$table.($where!==''?' WHERE '.$where:'').($order!==''?' ORDER BY '.$order:'');if($limit){$sql=smTop($sql,$limit);}$rows=smRows($db,$sql,$args);if(count($rows)>100){throw new RuntimeException('Consumed relation exceeds frozen 100 cap');}
 if($dm){foreach($rows as &$row){foreach($row as $field=>&$value){$type=$types[$field];if($value===null){continue;}if($type['data_type']==='decimal'){if(!is_string($value)||!preg_match('/^-?\d+(?:\.\d+)?$/D',$value)){throw new RuntimeException('Exact source decimal text absent');}$scale=(int)$type['numeric_scale'];$fixed=bcadd($value,'0',$scale);if(bccomp($value,$fixed,max($scale,strlen(strrchr($value,'.')?:'')))!==0){throw new RuntimeException('Target decimal outside source scale');}$value=$fixed;}elseif(in_array($type['data_type'],['datetime','timestamp'],true)&&(int)$type['datetime_precision']===0&&is_string($value)&&preg_match('/^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2})\.0{1,6}$/D',$value,$match)){$value=$match[1];}elseif($type['data_type']==='float'&&$type['numeric_scale']!==null&&is_numeric($value)){$value=round((float)$value,(int)$type['numeric_scale']);}}unset($value);}unset($row);}
 return $rows;
}
function n7Page(array $rows,int $page,int $size=5):array{$total=count($rows);$last=(int)ceil($total/$size);return ['total'=>$total,'per_page'=>$size,'current_page'=>$page>$last?($last>0?$last:1):max(1,$page),'last_page'=>$last,'data'=>array_slice($rows,($page-1)*$size,$size)];}
function n7OwnedRestaurant(PDO $db):array{$b=getenv('SM7_LANE')==='B';$id=$b?77:12;$marker=$b?'CDXCENTB3C677BA4':'CDXCENTC789071DA';$rows=n7Rows($db,'restaurants',['id','uuid','name','create_by','is_delete'],'id=?',[$id]);if(count($rows)!==1||strpos((string)$rows[0]['name'],$marker)!==0){throw new RuntimeException('Retained owned restaurant identity drift');}return $rows;}
function n7NoCredentials($value,string $key=''):bool{
 if(preg_match('/password|passwd|pwd|secret|token|private.?key|access.?key|api.?key|^zp.*key$/i',$key)&&$value!==null&&$value!==''){return false;}
 if(is_array($value)||is_object($value)){foreach((array)$value as $name=>$child){if(!n7NoCredentials($child,(string)$name)){return false;}}}
 elseif(is_string($value)){if(preg_match('#^https?://#i',$value)){if(parse_url($value,PHP_URL_USER)!==null||parse_url($value,PHP_URL_PASS)!==null||preg_match('/(?:password|access_token|api_key|client_secret)=/i',$value)){return false;}}$decoded=json_decode($value);if(json_last_error()===JSON_ERROR_NONE&&(is_object($decoded)||is_array($decoded))&&!n7NoCredentials($decoded)){return false;}}
 return true;
}
function n7SafeOwnedStar(PDO $db,array $rows):void{$owned=n7OwnedRestaurant($db)[0];if(count($rows)>1){throw new RuntimeException('Only one exact owned star row is authorized');}foreach($rows as $row){if($row['id']!==$owned['id']||$row['uuid']!==$owned['uuid']||$row['name']!==$owned['name']||!n7NoCredentials($row)){throw new RuntimeException('Owned credential-safe star guard rejected');}}}
function n7CanonicalRestaurantRow(array $row):array{
 $metadata=smLoad('source-mysql-metadata-private.json')['metadata']['restaurants'];$names=[];foreach($metadata as $column){$names[strtolower($column['column_name'])]=$column['column_name'];}$canonical=[];foreach($row as $key=>$value){$canonical[$names[$key]??$key]=$value;}
 if(getenv('SM7_LANE')==='C'&&isset($canonical['create_time'])&&is_string($canonical['create_time'])&&preg_match('/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{1,6}$/D',$canonical['create_time'])&&(int)substr($canonical['create_time'],0,4)>=1000){$date=DateTimeImmutable::createFromFormat('!Y-m-d H:i:s.u',$canonical['create_time']);if($date&&$date->format('Y-m-d H:i:s')===substr($canonical['create_time'],0,19)){$canonical['create_time']=$date->format('Y-m-d H:i:s');}}
 return $canonical;
}
function n7RestaurantImage(PDO $db,$path,array &$sets):string{$rows=n7Rows($db,'config',['id','config_key','config_value'],"status=1 AND config_key IN ('ossDisjunctor','ossHost','businessIdentify')",[],'id');$map=[];foreach($rows as $r){if(array_key_exists($r['config_key'],$map)){throw new RuntimeException('URL config key duplicate');}$map[$r['config_key']]=$r['config_value'];}$sets['url_config']=$rows;$prefix=($map['ossHost']??'').'/'.(!empty($map['ossDisjunctor'])?($map['businessIdentify']??'').'/':'');return empty($path)?'':str_replace('\\','/',$prefix.$path);}
function n7DishSource(PDO $db,string $keyword,array &$sets):array
{
 $foods=n7Rows($db,'foods_composition',['*'],'name LIKE ? OR sub_name LIKE ?',['%'.$keyword.'%','%'.$keyword.'%']);$sets['foods']=$foods;$components=[];$uuids=[];
 if($foods){$ids=array_column($foods,'id');$components=n7Rows($db,'dish',['*'],'composition_ratio>10 AND composition_id IN ('.implode(',',array_fill(0,count($ids),'?')).')',$ids);$uuids=array_values(array_unique(array_column($components,'dishes_uuid')));}$sets['components']=$components;
 $where='is_del=1 AND status=1 AND (name LIKE ?';$args=['%'.$keyword.'%'];if($uuids){$where.=' OR uuid IN ('.implode(',',array_fill(0,count($uuids),'?')).')';$args=array_merge($args,$uuids);}$where.=')';return [$where,$args];
}
function n7Snapshot(PDO $db,array $case):array
{
 $kind=$case['kind'];$params=$case['params'];$sets=['actor'=>catActor($db)];$body=[];
 if($kind==='search'){
  [$where,$args]=n7DishSource($db,(string)$params['keyword'],$sets);$rows=n7Rows($db,'dishes',['id','uuid','name'],$where,$args,'id ASC');$sets['dishes']=$rows;$body=['code'=>0,'message'=>'搜索菜品成功','data'=>['list'=>$rows]];
 }elseif($kind==='bill'){
  $sets['owned_restaurant']=n7OwnedRestaurant($db);
  $identity=n7Rows($db,'identity_type',['id','identity_type'],'is_delete=0');$sets['constructor_identity_categories']=$identity;
  [$where,$args]=n7DishSource($db,(string)$params['search_name'],$sets);$rows=n7Rows($db,'dishes',['uuid','name','category','sub_category','is_study'],$where,$args);$sets['dishes']=$rows;
  $categories=[1=>[],2=>[],3=>[],4=>[],5=>[],6=>[]];foreach($rows as $row){if(isset($categories[$row['category']])){$categories[$row['category']][]=$row;}}$body=['code'=>0,'message'=>'获取成功','data'=>$categories];
 }elseif($kind==='card'){
  $cards=n7Rows($db,'staff_card',['id','staff_uuid','is_delete','is_cancel','card_id','card_status','create_time','cost'],'is_delete=0 AND is_cancel=0 AND card_id LIKE ?',['%'.$params['cardId'].'%'],'id');$sets['cards']=$cards;$staff=[];$rows=[];$departments=[];
  foreach($cards as $card){$people=n7Rows($db,'staff',['id','uuid','name','mobile'],'uuid=? AND is_show=1',[$card['staff_uuid']]);if(count($people)>1){throw new RuntimeException('Card staff GUID nonunique');}if(!$people){continue;}$person=$people[0];$staff[$person['uuid']]=$person;
   $pairs=n7Rows($db,'staff_department',['id','staff_uuid','department_uuid'],'staff_uuid=?',[$person['uuid']]);$depNames=[];$depRows=[];foreach($pairs as $pair){$d=n7Rows($db,'department',['uuid','name'],'uuid=? AND is_show=1',[$pair['department_uuid']]);$depRows=array_merge($depRows,$d);foreach($d as $one){$depNames[]=$one['name'];}}
   if(count($depNames)>1){throw new RuntimeException('Unordered multiple department names; keep this candidate limited');}$departments[$person['uuid']]=['pairs'=>$pairs,'departments'=>$depRows];
   $status=$card['card_status']===null?5:$card['card_status'];$rows[]=['id'=>$person['id'],'staff_uuid'=>$person['uuid'],'name'=>$person['name'],'mobile'=>$person['mobile'],'card_id'=>$card['card_id']??'','card_status'=>$status,'issue_time'=>$card['create_time']??'','cost'=>$card['cost']??'','deptNames'=>implode(',',$depNames),'cardStatusDesc'=>[1=>'正常',2=>'挂失',3=>'冻结',4=>'退卡',5=>'未发卡'][(int)$status]??''];
  }
  foreach($rows as &$row){if(!is_int($row['card_status'])||$row['card_status']<0||$row['card_status']>255){throw new RuntimeException('Source uint8 status projection outside contract');}$row['card_status']=(string)$row['card_status'];}unset($row);
  usort($rows,static function($a,$b){return $b['id']<=>$a['id'];});$sets['staff']=$staff;$sets['departments']=$departments;$body=['code'=>0,'message'=>'查询成功','data'=>n7Page($rows,(int)$params['page'])];
 }elseif($kind==='log'){
  $logs=n7Rows($db,'staff_card_log',['id','staff_uuid','operate_time','operate_date','card_id','operate_type','operator'],'is_delete=0 AND card_id LIKE ? AND operate_date>=? AND operate_date<=?',['%'.$params['cardId'].'%',$params['startTime'],$params['endTime']],'id DESC');$sets['logs']=$logs;$rows=[];$people=[];
  foreach($logs as $log){$staff=n7Rows($db,'staff',['uuid','name'],'uuid=?',[$log['staff_uuid']]);if(count($staff)>1){throw new RuntimeException('Log joined staff GUID nonunique');}$people[$log['staff_uuid']]=$staff;$rows[]=['operate_time'=>$log['operate_time'],'operate_date'=>$log['operate_date'],'card_id'=>$log['card_id'],'operate_type'=>$log['operate_type'],'operator'=>$log['operator'],'name'=>$staff[0]['name']??null,'operateTypeDesc'=>[1=>'发卡',2=>'挂失',3=>'解挂',4=>'冻结',5=>'解冻',6=>'退卡',7=>'补卡'][(int)$log['operate_type']]??''];}
  $sets['people']=$people;$body=['code'=>0,'message'=>'查询成功','data'=>n7Page($rows,(int)$params['page'])];
 }elseif($kind==='restaurant_list'){
  $sets['owned_restaurant']=n7OwnedRestaurant($db);$all=n7Rows($db,'restaurants',['*'],'is_delete=2 AND name LIKE ?',['%'.$params['name'].'%'],'id DESC');n7SafeOwnedStar($db,$all);$sets['restaurants']=$all;$page=array_slice($all,((int)$params['page']-1)*5,5);$ids=array_values(array_unique(array_filter(array_column($page,'restaurant_shop_id'))));$shops=[];
  if($ids){$shops=n7Rows($db,'restaurant_shop',['id','name'],'is_delete=0 AND id IN ('.implode(',',array_fill(0,count($ids),'?')).')',$ids);}$sets['shops']=$shops;$map=array_column($shops,'name','id');foreach($page as &$row){$row['restaurant_shop_name']=$map[$row['restaurant_shop_id']]??'';$row['status_label']=[1=>'启用',2=>'停用'][$row['status']]??'启用';}unset($row);$body=['code'=>0,'message'=>'查询成功','data'=>['rows'=>$page,'total'=>count($all)]];
 }elseif($kind==='restaurant_read'){
  $sets['owned_restaurant']=n7OwnedRestaurant($db);$id=(int)$params['id'];$rows=[];
  if($id){$rows=n7Rows($db,'restaurants',['*'],'id=?',[$id]);if(count($rows)!==1){throw new RuntimeException('Frozen restaurant source no longer unique');}n7SafeOwnedStar($db,$rows);$row=$rows[0];$row['file_url']='';if(!empty($row['file_path'])){$row['file_url']=n7RestaurantImage($db,$row['file_path'],$sets);}$shops=[];if(!empty($row['restaurant_shop_id'])){$shops=n7Rows($db,'restaurant_shop',['id','name'],'id=?',[$row['restaurant_shop_id']]);if(count($shops)>1){throw new RuntimeException('Restaurant shop PK ambiguous');}}$sets['shop']=$shops;$row['restaurant_name']=$shops[0]['name']??'';$row['status_label']=$row['status']==1?'启用':'停用';}
  else{$row=['morning_start'=>'00:00:00','morning_end'=>'10:00:59','noon_start'=>'10:01:00','noon_end'=>'16:00:59','night_start'=>'16:01:59','night_end'=>'23:59:59'];}
  $sets['restaurants']=$rows;$today=date('Y-m-d');$sets['request_local_date']=$today;foreach(['morning_start','morning_end','noon_start','noon_end','night_start','night_end'] as $field){$row[$field]=$today.' '.$row[$field];}$body=['code'=>0,'message'=>'查询成功','data'=>$row];
 }elseif($kind==='dining'){
  $candidate=smLoad('candidates-private.json')['candidates']['dining'];if(date('Y-m-d')!==$candidate['date']){throw new RuntimeException('Actual today crossed frozen request date');}$sets['owned_restaurant']=n7OwnedRestaurant($db);
  $departments=n7Rows($db,'department',['uuid','name'],'leaf_flag=1 AND status=1 AND is_show=1');$sets['leaf_departments']=$departments;$all=[];
  foreach($departments as $row){if(isset($all[$row['uuid']])){throw new RuntimeException('Leaf department GUID duplicate');}$all[$row['uuid']]=['name'=>$row['name'],'morning_count'=>0,'noon_count'=>0,'night_count'=>0,'night_snack_count'=>0,'total_count'=>0];}
  $menus=n7Rows($db,'date_menu',['*'],'menu_date=? AND restaurant_id=? AND meal_type=3',[$candidate['date'],$params['restaurant_id']]);$sets['actual_today_menus']=$menus;$data=['meal_time'=>['morning_count'=>0,'noon_count'=>0,'night_count'=>0,'night_snack_count'=>0,'total_count'=>0],'department'=>[]];
  if($menus){$members=[];foreach($menus as $menu){$members[$menu['staff_uuid']][$menu['meal_times']]=true;}$staffUuids=array_keys($members);$pairs=n7Rows($db,'staff_department',['staff_uuid','department_uuid'],'staff_uuid IN ('.implode(',',array_fill(0,count($staffUuids),'?')).')',$staffUuids);$sets['actually_consumed_staff_departments']=$pairs;$mapped=[];foreach($pairs as $pair){$mapped[$pair['staff_uuid']][]=$pair['department_uuid'];}
   foreach($members as $uuid=>$meals){if(count($meals)===4){$data['meal_time']['total_count']++;}if(!isset($mapped[$uuid])){continue;}$fields=[1=>'morning_count',2=>'noon_count',3=>'night_count',4=>'night_snack_count'];foreach($fields as $meal=>$field){if(isset($meals[$meal])){$data['meal_time'][$field]++;}}
    foreach($mapped[$uuid] as $department){if(!isset($all[$department])){continue;}foreach($fields as $meal=>$field){if(isset($meals[$meal])){$all[$department][$field]++;$all[$department]['total_count']++;}}}
   }
   $sort=array_column($all,'total_count');array_multisort($sort,SORT_DESC,$all);
  }
  $data['department']=array_values($all);$body=['code'=>0,'message'=>'获取成功','data'=>$data];
 }else{throw new RuntimeException('Unsupported frozen readonly contract');}
 if($kind==='restaurant_list'){foreach($body['data']['rows'] as &$row){$row=n7CanonicalRestaurantRow($row);}unset($row);}elseif($kind==='restaurant_read'){$body['data']=n7CanonicalRestaurantRow($body['data']);}
 return ['body'=>json_decode(json_encode($body,JSON_UNESCAPED_UNICODE|JSON_THROW_ON_ERROR)),'source_sets'=>$sets,'source_digest'=>hash('sha256',serialize($sets))];
}
