<?php
// +----------------------------------------------------------------------
// | 萤火商城系统 [ 致力于通过产品和服务,帮助商家高效化开拓市场 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2017~2023 https://www.yiovo.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed 这不是一个自由软件,不允许对程序代码以任何形式任何目的的再发行
// +----------------------------------------------------------------------
// | Author: 萤火科技 <admin@yiovo.com>
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace app\store\service\order;

use think\facade\Db;
use app\store\model\eorder\Setting as SettingModel;
use app\store\model\store\Address as StoreAddressModel;
use app\store\model\eorder\Template as EOrderTemplateModel;
use app\store\service\order\Delivery as DeliveryService;
use app\common\service\BaseService;
use app\common\service\Order as OrderService;
use app\common\service\Goods as GoodsService;
use app\common\service\order\Shipping as ShippingService;
use app\common\library\helper;
use app\common\library\eOrder\Facade as EOrderFacade;
use app\store\model\Order as OrderModel;
use cores\exception\BaseException;

/**
 * 服务层:电子面单发货事件
 * Class EOrder
 * @package app\store\service\order
 */
class EOrder extends BaseService
{
    // 发货方式: 电子面单
    const DELIVERY_METHOD_EORDER = 30;

    // 电子面单模板
    private string $eorderContent = '';

    /**
     * 电子面单发货
     * @param int $orderId
     * @param array $param
     * @return bool
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function eorder(int $orderId, array $param): bool
    {
        // 设置默认的参数
        $param = helper::setQueryDefaultValue($param, [
            // 发货方式: 电子面单
            'deliveryMethod' => self::DELIVERY_METHOD_EORDER,
            // 整单发货
            'isAllPack' => false,
            // 发货的商品
            'packGoodsData' => [],
            // 同步至微信小程序《发货信息录入》
            'syncMpWeixinShipping' => 1,
        ]);
        // 获取订单详情
        $detail = OrderModel::detail($orderId);
        // 验证订单是否满足发货条件
        $DeliveryService = new DeliveryService;
        if (!$DeliveryService->verifyDelivery([$detail])) {
            $this->error = $DeliveryService->getError();
            return false;
        }
        // 执行订单发货操作
        Db::transaction(function () use ($DeliveryService, $orderId, $detail, $param) {
            // 获取电子面单模板记录
            $templateInfo = $this->getEOrderTemplateInfo($param['templateId']);
            // 请求API获取电子面单内容
            $eOrderTemplate = $this->getEOrderTemplate($orderId, $param['addressId'], $param['packGoodsData'], $templateInfo);
            // 订单发货事件
            $DeliveryService->deliveryEvent($detail, array_merge($param, [
                // 物流公司ID
                'expressId' => $templateInfo['express_id'],
                // 物流单号
                'expressNo' => $eOrderTemplate['expressNo'],
                // 电子面单内容
                'eorderHtml' => $this->getEOrderContent(),
            ]));
            // 获取已发货的订单
            $completed = OrderModel::detail($orderId, ['goods', 'trade']);
            // 发货信息同步微信平台
            (new ShippingService)->syncMpWeixinShipping($completed, [
                // 同步至微信小程序《发货信息录入》
                'syncMpWeixinShipping' => $param['syncMpWeixinShipping'],
                // 物流模式:1物流配送 3虚拟商品 4用户自提
                'logisticsType' => ShippingService::DELIVERY_EXPRESS,
                // 物流公司ID
                'expressId' => $templateInfo['express_id'],
                // 物流单号
                'expressNo' => $eOrderTemplate['expressNo'],
            ]);
        });
        return true;
    }

    /**
     * 电子面单发货
     * @param int $orderId 订单ID
     * @param int $addressId 收货地址ID
     * @param array $packGoodsData 发货的商品
     * @param $templateInfo
     * @return array
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function getEOrderTemplate(int $orderId, int $addressId, array $packGoodsData, $templateInfo): array
    {
        // 订单详情
        $orderInfo = OrderModel::detail($orderId, ['goods', 'address']);
        // 获取电子面单模板记录
        $templateInfo = $this->getEOrderTemplateInfo($templateInfo['template_id']);
        // 获取电子面单基础设置
        $options = $this->getEOrderOption();
        // 实例化电子面单API驱动
        $EOrderApi = EOrderFacade::store($templateInfo['provider']);
        // 设置API配置参数
        $EOrderApi->setOptions($options['providerConfig']);
        // 设置电子面单模板信息
        $EOrderApi->setEleTemplate($this->getEleTemplate($templateInfo));
        // 设置发货单信息
        $EOrderApi->setDeliverOrder($this->getDeliverOrder($orderInfo, $packGoodsData));
        // 设置发件人信息
        $EOrderApi->setSender($this->getSender($addressId));
        // 设置收件人信息
        $EOrderApi->setReceiver($this->getReceiver($orderInfo));
        // 获取电子面单
        $result = $EOrderApi->handle();
        // 记录电子面单的内容
        $this->eorderContent = $result['content'];
        return $result;
    }

    /**
     * 获取收件人信息
     * @param $orderInfo
     * @return array
     */
    private function getReceiver($orderInfo): array
    {
        $addressInfo = $orderInfo['address'];
        return [
            'name' => $addressInfo['name'],
            'mobile' => $addressInfo['phone'],
            'provinceName' => $addressInfo['region']['province'],
            'cityName' => $addressInfo['region']['city'],
            'expAreaName' => $addressInfo['region']['region'],
            'address' => $addressInfo['detail']
        ];
    }

    /**
     * 获取发件人信息
     * @param int $addressId
     * @return array
     */
    private function getSender(int $addressId): array
    {
        $addressInfo = StoreAddressModel::detail($addressId);
        return [
            'name' => $addressInfo['name'],
            'mobile' => $addressInfo['phone'],
            'provinceName' => $addressInfo['region']['province'],
            'cityName' => $addressInfo['region']['city'],
            'expAreaName' => $addressInfo['region']['region'],
            'address' => $addressInfo['detail']
        ];
    }

    /**
     * 获取发货单信息
     * @param $orderInfo
     * @param array $packGoodsData 发货的商品
     * @return array
     */
    private function getDeliverOrder($orderInfo, array $packGoodsData): array
    {
        // 构建商品信息
        $commodity = $this->buildCommodity($orderInfo, $packGoodsData);
        return [
            // 订单编号,不可重复提交
            'orderCode' => OrderService::createOrderNo(),
            // 包裹总重量kg
            'weight' => helper::getArrayColumnSum($commodity, 'goodsWeight'),
            // 包裹数
            'quantity' => 1,
            //是 否通知快递员上门揽件  0通知 1不通知
            'isNotice' => 1,
            // 商品信息
            'commodity' => $commodity,
        ];
    }

    /**
     * 构建商品信息
     * @param $orderInfo
     * @param array $packGoodsData 发货的商品
     * @return array
     */
    private function buildCommodity($orderInfo, array $packGoodsData): array
    {
        $commodity = [];
        $orderGoodsIds = helper::getArrayColumn($packGoodsData, 'orderGoodsId');
        foreach ($orderInfo['goods'] as $goods) {
            if (in_array($goods['order_goods_id'], $orderGoodsIds)) {
                $goodsProps = !empty($goods['goods_props']) ? GoodsService::goodsPropsToAttr($goods['goods_props']) : '';
                $commodity[$goods['order_goods_id']] = [
                    'orderGoodsId' => $goods['order_goods_id'],   // 订单商品ID
                    'goodsName' => $goods['goods_name'],          // 商品名称
                    'goodsProps' => $goodsProps,                  // 商品规格
                    'goodsquantity' => $goods['total_num'],       // 商品数量
                    'goodsWeight' => $goods['goods_weight']       // 商品重量kg
                ];
            }
        }
        return $commodity;
    }

    /**
     * 获取电子面单模板信息
     * @param $templateInfo
     * @return array
     * @throws BaseException
     */
    private function getEleTemplate($templateInfo): array
    {
        $shipperCodeKey = $templateInfo['provider'] === 'kdniao' ? 'kdniao_code' : 'kuaidi100_code';
        $shipperCode = $templateInfo['express'][$shipperCodeKey] ?? null;
        if (empty($shipperCode)) {
            $codeEnum = ['kdniao' => '快递鸟', 'kd100' => '快递100'];
            throwError("很抱歉,物流公司记录不存在{$codeEnum[$templateInfo['provider']]}编码,请先补充");
        }
        return [
            'shipperCode' => $templateInfo['express'][$shipperCodeKey],
            'customerName' => $templateInfo['config']['customerName'],
            'customerPwd' => $templateInfo['config']['customerPwd'],
            'sendSite' => $templateInfo['config']['sendSite'],
            'monthCode' => $templateInfo['config']['monthCode'],
            'payType' => $templateInfo['config']['payType'],
        ];
    }

    /**
     * 获取电子面单模板记录
     * @param int $templateId
     * @return EOrderTemplateModel|false|\think\Model
     * @throws BaseException
     */
    private function getEOrderTemplateInfo(int $templateId)
    {
        $model = EOrderTemplateModel::detail($templateId);
        empty($model) && throwError('未找到电子面单模板记录');
        return EOrderTemplateModel::related($model, ['express']);
    }

    /**
     * 获取电子面单基础设置
     * @return array|mixed
     * @throws BaseException
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function getEOrderOption()
    {
        $options = SettingModel::getBasic();
        !$options['enable'] && throwError('很抱歉,电子面单功能尚未开启');
        return $options;
    }

    /**
     * 返回电子面单内容
     * @return string
     */
    public function getEOrderContent(): string
    {
        return $this->eorderContent;
    }
}