<?php
declare(strict_types=1);

final class OfflineAiConnection extends \app\common\database\Dm
{
    public $denied=0;public $schemaReads=0;public $giftFields=[];public $giftSchemaReads=0;
    public function connect(array $config=[],$linkNum=0,$autoConnection=false): PDO {++$this->denied;throw new RuntimeException('Offline DB denied');}
    public function query(string $sql,array $bind=[],bool $master=false): array {++$this->denied;throw new RuntimeException('Offline DB denied');}
    public function execute(string $sql,array $bind=[]): int {++$this->denied;throw new RuntimeException('Offline DB denied');}
    public function getPDOStatement(string $sql,array $bind=[],bool $master=false,bool $procedure=false): PDOStatement {++$this->denied;throw new RuntimeException('Offline DB denied');}
    public function getFields(string $tableName): array
    {if($tableName==='yoshop_user_gift_log'&&$this->giftFields){++$this->giftSchemaReads;return $this->giftFields;}if($tableName!=='cdx_shadow_single'){++$this->denied;throw new RuntimeException('Offline schema denied');}++$this->schemaReads;return offlineFields();}
    public function getTables(string $dbName=''): array {++$this->denied;throw new RuntimeException('Offline schema denied');}
}
final class OfflineAiBuilder extends \app\common\database\DmBuilder
{
    private $projection;private $mode='native';public $fieldFragment;
    public function mode(string $mode,?array $projection): void {$this->mode=$mode;$this->projection=$projection;}
    protected function parseField(\think\db\Query $query,$fields): string
    {
        if($this->mode==='sentinel'){return $this->fieldFragment='__CDX_PROJECTION_SENTINEL__';}
        if($this->mode!=='projected'){return $this->fieldFragment=parent::parseField($query,$fields);}
        $parts=[];foreach($this->projection['columns'] as $c){$parts[]=parent::parseKey($query,$c['source']).' AS '.parent::parseKey($query,$c['alias'],true);}
        foreach($this->projection['helpers'] as $c){$parts[]='CAST('.parent::parseKey($query,$c['source']).' AS VARCHAR(128)) AS '.parent::parseKey($query,$c['alias'],true);}
        return $this->fieldFragment=implode(',',$parts);
    }
}
final class OfflineAiModel extends \think\Model
{
    protected $name='offline_model';protected $table='cdx_shadow_single';protected $autoWriteTimestamp=false;protected $dateFormat='Y-m-d H:i:s';
    public function getWideIdAttr($value){if(!is_int($value)&&!is_string($value)&&$value!==null){throw new UnexpectedValueException('Getter received imprecise type');}return $value;}
}
final class OfflineAiFloatModel extends \think\Model
{protected $name='offline_float';protected $table='cdx_shadow_single';protected $autoWriteTimestamp=false;protected $dateFormat='Y-m-d H:i:s';protected $type=['wide_id'=>'float'];}

final class OfflineAiManager extends \think\DbManager
{
    private $offline;public $allocations=0;
    public function __construct(OfflineAiConnection $connection){$this->offline=$connection;parent::__construct();$this->setConfig(['auto_timestamp'=>true,'datetime_format'=>'Y-m-d H:i:s']);}
    public function connect(string $name=null,bool $force=false){if($name!==null&&$name!==''){throw new RuntimeException('Unexpected offline connection name');}++$this->allocations;return $this->offline;}
}

function buildOfflineCase(OfflineAiConnection $connection,array $fields,array $order,int $page): array
{
    $query=new \think\db\Query($connection);$query->bind(['owned_min'=>[7,PDO::PARAM_INT],'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);
    $options=$query->parseOptions();$options['operation']='select';return [$query,$options];
}
function emitOffline($connection,$query,array $options,string $mode,?array $projection): array
{
    $copy=clone $query;foreach($options as $name=>$value){$copy->setOption($name,$value);}$builder=new OfflineAiBuilder($connection);$builder->mode($mode,$projection);$sql=$builder->select($copy);
    return ['sql'=>$sql,'field_fragment'=>$builder->fieldFragment,'bind'=>$copy->getBind()];
}

function compileGiftOffline(OfflineAiConnection $connection): array
{
    $q=new \think\db\Query($connection);$q->bind(['owned_min'=>[0,PDO::PARAM_INT]])->table('yoshop_user_gift_log')->alias('g')->field(['g.log_id','g.biz_order_id','g.money','g.balance_before','g.balance_after'])->where('g.log_id','>=',':owned_min')->order(['g.log_id'=>'DESC'])->limit(5);
    $options=$q->parseOptions();$options['operation']='select';$schema=giftContract();$p=ProjectionContract::compile($options,$schema,false);
    $native=emitOffline($connection,$q,$options,'native',null);$projected=emitOffline($connection,$q,$options,'projected',$p);
    return ['schema'=>$schema,'projection'=>$p,'native'=>$native,'projected'=>$projected,'plan'=>new ShadowPlan(['sql'=>$projected['sql'],'names'=>$p['names'],'visible'=>$p['visible'],'restore'=>$p['restore']])];
}
