<?php
declare(strict_types=1);

final class OfflineStoreConnection extends \think\db\connector\Mysql
{
    public $denied=0;public $schemaReads=0;
    public function connect(array $config=[],$linkNum=0,$autoConnection=false){++$this->denied;throw new RuntimeException('Offline DB denied');}
    public function query($sql,$bind=[],$master=false,$pdo=false){++$this->denied;throw new RuntimeException('Offline DB denied');}
    public function execute($sql,$bind=[],\think\db\Query $query=null){++$this->denied;throw new RuntimeException('Offline DB denied');}
    public function getFields($tableName)
    {if($tableName!=='cdx_shadow_single'){++$this->denied;throw new RuntimeException('Offline schema denied');}++$this->schemaReads;return offlineFields();}
    public function getTables($dbName=''){++$this->denied;throw new RuntimeException('Offline schema denied');}
}
final class OfflineStoreBuilder extends \think\db\builder\Mysql
{
    private $projection;private $mode='native';public $fieldFragment;
    public function mode(string $mode,?array $projection): void {$this->mode=$mode;$this->projection=$projection;}
    protected function parseField($fields,$options=[])
    {
        if($this->mode==='sentinel'){return $this->fieldFragment='__CDX_PROJECTION_SENTINEL__';}
        if($this->mode!=='projected'){return $this->fieldFragment=parent::parseField($fields,$options);}
        $parts=[];foreach($this->projection['columns'] as $c){$parts[]=parent::parseKey($c['source'],$options).' AS '.parent::parseKey($c['alias'],$options,true);}
        foreach($this->projection['helpers'] as $c){$parts[]='CAST('.parent::parseKey($c['source'],$options).' AS VARCHAR(128)) AS '.parent::parseKey($c['alias'],$options,true);}
        return $this->fieldFragment=implode(',',$parts);
    }
}
final class OfflineStoreModel extends \think\Model
{
    protected $name='offline_model';protected $autoWriteTimestamp=false;protected $dateFormat='Y-m-d H:i:s';protected $resultSetType='array';
    public function getWideIdAttr($value){if(!is_int($value)&&!is_string($value)&&$value!==null){throw new UnexpectedValueException('Getter received imprecise type');}return $value;}
}
final class OfflineStoreFloatModel extends \think\Model
{
    protected $name='offline_float';protected $autoWriteTimestamp=false;protected $dateFormat='Y-m-d H:i:s';protected $resultSetType='array';protected $type=['wide_id'=>'float'];
}

function buildOfflineCase(OfflineStoreConnection $connection,array $fields,array $order,int $page): array
{
    $query=new \think\db\Query($connection);$query->bind('owned_min',7,PDO::PARAM_INT)->bind('owned_label','离线"检查',PDO::PARAM_STR);
    $query->table('cdx_shadow_single')->alias('s')->field($fields)->where('s.id','>=',':owned_min')->where('s.label','=',':owned_label')->order($order)->page($page,2);
    $parse=new ReflectionMethod($query,'parseExpress');$parse->setAccessible(true);$options=$parse->invoke($query);$options['operation']='select';
    return [$query,$options];
}
function emitOffline($connection,$query,array $options,string $mode,?array $projection): array
{
    $copy=clone $query;$builder=new OfflineStoreBuilder($connection,$copy);$builder->mode($mode,$projection);$sql=$builder->select($options);
    return ['sql'=>$sql,'field_fragment'=>$builder->fieldFragment,'bind'=>$copy->getBind()];
}
