<?php
// Runs unmodified branch Bill logic/read SQL; persistence dependencies are SQLite test doubles.
// No project bootstrap, credentials, network, production DB, or hardware calls.
namespace think {
    class Exception extends \Exception {}
    class Db {
        public static $pdo;
        public static function query($sql) { return self::$pdo->query($sql)->fetchAll(\PDO::FETCH_ASSOC); }
    }
}
namespace app\model {
    class BaseModel {}
    class RecipesLog {
        public function inquiryOne($where) { return []; }
        public function add($data) { return 1; }
    }
}
namespace {
    error_reporting(E_ALL & ~E_DEPRECATED);
    if ($argc !== 3) { throw new \RuntimeException('Usage: php reproduce.php BillLogic.php BillModel.php'); }
    require $argv[2];
    require $argv[1];
    function guid() { return uniqid('fixture-', true); }
    function getImgFullPath($path) { return $path; }
    class FixtureBill extends \app\model\Bill {
        public $failInsert = false;
        public function inquiryAll($where) {
            $stmt = \think\Db::$pdo->prepare('SELECT * FROM ydy_recipes WHERE menu_date=? AND restaurant_id=?');
            $stmt->execute([$where['menu_date'], $where['restaurant_id']]);
            return $stmt->fetchAll(\PDO::FETCH_ASSOC);
        }
        public function del_bill($where) {
            $ids = array_map('intval', $where['id'][1]);
            return \think\Db::$pdo->exec('DELETE FROM ydy_recipes WHERE id IN ('.implode(',', $ids).')');
        }
        public function addAll($rows, $replace=false) {
            if ($this->failInsert) { throw new \think\Exception('injected insert failure'); }
            $stmt = \think\Db::$pdo->prepare('INSERT INTO ydy_recipes (dishes_uuid,menu_date,restaurant_id,meal_times,type) VALUES (?,?,?,?,?)');
            foreach ($rows as $row) {
                $stmt->execute([$row['dishes_uuid'],$row['menu_date'],$row['restaurant_id'],$row['meal_times'],$row['type']]);
            }
            return count($rows);
        }
    }
    class FixtureCater {
        public function update_food_number($where, $method, $number) { return true; }
        public function inquiry($where, $page, $size, $order, $field) {
            return \think\Db::query('SELECT uuid,name,file_path AS img,category FROM ydy_dishes WHERE is_del=1 AND status=1');
        }
    }
    class FixtureDishes { public function modifyByWhere($where,$data) { return true; } }
    \think\Db::$pdo = new \PDO('sqlite::memory:');
    \think\Db::$pdo->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION);
    \think\Db::$pdo->exec('CREATE TABLE ydy_recipes(id INTEGER PRIMARY KEY,dishes_uuid TEXT,menu_date TEXT,restaurant_id INTEGER,meal_times INTEGER,type INTEGER)');
    \think\Db::$pdo->exec('CREATE TABLE ydy_dishes(uuid TEXT PRIMARY KEY,name TEXT,file_path TEXT,category INTEGER,is_del INTEGER,status INTEGER)');
    for ($i=1;$i<=7;$i++) { \think\Db::$pdo->exec("INSERT INTO ydy_dishes VALUES ('d$i','fixture $i','',1,1,1)"); }
    $model = new FixtureBill();
    $reflect = new \ReflectionClass(\app\p\logic\Bill::class);
    $logic = $reflect->newInstanceWithoutConstructor();
    foreach (['BillModel'=>$model,'CaterModel'=>new FixtureCater(),'DishesModel'=>new FixtureDishes()] as $key=>$value) {
        if ($reflect->hasProperty($key)) { $reflect->getProperty($key)->setValue($logic,$value); }
        else { $logic->$key = $value; } // CaterModel is dynamic in the source.
    }
    function payload($n,$meal=1) { $data=[]; for($i=1;$i<=$n;$i++) { $data[]=['uuid'=>'d'.$i,'meal_times'=>$meal,'type'=>1]; } return $data; }
    function countMeal($meal=1,$date='2026-09-14',$restaurant=1) {
        $stmt=\think\Db::$pdo->prepare('SELECT count(*) FROM ydy_recipes WHERE meal_times=? AND menu_date=? AND restaurant_id=?');
        $stmt->execute([$meal,$date,$restaurant]); return (int)$stmt->fetchColumn();
    }
    $results=[];
    function check($name,$ok,$details) {
        global $results;
        $results[]=['test'=>$name,'assertion'=>$ok?'PASS':'FAIL','observed'=>$details];
        if (!$ok) { throw new \RuntimeException($name.' '.json_encode($details)); }
    }
    $logic->add(payload(6),'2026-09-14',1);
    check('normal_save_six',countMeal()===6,['breakfast'=>countMeal()]);
    $logic->add(payload(3),'2026-09-15',1);
    $logic->add(payload(2),'2026-09-14',2);
    $stale = array_merge(payload(4),[['uuid'=>'d7','meal_times'=>2,'type'=>1]]);
    $res=$logic->add($stale,'2026-09-14',1);
    check('stale_lunch_save_overwrites_breakfast',$res['code']===0 && countMeal()===4 && countMeal(2)===1,['code'=>$res['code'],'breakfast'=>countMeal(),'lunch'=>countMeal(2)]);
    check('date_restaurant_boundaries',countMeal(1,'2026-09-15')===3 && countMeal(1,'2026-09-14',2)===2,['other_date'=>countMeal(1,'2026-09-15'),'other_restaurant'=>countMeal(1,'2026-09-14',2)]);
    $logic->add(payload(6),'2026-09-14',1);
    \think\Db::$pdo->exec("UPDATE ydy_dishes SET status=0 WHERE uuid IN ('d5','d6')");
    $visible=$model->bill_list('2026-09-14',1);
    $editable=$logic->cater_list('2026-09-14',1);
    $save=[];
    foreach (['breakfastTitleItemData'=>1,'lunchTitleItemData'=>2,'dinnerTitleItemData'=>3] as $key=>$meal) {
        foreach($editable[$key] as $category) { foreach($category['children'] as $dish) {
            if($dish['select']) { $dish['meal_times']=$meal; $dish['type']=$category['value']; $save[]=$dish; }
        }}
    }
    $logic->add($save,'2026-09-14',1);
    check('disabled_dishes_lost_on_resave',count($visible)===6 && count($save)===4 && countMeal()===4,['view_count'=>count($visible),'editor_selected'=>count($save),'after_save'=>countMeal()]);
    $logic->add(payload(6),'2026-09-14',1);
    $res=$logic->add(json_decode('{broken',true),'2026-09-14',1);
    check('invalid_json_null_clears_day',$res['code']===0 && countMeal()===0,['code'=>$res['code'],'after_save'=>countMeal()]);
    $logic->add(payload(6),'2026-09-14',1);
    $model->failInsert=true;
    $res=$logic->add(payload(6),'2026-09-14',1);
    check('insert_failure_has_no_outer_rollback',$res['code']===1 && countMeal()===0,['code'=>$res['code'],'after_failure'=>countMeal()]);
    echo json_encode(['scope'=>'Unmodified branch logic and read SQL; SQLite persistence doubles; NOT production or HTTP E2E','php'=>PHP_VERSION,'results'=>$results],JSON_PRETTY_PRINT|JSON_UNESCAPED_UNICODE).PHP_EOL;
}
