<?php


namespace App\Services;


use App\Jobs\LogisticSFSync;
use App\Jobs\LogisticYDSync;
use App\Jobs\LogisticYTOSync;
use App\Jobs\LogisticZopSync;
use App\OrderPackage;
use Carbon\Carbon;
use Exception;
use Illuminate\Database\Eloquent\Collection;

class OrderPackageReceivedSyncService
{
    protected $logisticSFService;
    protected $logisticZopService;

    /**
     * 同步快递信息
     * 1 如果当前时间大于初始化时间 每日执行一次，更新order_packages中创建时间大于初始化时间，没有异常，用户未收货的全部订单的快递路由状态
     * 2 如果当前时间小于等于初始化时间，执行初始化脚本，将数据库中全部小于等于初始化时间的数据更新
     * @throws Exception
     */
    public function syncLogisticRoute()
    {
        LogService::log(OrderPackageReceivedSyncService::class, "同步快递信息定时方法", '');
        ini_set('max_execution_time', 60 * 60);
        //初始化时间 2020-12-31 23:59:59
        $initDate = Carbon::parse(config('api_logistic.init_date'));
        $query = OrderPackage::query()
            ->select(['logistic_number', 'order_id'])
            ->with(['order' => function ($query) {
                return $query->with('logistic');
            }]);
        if (Carbon::now()->lte($initDate)) {//当前时间小于等于初始化时间
            //初始化查询一个月的数据，exception为否
            $query = $query->where('sent_at', '>=', $initDate->subDays((int)config('api_logistic.days'))->toDateTimeString())
                ->whereNull('received_at');
        } else {//查询20天以内的数据
            $query = $query->where('sent_at', '>=', now()->subDays(20))
                ->whereNull('received_at');
        }
        $query->chunk(2000, function ($orderPackages) {
            $logisticNumbers = $this->buildData($orderPackages);
            //sf
            if (array_key_exists('SF', $logisticNumbers)) {
                $SFLogisticNumbers = $logisticNumbers['SF'];
                LogService::log(OrderPackageReceivedSyncService::class, "同步快递信息定时方法-SF", '');
                foreach ($SFLogisticNumbers as $logisticNumber) {
                    LogisticSFSync::dispatch($logisticNumber);
                }
            }
            //更新中通
            if (array_key_exists('ZTO', $logisticNumbers)) {
                $ZTOLogisticNumbers = $logisticNumbers['ZTO'];
                LogService::log(OrderPackageReceivedSyncService::class, "同步快递信息定时方法-ZTO", '');
                foreach ($ZTOLogisticNumbers as $logisticNumber) {
                    LogisticZopSync::dispatch($logisticNumber);
                }
            }
            //更新韵达
            if (array_key_exists('YUNDA', $logisticNumbers)) {
                $YDLogisticNumbers = $logisticNumbers['YUNDA'];
                LogService::log(OrderPackageReceivedSyncService::class, "同步快递信息定时方法-YUNDA", '');
                foreach ($YDLogisticNumbers as $logistic_number) {
                    LogisticYDSync::dispatch($logistic_number);
                }
            }
            //更新圆通
            if (array_key_exists('YTO', $logisticNumbers)) {
                $YTOLogisticNumbers = $logisticNumbers['YTO'];
                LogService::log(OrderPackageReceivedSyncService::class, "同步快递信息定时方法-YTO", '');
                foreach ($YTOLogisticNumbers as $logistic_number) {
                    LogisticYTOSync::dispatch($logistic_number);
                }
            }
        });
    }

    /**
     * 根据传递的承运商与快递单号更新快递信息
     * @param array $logisticNumbers 快递单号
     * example： ['SF' => ['SF1038651915891', 'SF1038651413847', 'SF1038611050071'],'ZT'=>['75424148714142','548464120822', '75424147834290']....]
     * @throws Exception 快递接口调用或者返回的信息有误，无法更新指定的快递路由信息
     */
    public function syncLogisticRouteApi(array $logisticNumbers)
    {
        $this->update($this->getLogisticRoutes($logisticNumbers));
    }

    /**
     * 获取快件揽收信息
     * @param array $request [
     * 'SF' => ['SF1038651915891', 'SF1038651413847', 'SF1038611050071'],
     * 'ZT'=>['75424148714142','548464120822', '75424147834290']
     * ]
     * @return array
     * @throws Exception
     */
    public function getLogisticRoutes(array $request): array
    {
        $this->logisticSFService = new LogisticSFService();
        $resultSF = [];
        $resultYD = [];
        $resultYT = [];
        $resultOther = [];
        foreach ($request as $key => $logisticNums) {
            switch ($key) {
                case "SF":
                    $resultSF = $this->logisticSFService->get($logisticNums);
                    break;
                case "YD":
                    $resultYD = [];
                    break;
                case "YT":
                    $resultYT = [];
                    break;
                default:
                    $resultOther = [];
                    break;
            }
        }
        return array_merge($resultSF, $resultYD, $resultYT, $resultOther);
    }

    /**
     * 根据快递单号更新状态
     * @param array $orderPackages
     */
    public function update(array $orderPackages)
    {
        foreach ($orderPackages as $data) {
            if (empty($data)) continue;
            $orderPackage = OrderPackage::query()->where('logistic_number', $data['logistic_number'])->first();
            if (isset($data['status'])) $orderPackage->status = $data['status'];
            if (isset($data['received_at'])) $orderPackage->received_at = $data['received_at'];
            if (isset($data['exception'])) $orderPackage->exception = $data['exception'];
            if (isset($data['transfer_status'])) $orderPackage->transfer_status = $data['transfer_status'];
            if (isset($data['exception_type'])) $orderPackage->exception_type = $data['exception_type'];
            $orderPackage->save();
        }
    }


    /**
     * 将orderPackage集合分类并摘取指定数据
     * @param Collection $orderPackages
     * @return array
     */
    private function buildData(Collection $orderPackages): array
    {
        $data = [];
        foreach ($orderPackages as $orderPackage) {
            try {
                $logisticCode = $orderPackage->order->logistic->code;
            } catch (Exception $e) {
                continue;
            }
            $key = config('api_logistic.logistic.' . $logisticCode);
            if (!isset($data[$key])) {
                $data[$key] = [];
            }
            $data[$key][] = $orderPackage->logistic_number;
        }
        return $data;
    }


    /**
     * @param array $data
     * @param $lastRouteDate
     * @return array
     */
    public function setExceptionType(array $data, $lastRouteDate): array
    {
        $logistic_number = $data['logistic_number'];
        /** @var OrderPackage $orderPackage */
        $orderPackage = OrderPackage::query()->with('order')->where('logistic_number', $logistic_number)->first();
        $delivered_duration = now()->diffInHours(Carbon::parse($orderPackage['sent_at']));
        $last_routed_duration = now()->diffInHours(Carbon::parse($lastRouteDate));
        $VALID_HOURS = 4;
        $SHORT_RESPONSE_HOURS = (function ($province) {
            switch ($province) {
                case '浙江省':
                case '江苏省':
                case '上海':
                case '安徽省':
                    return 24;
                case '北京':
                case '天津':
                case '江西省':
                case '湖北省':
                case '湖南省':
                case '广东省':
                case '福建省':
                case '山东省':
                case '河北省':
                case '河南省':
                case '山西省':
                case '四川省':
                case '陕西省':
                case '重庆':
                case '广西壮族自治区':
                case '贵州省':
                case '云南省':
                case '海南省':
                case '吉林省':
                case '黑龙江省':
                case '辽宁省':
                    return 72;
                case '青海省':
                case '宁夏回族自治区':
                case '甘肃省':
                case '内蒙古自治区':
                case '新疆维吾尔自治区':
                case '西藏自治区':
                    return 120;
                default:
                    return 24;
            }
        })($orderPackage->order->province);
        $LONG_RESPONSE_HOURS = (function ($province) {
            switch ($province) {
                case '浙江省':
                case '江苏省':
                case '上海':
                case '安徽省':
                    return 72;
                case '北京':
                case '天津':
                case '江西省':
                case '湖北省':
                case '湖南省':
                case '广东省':
                case '福建省':
                case '山东省':
                case '河北省':
                case '河南省':
                case '山西省':
                case '四川省':
                case '陕西省':
                case '重庆':
                case '广西壮族自治区':
                case '贵州省':
                case '云南省':
                case '海南省':
                case '吉林省':
                case '黑龙江省':
                case '辽宁省':
                    return 120;
                case '青海省':
                case '宁夏回族自治区':
                case '甘肃省':
                case '内蒙古自治区':
                case '新疆维吾尔自治区':
                case '西藏自治区':
                    return 168;
                default:
                    return 72;
            }
        })($orderPackage->order->province);
        $SENDING_RESPONSE_HOURS = 48;
        $HAVEN_SECOND_GOT_HOURS = 24;
        $IS_ROUTED = 1;               //0000 0001 有路由信息
        $IS_IN_VALID_TIME = 2;        //0000 0010 大于4小时
        $IS_WEIGHED = 4;              //0000 0100 称重过
        $IS_RECEIVED = 8;          //0000 1000 已经收货
        $IS_SENDING = 16;             //0001 0000 正在派送
        $IS_SHORT_NO_RESPONSE = 32;     //0010 0000 中转异常
        $IS_LONG_NO_RESPONSE = 64;     //0010 0000 疑似丢件
        $IS_SENDING_NO_RESPONSE = 128;     //0010 0000 派送异常
        $IS_SECOND_ROUTE_HAVE = 256;     //0100 0000 揽件异常
        $conclusion = (function () use (
            $data, $delivered_duration, $last_routed_duration,
            $VALID_HOURS, $IS_ROUTED, $IS_IN_VALID_TIME, $IS_WEIGHED, $IS_RECEIVED, $IS_SENDING, $IS_SHORT_NO_RESPONSE, $IS_LONG_NO_RESPONSE, $IS_SENDING_NO_RESPONSE,
            $SHORT_RESPONSE_HOURS, $LONG_RESPONSE_HOURS, $SENDING_RESPONSE_HOURS, $HAVEN_SECOND_GOT_HOURS, $IS_SECOND_ROUTE_HAVE,
            $orderPackage
        ) {
            $conclusion = 0;
            $conclusion |= !empty($data['transfer_status']) ? $IS_ROUTED : 0;
            $conclusion |= ($delivered_duration > $VALID_HOURS) ? $IS_IN_VALID_TIME : 0;
            $conclusion |= ($orderPackage->weighed_at) ? $IS_WEIGHED : 0;
            $conclusion |= ($data['status'] == '已收件') ? $IS_RECEIVED : 0;
            $conclusion |= ($data['status'] == '派送中') ? $IS_SENDING : 0;//
            $conclusion |= ($last_routed_duration > $SHORT_RESPONSE_HOURS && $last_routed_duration < $LONG_RESPONSE_HOURS) ? $IS_SHORT_NO_RESPONSE : 0;
            $conclusion |= ($last_routed_duration > $LONG_RESPONSE_HOURS) ? $IS_LONG_NO_RESPONSE : 0;
            $conclusion |= ($last_routed_duration > $SENDING_RESPONSE_HOURS && $data['status'] == '派送中') ? $IS_SENDING_NO_RESPONSE : 0;
            $conclusion |= ($delivered_duration > $HAVEN_SECOND_GOT_HOURS && $data['routes_length'] < 2) ? $IS_SECOND_ROUTE_HAVE : 0;//超过指定时间，路由信息小于两条
            return $conclusion;
        })();
        switch ($conclusion) {
            case $IS_IN_VALID_TIME:
                $data['exception_type'] = '疑似库内丢件';
                break;
            case $IS_IN_VALID_TIME | $IS_WEIGHED:
                $data['exception_type'] = '揽件异常';
                break;
            case $IS_ROUTED | $IS_IN_VALID_TIME | $IS_SHORT_NO_RESPONSE:
            case $IS_ROUTED | $IS_IN_VALID_TIME | $IS_SHORT_NO_RESPONSE | $IS_WEIGHED:
                $data['exception_type'] = '中转异常';
                break;
            case $IS_ROUTED | $IS_IN_VALID_TIME | $IS_LONG_NO_RESPONSE:
            case $IS_ROUTED | $IS_IN_VALID_TIME | $IS_LONG_NO_RESPONSE | $IS_WEIGHED:
                $data['exception_type'] = '疑似丢件';
                break;
            default:
                break;
        }
        if ($conclusion
            == ($conclusion | $IS_ROUTED | $IS_IN_VALID_TIME | $IS_SENDING | $IS_SENDING_NO_RESPONSE)) {
            $data['exception_type'] = '派件异常';
        }
        if ($conclusion
            == ($conclusion | $IS_SECOND_ROUTE_HAVE)) {
            $data['exception_type'] = '派件异常';
            $data['exception'] = '是';

        }
        switch ($conclusion) {
            case $IS_IN_VALID_TIME:
            case $IS_IN_VALID_TIME | $IS_WEIGHED:
            case $IS_ROUTED | $IS_SHORT_NO_RESPONSE:
            case $IS_LONG_NO_RESPONSE:
                $data['exception'] = '是';
                break;
            default:
                break;
        }
        return [
            'exception_type' => array_key_exists('exception_type', $data) ? $data['exception_type'] : null,
            'exception' => array_key_exists('exception', $data) ? $data['exception'] : null,
        ];
    }
}
