<?php


namespace App\Services;


use App\Exceptions\ErrorException;
use App\MaterialBox;
use App\StationCacheShelfGrid;
use App\StationTaskMaterialBox;
use Carbon\Carbon;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Http;
use App\Traits\ServiceAppAop;
use Illuminate\Support\Facades\Log;


class ForeignHaiRoboticsService
{
    use ServiceAppAop;
//    protected $modelClass=ForeignHaiRobotics::class;

    /** @var  $stationTaskMaterialBoxService StationTaskMaterialBoxService */
    private $stationTaskMaterialBoxService;
    /** @var  $stationTaskBatchService StationTaskBatchService */
    private $stationTaskBatchService;
    /** @var  $stationTaskCommoditiesService StationTaskCommodityService */
    private $stationTaskCommoditiesService;
    /** @var  $materialBoxService MaterialBoxService */
    private $materialBoxService;
    /** @var  $stationTaskService StationTaskService */
    private $stationTaskService;
    /** @var  $stationService StationService */
    private $stationService;
    public function __construct()
    {
        $this->stationTaskMaterialBoxService=null;
        $this->materialBoxService=null;
        $this->stationService=null;
    }

    /**
     * @param string $modeName '输送线入立架'|'立架出至输送线'|'移动立架内位置'|'缓存架入立架'|'立架出至缓存架'
     * @param string $fromLocation
     * @param string $toLocation
     * @param Collection $taskMaterialBoxes
     * @param string $groupId
     * @param int $priority
     * @param int $isSequenced
     * @return array
     */
    private function makeJson_move(
        Collection $taskMaterialBoxes,
        string $modeName,
        string $fromLocation='',
        string $toLocation='',
        $groupId=''
        , $priority=10
        , $isSequenced=1
    ): array
    {
        $timestampSuffix = microtime(true);
        $taskMode=(function()use($modeName){
            switch ($modeName){
                case '输送线入立架': return 1;
                case '立架出至输送线': return 2;
                case '移动立架内位置': return 3;
                case '缓存架入立架':
                case '立架出至缓存架': return 6;
                default: throw new \Exception('发至海柔的移料箱请求，模式不存在');
            }
        })();
        $bins=$taskMaterialBoxes->map(function (StationTaskMaterialBox $taskMaterialBox)use($timestampSuffix,$fromLocation,$toLocation){
            return [
                "taskCode"  =>$taskMaterialBox['id'].'_'.$timestampSuffix,
                "binCode"   => $taskMaterialBox['materialBox']['code'],
                "fromLocCode" => $fromLocation??'',
                "toLocCode" => $toLocation??'',
            ];
        });
        return [[
            "taskMode"  =>$taskMode,
            "bins"=>$bins,
            "groupCode"=>$groupId.'_'.$timestampSuffix,
            "priority"=>$priority,
            "sequenceFlag"=>$isSequenced,
        ]];
    }

    public function fetchGroup($toLocation, Collection $taskMaterialBoxes, $groupIdPrefix=''): bool
    {
        LogService::log(__METHOD__,'runMany','波次任务分配6.r5f0：');
        LogService::log(__METHOD__,'runMany','波次任务分配6.r5f1：'.json_encode($toLocation).json_encode($taskMaterialBoxes).json_encode($groupIdPrefix));
        $dataToPost=$this->makeJson_move(
            $taskMaterialBoxes,
            '立架出至输送线',
            '',
            $toLocation??'',
            $groupIdPrefix
        );
        LogService::log(__METHOD__,'runMany','波次任务分配6.r5f2：'.json_encode($dataToPost));
        return $this->controlHaiRobot($dataToPost,$taskMaterialBoxes,'立架出至输送线');
    }

    public function moveBin(){

    }

    public function markBinProcessed(
        $workStation,
        $binCode,
        $success,
        $created_at,
        $exception,
        $is_in_plan
    ): bool
    {
        LogService::log('海柔请求','markBinProcessed1.1',
            '');
        $this->instant($this->stationService,'StationService');
        $this->instant($this->materialBoxService,'MaterialBoxService');
        $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
        $this->instant($this->stationTaskCommoditiesService,'StationTaskCommodityService');
        $this->instant($this->stationTaskBatchService,'StationTaskBatchService');
        try{
            LogService::log('海柔请求','markBinProcessed1.2',
                json_encode([$binCode,$success,$exception,$is_in_plan]));
            if($failed
                =!$success)
                throw new ErrorException('海柔任务失败:'.$exception);
            LogService::log('海柔请求','markBinProcessed1.3',
                $failed);
//            if($NotInPlan
//                =!$is_in_plan)
//                throw new ErrorException('海柔认为是计划外的料箱：'.$exception);

            LogService::log('海柔请求','markBinProcessed1.4',
                '$NotInPlan');

            $materialBox=
                $this->materialBoxService->get(['code'=>$binCode])->first();
            LogService::log('海柔请求','markBinProcessed1.5',
                json_encode($materialBox));
            /** @var StationTaskMaterialBox $stationTaskMaterialBox */
            $stationTaskMaterialBox
                =(function()use($materialBox){
                return $stationTaskMaterialBox=
                    StationTaskMaterialBox::query()
                        ->where('material_box_id',$materialBox['id'])
                        ->where('created_at','>',Carbon::now()->subDay())
                        ->whereIn('status',['处理中','待处理','异常','处理队列'])
                        ->orderBy('id','desc')
                    ->first();
            })();
            LogService::log('海柔请求','markBinProcessed1.6',
                json_encode($stationTaskMaterialBox));
            if(!$stationTaskMaterialBox){
                throw new ErrorException($binCode.'该料箱没有安排在处理队列中.');
            }
            LogService::log('海柔请求','markBinProcessed1.7',
                json_encode($stationTaskMaterialBox));

            DB::transaction(function ()use($stationTaskMaterialBox){
                $stationTaskMaterialBox_next=
                    $this->stationTaskMaterialBoxService
                        ->processNextQueued($stationTaskMaterialBox); //找到队列中下一个料箱，并标记为处理中
                $this->stationTaskCommoditiesService
                    ->markProcessed($stationTaskMaterialBox['stationTaskCommodities']);
                LogService::log('海柔请求','markBinProcessed1.8',
                    json_encode($stationTaskMaterialBox));
                if($stationTaskMaterialBox_next)
                    $this->stationTaskCommoditiesService
                        ->markProcessing($stationTaskMaterialBox_next['stationTaskCommodities']);//因为上边商品任务被标记完成了，所以这里要将队列中找出正在处理的料箱对应的标记为“处理中”

                $this->stationTaskMaterialBoxService
                    ->markProcessed($stationTaskMaterialBox);
                $notProcessedBoxTasks = $this->stationTaskMaterialBoxService->getNotProcessedSiblings($stationTaskMaterialBox);
                if($notProcessedBoxTasks->isEmpty()){
                    $this->instant($this->stationTaskService,'StationTaskService');
                    LogService::log('海柔请求','markBinProcessed1.81',
                        json_encode($stationTaskMaterialBox['stationTaskBatch']));
                    $stationTaskMaterialBox->loadMissing('stationTaskBatch');
                    $this->stationTaskBatchService->markProcessed($stationTaskMaterialBox['stationTaskBatch']);
                    LogService::log('海柔请求','markBinProcessed1.82',
                        json_encode($stationTaskMaterialBox['stationTaskBatch']));
                    $this->stationTaskService->markProcessed($stationTaskMaterialBox['stationTask']);
                }
                $this->storeBox($stationTaskMaterialBox)
                    ?true
                    :(function(){throw new ErrorException('呼叫机器人回收U型线料箱失败');})();
                LogService::log('海柔请求','markBinProcessed1.9',
                    json_encode($stationTaskMaterialBox));
                $this->stationService->broadcastBinMonitor($stationTaskMaterialBox['station_id'],$stationTaskMaterialBox['stationTask']);
                LogService::log('海柔请求','markBinProcessed1.99',
                    json_encode($stationTaskMaterialBox));
            });

            return true;
        }catch (\Exception $e){

            $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
            $this->instant($this->materialBoxService,'MaterialBoxService');
            $box=$this->materialBoxService->firstOrCreate(['code'=>$binCode]);
            $stationTaskMaterialBox_toStore=
                $this->stationTaskMaterialBoxService->create([
                    'station_id' => $this->stationService->getStation_byType('立库')['id'],
                    'material_box_id' => $box['id'],
                    'status' => '处理中'
                ] );

            $dataToPost=$this->makeJson_move(
                collect([$stationTaskMaterialBox_toStore]),
                '输送线入立架',
                'BIN-IN1',//TODO：这里应该是动态取得，参考出立架getULineExit()方法，不然不能从站获得对应的出口，而且要改Station的child为children
                '',
                $stationTaskMaterialBox_toStore['stationTaskBatch']['id']
            );
            $this->controlHaiRobot($dataToPost,collect([$stationTaskMaterialBox_toStore]),'输送线入立架');

            $stationTaskMaterialBox = $stationTaskMaterialBox_toStore??$materialBox??null;
            if($stationTaskMaterialBox && get_class($stationTaskMaterialBox)==MaterialBox::class){
                $stationTaskMaterialBox = StationTaskMaterialBox::query()
                    ->where('material_box_id',$stationTaskMaterialBox['id'])
                    ->where('status','<>','完成')
                    ->where('created_at','>',now()->format('Y-m-d'))
                    ->first();
            }
            if($stationTaskMaterialBox)
                $this->stationTaskMaterialBoxService
                    ->excepted($stationTaskMaterialBox);
            return $e->getMessage();
        }
    }

    public function storeBox(?StationTaskMaterialBox $stationTaskMaterialBox): bool
    {
        LogService::log('海柔请求','putBinToStore1',
            '');
        $this->instant($this->stationService,'StationService');
        $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
        $stationTaskMaterialBox_toStore=
            $this->stationTaskMaterialBoxService->firstOrCreate([
                'station_id' => $this->stationService->getStation_byType('立库')['id'],
                'material_box_id' => $stationTaskMaterialBox['materialBox']['id'],
                'status' => '处理中',
                'type' => '放',
            ]);
        LogService::log('海柔请求','putBinToStore2',
            json_encode($stationTaskMaterialBox));
        $dataToPost=$this->makeJson_move(
            collect([$stationTaskMaterialBox_toStore]),
            '输送线入立架',
            'BIN-IN1',//TODO：这里应该是动态取得，参考出立架getULineExit()方法，不然不能从站获得对应的出口，而且要改Station的child为children
            '',
            $stationTaskMaterialBox['stationTaskBatch']['id']
        );
        LogService::log('海柔请求','putBinToStore3',
            json_encode($dataToPost));
        $controlSuccess = $this->controlHaiRobot($dataToPost,collect([$stationTaskMaterialBox_toStore]),'输送线入立架');

        return $controlSuccess;
    }

    /** 缓存架入立架 料箱 任务
     * @param StationTaskMaterialBox|null $stationTaskMaterialBox
     * @param string|null $formLocation
     * @return bool
     * @throws ErrorException
     */
    public function putBinToStore_fromCacheShelf(?StationTaskMaterialBox $stationTaskMaterialBox,
                                                 string $formLocation): bool
    {
        LogService::log('海柔请求','putBinToStore_fromCacheShelf1', '');
        LogService::log('海柔请求','putBinToStore_fromCacheShelf2', json_encode($stationTaskMaterialBox));

        $dataToPost=$this->makeJson_move(
            collect([$stationTaskMaterialBox]),
            '缓存架入立架',
            $formLocation,
            ''
        );
        LogService::log('海柔请求','putBinToStore_fromCacheShelf3', json_encode($dataToPost));

        $controlSuccess = $this->controlHaiRobot($dataToPost,collect([$stationTaskMaterialBox]),'缓存架入立架');
        LogService::log('海柔请求','putBinToStore_fromCacheShelf4', 'controlHaiRobot '. json_encode($controlSuccess));

        if($controlSuccess){
            $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
            $this->stationTaskMaterialBoxService->set($stationTaskMaterialBox,[
                'status' => '处理中',
            ]);
            LogService::log('海柔请求','putBinToStore_fromCacheShelf5', 'controlHaiRobot '. json_encode($stationTaskMaterialBox));
        }
        return $controlSuccess;
    }

    public function taskUpdate(
//        $groupCode,
        $stationTaskMaterialBox_id, //实际对应传入的字段 taskCode, 这里用料箱任务号做taskCode
        $updateEventType,   //0：task_begin(取货)1：task_end（放货）
        $status,            //0：任务成功1：任务失败
        $binCode
    ):bool{
        LogService::log('海柔请求','taskUpdateIn',
            json_encode([
                $stationTaskMaterialBox_id,
                $updateEventType,
                $status,
                $binCode
            ]));
        $this->instant($this->stationTaskMaterialBoxService,'StationTaskMaterialBoxService');
        try{
            if(($failed
                    =$status)==1){
                throw new ErrorException('海柔任务失败');
            }
            if($料箱不匹配=
                !$stationTaskMaterialBox
                    =(function()use($stationTaskMaterialBox_id,$binCode){
                    $stationTaskMaterialBox=StationTaskMaterialBox::query()->find($id=$stationTaskMaterialBox_id);
                    if($stationTaskMaterialBox['materialBox']['code']==$binCode)return $stationTaskMaterialBox;
                    return null;
                })()){
                throw new ErrorException('发回的料箱和任务号（ID）不匹配:$stationTaskMaterialBox_id:'
                    .$stationTaskMaterialBox_id.' $binCode:'.$binCode. ' '.
                    StationTaskMaterialBox::query()
                        ->where('id', $id=$stationTaskMaterialBox_id)
                        ->get()
                        ->toJson());
            }
            if(($标记已放置=
                function()use($updateEventType,$stationTaskMaterialBox){
                    if(($isPut
                            =$updateEventType)==1){
                        $this->stationTaskMaterialBoxService->markHasPut($stationTaskMaterialBox);
                        return true;
                    }return false;
                })())
                    return true;
            ($标记已取出=
                function()use($updateEventType,$stationTaskMaterialBox){
                    if(($isGet
                            =$updateEventType)==0){
                        $this->stationTaskMaterialBoxService->markHasTaken($stationTaskMaterialBox);
                    }
                })();
        }catch (\Exception $e){
            $this->excepted($stationTaskMaterialBox_id, $binCode, $e->getMessage());
            return false;
        }
        return true;
    }

//    public function markHasPut($taskCode,$binCode):bool{
//        try{
//            //标记料箱进入位置
////            $taskMaterialBoxesService->markDone();//
//        }catch (\Exception $e){
//            switch ($e->getCode()){
//                case 'taskBinNotMatch';
//                case 'taskGetFailed';
//            }
//        }
//
//    }


    public function excepted($taskCode='',$binCode='', $msg=''):bool{
        try{
            throw new ErrorException(
                "taskCode任务号：$taskCode , binCode箱号：$binCode 海柔运行报错: $msg"
            );
        }catch (\Exception $e){
            return true;
        }
    }

    /**
     * @param array $dataToPost
     * @return bool
     */
    public function controlHaiRobot(array $dataToPost,Collection $taskMaterialBoxes,$modeName): bool
    {
        LogService::log('海柔请求','runMany','波次任务分配6.r5f2c1：'.json_encode($dataToPost));
        try{
            LogService::log('海柔请求','runMany','波次任务分配6.r5f2c1.51：');
            $response = Http::post(config('api.haiq.storage.moveBin'), $dataToPost);
            if(isset($response->json()['code'])&&$response->json()['code']==500)
                throw new ErrorException('机器人500错误：'.json_encode($response->json()));
            LogService::log('海柔请求','runMany','波次任务分配6.r5f2c1.52：');
            LogService::log(__METHOD__,'runMany','波次任务分配6.r5f2c1.53：'.json_encode($response->json()));
        }catch (\Exception $e){
            LogService::log('海柔请求','runMany','波次任务分配6.r5f2c1.54：'.json_encode($dataToPost).$e->getMessage());
            throw new ErrorException('海柔机器人任务执行失败：'.json_encode($dataToPost).$e->getMessage());
        }
        LogService::log('海柔请求','runMany','波次任务分配6.r5f2c2：'.json_encode($dataToPost));
        $errMsg = (function () use ($response) {
            if ($response->ok()) return '';
            $errMsg = '错误: ';
            if (!$response) {
                return $errMsg . '没有返回内容，检查连接或目标服务器';
            }
            switch (((string)$response["code"])[0]) {
                case 5:
                    $errMsg .= '目标服务器代码错误，请联系对方';
                    break;
                case 4:
                    $errMsg .= '权限不足以请求资源，请检查对方服务器规范';
                    break;
                default:
                    $errMsg .= '出现未知请求错误';
                    break;
            }
            $responseDetails = ' code:' . $response["code"]
                . ' header:' . $response->body()
                . ' response:' . json_encode($response->headers());
            return $errMsg . $responseDetails;
        })();
        LogService::log('海柔请求','runMany','波次任务分配6.r5f2c3：'.json_encode($errMsg));

        LogService::log(__METHOD__, __FUNCTION__,
            $errMsg ?? ''
            . '请求：' . json_encode($dataToPost)
            . '调用堆栈c：' . json_encode(array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 0, 3))
        );
        $isSuccess = !$errMsg;
        $标记料箱状态=(function() use ($taskMaterialBoxes,$modeName){
            foreach ($taskMaterialBoxes as $taskMaterialBox){
                switch ($modeName){
                    case '缓存架入立架':
                    case '输送线入立架':
                    case '移动立架内位置':
                    $taskMaterialBox->materialBox['status']='在入库中';break;
                    case '立架出至输送线':
                    case '立架出至缓存架':
                    $taskMaterialBox->materialBox['status']='在出库中';break;
                    default:
                        $taskMaterialBox->materialBox['status']='未知';break;
                }
                $taskMaterialBox->materialBox->update();
            }
        })();
        return $isSuccess;
    }

}
