<?php


namespace App\Services;


use App\Batch;
use App\Log;
use App\Station;
use App\StationTask;
use App\StationTaskBatch;
use App\StationTaskBatchType;
use Exception;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\Cache;

class StationTaskBatchService
{
    /** @var StationService $stationService */
    private $stationService;
    /** @var StationTaskBatchTypeService $stationTaskBatchTypeService */
    private $stationTaskBatchTypeService;
    /** @var BatchService $batchService */
    private $batchService;
    /** @var StationTypeService $stationTypeService */
    private $stationTypeService;
    /** @var StationTaskService $stationTaskService */
    private $stationTaskService;
    /** @var ForeignHaiRoboticsService $foreignHaiRoboticsService */
    private $foreignHaiRoboticsService;

    public function __construct()
    {
        $this->stationService = null;
        $this->stationTypeService = null;
        $this->stationTaskBatchTypeService = null;
        $this->batchService = null;
        $this->stationTaskService = null;
        $this->foreignHaiRoboticsService = null;
    }

    /**
     * @param $batches Batch[]
     * @param Collection $stationTasks_toAttach
     * @return Collection
     * @throws Exception
     */
    function createByBatches(array $batches, Collection $stationTasks_toAttach): Collection
    {
        $this->stationService = app('StationService');
        $this->stationTypeService = app('StationTypeService');
        $this->stationTaskBatchTypeService = app('StationTaskBatchTypeService');
        $this->batchService = app('BatchService');

        $stationTaskBatches_toCreate = new Collection();
        $id_stationTaskBatchType = $this->stationTaskBatchTypeService->firstByWhere('name', 'U型线分捡');

        $batches_handled = collect();
        foreach ($batches as $batch) {
            if ($batch['status'] == '未处理') {
                $stationType = $this->stationTypeService->getByBatch($batch);
                $station = $this->stationService->getStation_byType($stationType['name']);
                $stationTaskBatches_toCreate->push([
                    'batch_id' => $batch['id'],
                    'station_id' => $station['id'],
                    'station_task_batch_type_id' => $id_stationTaskBatchType,
                    'status' => '待处理'
                ]);
                $batches_handled->push($batch);
            }
        }
        $this->batchService->updateWhereIn('id', data_get($batches_handled, '*.id'), ['status' => '处理中']);
        $this->insert($stationTaskBatches_toCreate->toArray());
        $this->stationTaskService->registerSubTasks($stationTasks_toAttach,
            $stationTaskBatches_toCreate->map(function ($stationMissionBatch) {
                return [$stationMissionBatch];
            })
        );

        return $stationTaskBatches_toCreate;
    }

    function insert(array $stationMissionBatches): bool
    {
        $inserted = StationTaskBatch::query()->insert($stationMissionBatches);
        LogService::log(__METHOD__, __FUNCTION__, json_encode($stationMissionBatches) .
            '||' . json_encode(array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 0, 3)));
        return $inserted;
    }

    function markManyExcepted(Collection $stationTaskBatches_failed)
    {
        foreach (
            $stationTaskBatches_failed
                 as $stationTaskBatch) {
            if($stationTaskBatch['status']!='异常')
                $this->markExcepted($stationTaskBatch);
        }

        ($logAtFailings_andWait =
            function ($stationTaskBatches_failed) {
                if ($stationTaskBatches_failed->isEmpty()) return;
                LogService::log(__METHOD__, __FUNCTION__,
                    '任务波次异常失败的：' . $stationTaskBatches_failed->toJson()
                    . '调用堆栈：' . json_encode(array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 0, 3))
                );
            })($stationTaskBatches_failed);
    }

    /**
     * @param Collection $stationTaskBatches
     * @return Collection|\Tightenco\Collect\Support\Collection|null 返回执行失败的记录
     */
    function runMany(Collection $stationTaskBatches)
    {
        $stationTaskBatches_failed = null;
        ($execute =
            function (
                Collection $stationTaskBatches, &$stationTaskBatches_failed) {
                if ($stationTaskBatches->isEmpty()) return;
                $stationTaskBatches_failed = collect();
                foreach ($stationTaskBatches as $stationTaskBatch) {
                    $failed = !$this->run($stationTaskBatch);
                    if ($failed) $stationTaskBatches_failed->push($stationTaskBatch);
                }
            })($stationTaskBatches, $stationTaskBatches_failed);

        ($logAtFailings_andWait =
            function ($stationTaskBatches_failed) {
                if ($stationTaskBatches_failed->isEmpty()) return;
                $retry_after_sec = config('task.batchTask.retry_after_sec');
                LogService::log(__METHOD__, __FUNCTION__,
                    '任务波次没有执行完的，' . $retry_after_sec . '秒后准备重试：' . $stationTaskBatches_failed->toJson()
                    . '调用堆栈：' . json_encode(array_slice(debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS), 0, 3))
                );
                sleep($retry_after_sec);
            })($stationTaskBatches_failed);

        $execute                    ($stationTaskBatches_failed, $stationTaskBatches_failed); //再次尝试
        $this->markManyExcepted  ($stationTaskBatches_failed);
        return                      $stationTaskBatches_failed;
    }

    function run(StationTaskBatch $stationTaskBatch): bool
    {
        $toLocation         = $stationTaskBatch['station']['code'];
        $groupPrefix        = $stationTaskBatch['id'];
        $taskMaterialBoxes  = $stationTaskBatch['stationTask']['taskMaterialBoxes'] ??
            function () use ($stationTaskBatch) {
                throw new Exception('找不到料箱:' . json_encode($stationTaskBatch));
            };
        $isFetchedFromRobotics          = $this->foreignHaiRoboticsService->
                                    fetchGroupToProcessor($toLocation, $taskMaterialBoxes, $groupPrefix);
        ($markNewStatus
            =function()use($isFetchedFromRobotics,$stationTaskBatch){
            $isFetchedFromRobotics?
                $this->markProcessing($stationTaskBatch):
                $this->markExcepted($stationTaskBatch);
        })();
        return $isFetchedFromRobotics;
    }

    function markProcessing(StationTaskBatch $stationTaskBatch)
    {
        $stationTaskBatch   ['status']                  = '处理中';
        $stationTaskBatch   ->update();
        $stationTaskBatch   ->loadMissing               ('stationTask');
        $stationTaskBatch   ['stationTask']['status']   = '异常';
        $stationTaskBatch   ['stationTask']             ->update();
    }

    function markExcepted(StationTaskBatch $stationTaskBatch)
    {
        $stationTaskBatch['status'] = '异常';
        $stationTaskBatch           ->update();
    }
}
