<?php


namespace App\Services;


use App\Batch;
use App\Events\BroadcastToStation;
use App\Exceptions\ErrorException;
use App\Exceptions\Exception;
use App\MaterialBox;
use App\OrderCommodity;
use App\StationCacheShelfGrid;
use App\StationTask;
use App\StationTaskBatch;
use App\StationTaskMaterialBox;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;
use App\Traits\ServiceAppAop;
use Illuminate\Support\Facades\DB;


class StationTaskMaterialBoxService
{
    use ServiceAppAop;
    protected $modelClass=StationTaskMaterialBox::class;
    /** @var StationService $stationService */
    private $stationService;
    /** @var StationTypeService $stationTypeService */
    private $stationTypeService;
    /** @var StationTaskService $stationTaskService */
    private $stationTaskService;
    /** @var StationTaskBatchService $stationTaskBatchService */
    private $stationTaskBatchService;
    /** @var StationTaskCommodityService $stationTaskCommodityService */
    private $stationTaskCommodityService;
    /** @var OrderCommodityService $orderCommodityService */
    private $orderCommodityService;
    /** @var MaterialBoxService $materialBoxService */
    private $materialBoxService;
    /** @var CacheShelfService $cacheShelfService */
    private $cacheShelfService;
    public function __construct(){
        $this->stationService=null;
        $this->stationTypeService=null;
        $this->stationTaskService=null;
        $this->materialBoxService=null;
        $this->stationTaskBatchService=null;
        $this->stationTaskCommodityService=null;
    }

    function create($kvPairs)
    {
        return StationTaskMaterialBox::query()->create($kvPairs);
    }
    function createByStationAndMaterialBox($station, $materialBox)
    {
        return StationTaskMaterialBox::query()->create([
            'station_id' => $station['id'],
            'material_box_id' => $materialBox['id'],
            'status' => '待处理'
        ]);
    }
    function getOccupied_byBatches(?Collection $batches): ?Collection
    {
        return StationTaskMaterialBox::query()
            ->where('status','<>','完成')
            ->where('created_at','>',Carbon::now()->subHours(2))
            ->whereHas('materialBox',function ($query)use($batches){
                $locations=OrderCommodity::query()
                    ->whereHas('order',function ($queryO)use($batches){
                        $queryO->whereIn('batch_id',data_get($batches,'*.id')??[]);
                    })->get('location');
                $query->whereIn('code',data_get($locations,'*.location')??[]);
            })
            ->get();
    }

    function createByBatches(Collection $batches,Collection $stationTasks_toAttach): Collection
    {
        $this->instant($this->stationTaskService,'StationTaskService');
        LogService::log(__METHOD__,'assignTasks','波次任务分配4.c1：'.json_encode($batches));
        $stationTaskMaterialBoxes_byBatch = (function () use ($batches) {
            $stationTaskMaterialBoxes_listByBatch = new Collection();
            foreach ($batches as $batch) {
                $stationTaskMaterialBoxes_listByBatch->push(
                    $this->createByBatch($batch)
                );
            }
            return $stationTaskMaterialBoxes_listByBatch;
        })();
        LogService::log(__METHOD__,'assignTasks','波次任务分配4.c2：'.json_encode($batches));
        $this->stationTaskService
            ->registerSubTasks(
                $stationTasks_toAttach,
                $stationTaskMaterialBoxes_byBatch);
        return collect(data_get($stationTaskMaterialBoxes_byBatch,'*.*'));
    }

    function createByBatch(Batch $batch): ?Collection
    {
        $this->instant($this->materialBoxService,'MaterialBoxService');
        $this->instant($this->stationTypeService,'StationTypeService');
        $this->instant($this->stationService,'StationService');
        $this->instant($this->stationTaskBatchService,'StationTaskBatchService');
        $this->instant($this->orderCommodityService,'OrderCommodityService');
        $stationMaterialBoxes_toCreate=new Collection();
        $order_ids=data_get($batch['orders'],'*.id');
        $orderCommodities=OrderCommodity::query()->with('orderBin')->whereIn('order_id',$order_ids)->get();
        $orderCommodities=$this->orderCommodityService->correctLocation_fromWMS($orderCommodities);
        if($orderCommodities->isEmpty())return $stationMaterialBoxes_toCreate;
        $stationType=$this->stationTypeService->getForMaterialBox_onBatchProcess();
        $stationTaskBatch=$this->stationTaskBatchService->get(['batch_id'=>$batch['id']])->first();
        $materialBoxIds_used=[];
        $orderCommodities=$orderCommodities->sortBy('commodity_id');//按商品排序后，出货可以同商品挨在一起
        foreach ($orderCommodities as $orderCommodity){
            $station=$this->stationService->getStation_byType($stationType['name']);
            $materialBox=$this->materialBoxService->firstOrCreate(['code' => $orderCommodity['location']]);
            if(in_array($materialBox['id'],$materialBoxIds_used))continue;
            $stationMaterialBoxes_toCreate->push([
                'station_id'=>$station['id'],
                'material_box_id'=>$materialBox['id'],
                'station_task_batch_id'=>$stationTaskBatch['id'],
                'status'=>'待处理'
            ]);
            $materialBoxIds_used[]=$materialBox['id'];
        }
        return $this->insert($stationMaterialBoxes_toCreate->toArray(),true);
    }

    function get(array $kvPairs, $with=null){
        ksort($kvPairs);
        return Cache::remember('StationTaskMaterialBox'.md5(json_encode($kvPairs).json_encode([$with])), config('cache.expirations.fastChange'), function ()use($kvPairs,$with) {
            $query = StationTaskMaterialBox::query();
            if($with){
                $query->with($with);
            }
            foreach ($kvPairs as $column => $value){
                if (is_array($value))$query->whereIn($column,$value);
                else $query->where($column,$value);
            }
            return $query->get();
        });
    }


    function markHasPut(StationTaskMaterialBox $stationTaskMaterialBox){
        $this->instant($this->stationTaskBatchService,'StationTaskBatchService');
        $this->instant($this->stationTaskCommodityService,'StationTaskCommodityService');
        $this->instant($this->stationTaskService,'StationTaskService');
        $this->instant($this->stationService,'StationService');
        $this->instant($this->cacheShelfService,'CacheShelfService');
        try{
            LogService::log('海柔请求','markHasTaken1','');
            $taskType=$this->getServingTaskType($stationTaskMaterialBox);
            LogService::log('海柔请求','markHasTaken2',
                json_encode($taskType));
            switch ($taskType){
                case '分波次':
                    $this->markProcessing($stationTaskMaterialBox);
                    $this->stationTaskBatchService->markProcessing_byIds($stationTaskMaterialBox['station_task_batch_id']);
                    $this->stationTaskCommodityService->markProcessing($stationTaskMaterialBox['stationTaskCommodities']);
                    $this->stationTaskService->markProcessing_byIds(data_get($stationTaskMaterialBox['stationTaskCommodities'],'*.station_task_id'));
                    /** @var StationTask $stationTask */
                    $stationTask = $this->stationTaskService->getProcessing();
                    $this->stationService->broadcastBinMonitor($stationTaskMaterialBox['station_id'], $stationTask);
                    $stationTaskMaterialBox->materialBox['status']='在U型线';
                    $stationTaskMaterialBox->materialBox->update();
                    break;
                case '入立库':
                    $this->set($stationTaskMaterialBox,[
                        'id' => $stationTaskMaterialBox['station_id'],
                        'status' => '完成',
                    ]);
                    $this->cacheShelfService->putStationTaskMaterialBoxProcess($stationTaskMaterialBox);
                    $stationTaskMaterialBox->materialBox['status']='在立库';
                    $stationTaskMaterialBox->materialBox->update();
                    break;
                case '入缓存架':
                    $stationTaskMaterialBox->materialBox['status']='在缓存架';
                    $stationTaskMaterialBox->materialBox->update();
                    break;
                default:;
            }
        }catch (\Exception $e){
            throw new ErrorException('放置料箱出错');
        }
    }

    // TODO 料箱处理
    function markHasTaken($stationTaskMaterialBox)
    {
        //TODO: 标记 料箱位置（需要其字段存在）$stationTaskMaterialBox['materialBox']['position']
        $this->instant($this->cacheShelfService,'CacheShelfService');

        // 料箱从缓存架上拿走
        if($stationTaskMaterialBox['station']['stationType']['name']=='缓存架'){

            $stationTaskMaterialBox['status'] = '完成';
            $result = $this->cacheShelfService->lightOffTask($stationTaskMaterialBox['station']['code'],0);
            if($result['success'])
                $this->markProcessed($stationTaskMaterialBox);
        }
    }

    function processNextQueued(?StationTaskMaterialBox $stationTaskMaterialBox_lastProcessed){
        $station_id=$stationTaskMaterialBox_lastProcessed['station_id'];
        $stationTaskMaterialBox_next=StationTaskMaterialBox::query()
            ->where('station_id',$station_id)
            ->where('status','处理队列')
            ->orderBy('updated_at')
            ->first();
        if($stationTaskMaterialBox_next){
            $stationTaskMaterialBox_next->update(['status'=>'处理中']);
        }
        return $stationTaskMaterialBox_next;
    }

    function markProcessed(StationTaskMaterialBox $stationTaskMaterialBox){
        $stationTaskMaterialBox['status'] = '完成';
        $stationTaskMaterialBox->save();
    }
    function getNotProcessedSiblings($stationTaskMaterialBox){
        return StationTaskMaterialBox::query()
            ->whereNotIn('status',['完成'])
            ->where('station_task_id',$stationTaskMaterialBox['station_task_id'])
            ->get();
    }

    /**
     * 每波次仅将最老的作务标为“处理中”，其他置入队列；
     * 如某波次已经有“处理中“，则他部置入队列
     * @param $stationTaskMaterialBox_orBoxes ?? 单个或多个
     */
    function markProcessing($stationTaskMaterialBox_orBoxes)
    {
        $this->instant($this->stationTaskService,'StationTaskService');
        $stationTaskMaterialBoxes =
            (function()use($stationTaskMaterialBox_orBoxes){
                if (get_class($stationTaskMaterialBox_orBoxes)==StationTaskMaterialBox::class){
                    return collect([$stationTaskMaterialBox_orBoxes]);
                }
                return collect($stationTaskMaterialBox_orBoxes);
            })();
        $stationTaskMaterialBoxes_grouped=
            ($按时间从前往后排出顺序=function ()use(&$stationTaskMaterialBoxes){
                return $stationTaskMaterialBoxes
                    ->sortBy('id')
                    ->groupBy('station_task_batch_id');
            })();
        $stationTaskMaterialBoxes_grouped->each(function(&$groupByBatch){
            ($将所有要标记的箱任务先放在队列里=function()use(&$groupByBatch){
                $groupByBatch->each(function (&$stationTaskMaterialBox){
                    $stationTaskMaterialBox['status']='处理队列';
                });
            })();
            ($如果之前没有处理中则标记第一个为处理目标，准备持久化=function()use(&$groupByBatch){
                $stationId=$groupByBatch[0]['station_id'];
                $processing=$this->getProcessing_byStationId($stationId);
                if(!$processing){
                    $groupByBatch[0]['status']='处理中';
                    $groupByBatch[0]->update();
                }else{
                    foreach ($groupByBatch as &$stationTaskMaterialBox){
                        if($stationTaskMaterialBox['id']==$processing['id']){
                            $stationTaskMaterialBox['status']='处理中';
                        }
                    }
                }
            })();
        });
        ($持久化处理队列的记录=function()use(&$stationTaskMaterialBoxes_grouped){
            $toArray = $stationTaskMaterialBoxes_grouped->collapse();
            $toArray=$toArray->where('status','处理队列');
            $ids_toUpdate = data_get($toArray, '*.id');
            if(count($ids_toUpdate))
                StationTaskMaterialBox::query()->whereIn('id',$ids_toUpdate)->update(['status'=>'处理队列']);
        })();
//        StationTaskMaterialBox::query()
//            ->whereIn('id', data_get($stationTaskMaterialBoxes, '*.id'))
//            ->update(['status'=>'处理中']);
        $this->stationTaskService
            ->markProcessing_byIds(
                data_get($stationTaskMaterialBoxes, '*.*.station_id')
            );
    }

    function getProcessing_byStationId($stationId)
    {
        //这里不能用缓存，因为更新会非常快
        return StationTaskMaterialBox::query()
            ->where('station_id',$stationId)
            ->where('status','处理中')
            ->where('created_at','>',Carbon::now()->subDay())
            ->first();
    }

    function excepted($stationTaskMaterialBoxes_orBox){
        if (get_class($stationTaskMaterialBoxes_orBox)==StationTaskMaterialBox::class){
            $stationTaskMaterialBoxes_orBox = collect([$stationTaskMaterialBoxes_orBox]);
        }
        StationTaskMaterialBox::query()->whereIn('id',data_get($stationTaskMaterialBoxes_orBox,'*.id'))
            ->update(['status'=>'异常']);
        switch (get_class($stationTaskMaterialBoxes_orBox)){
            case MaterialBox::class:
            case StationTaskMaterialBox::class:
                throw new ErrorException('料箱异常'.json_encode($stationTaskMaterialBoxes_orBox->toJson()));
        }
    }

    function getServingTaskType(StationTaskMaterialBox $stationTaskMaterialBox): string
    {
        $stationTaskMaterialBox->load('station.stationType');
        if($isBatching=(
            $stationTaskMaterialBox['station_task_batch_id'] &&
            $stationTaskMaterialBox['station']['stationType']['name'] == '料箱监视器')
        ){
            return '分波次';
        }
        if($isPuttingBack=(
            $stationTaskMaterialBox['station']['stationType']['name'] == '立库')
        ){
            return '入立库';
        }
        if($isCacheShelf=(
            $stationTaskMaterialBox['station']['stationType']['name'] == '缓存架')
        ){
            return '入缓存架';
        }
//        if($isStoring=false){
//            return '入库';
//        }
        throw new ErrorException('当前类型找不到');
    }
}
