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

namespace app\common\model;

use cores\BaseModel;
use app\common\model\store\shop\Order as ShopOrder;
use app\common\service\Order as OrderService;
use app\common\service\order\Shipping as ShippingService;
use app\common\service\order\Complete as OrderCompleteService;
use app\common\service\order\source\Factory as OrderSourceFactory;
use app\common\enum\order\PayStatus as PayStatusEnum;
use app\common\enum\order\OrderStatus as OrderStatusEnum;
use app\common\enum\order\DeliveryType as DeliveryTypeEnum;
use app\common\enum\order\ReceiptStatus as ReceiptStatusEnum;
use app\common\enum\order\DeliveryStatus as DeliveryStatusEnum;
use app\common\library\helper;
use think\model\relation\HasOne;
use think\model\relation\HasMany;
use think\model\relation\BelongsTo;

/**
 * 订单模型
 * Class Order
 * @package app\common\model
 */
class Order extends BaseModel
{
    // 定义表名
    protected $name = 'order';

    // 定义表名(外部引用)
    public static string $tableName = 'order';

    // 定义主键
    protected $pk = 'order_id';

    // 定义别名
    protected string $alias = 'order';

    /**
     * 追加字段
     * @var array
     */
    protected $append = [
        'state_text',   // 售后单状态文字描述
    ];

    /**
     * 订单商品列表
     * @return HasMany
     */
    public function goods(): HasMany
    {
        $module = self::getCalledModule();
        return $this->hasMany("app\\{$module}\\model\\OrderGoods")->withoutField('content');
    }

    /**
     * 关联订单发货单
     * @return hasMany
     */
    public function delivery(): HasMany
    {
        $module = self::getCalledModule();
        return $this->hasMany("app\\{$module}\\model\\order\\Delivery");
    }

    /**
     * 关联订单收货地址表
     * @return HasOne
     */
    public function address(): HasOne
    {
        $module = self::getCalledModule();
        return $this->hasOne("app\\{$module}\\model\\OrderAddress");
    }

    /**
     * 关联订单收货地址表
     * @return HasOne
     */
    public function extract(): HasOne
    {
        $module = self::getCalledModule();
        return $this->hasOne("app\\{$module}\\model\\OrderExtract");
    }

    /**
     * 关联自提门店表
     * @return BelongsTo
     */
    public function extractShop(): BelongsTo
    {
        $module = self::getCalledModule();
        return $this->belongsTo("app\\{$module}\\model\\store\\Shop", 'extract_shop_id');
    }

    /**
     * 关联门店店员表
     * @return BelongsTo
     */
    public function extractClerk(): BelongsTo
    {
        $module = self::getCalledModule();
        return $this->belongsTo("app\\{$module}\\model\\store\\shop\\Clerk", 'extract_clerk_id');
    }

    /**
     * 关联用户表
     * @return BelongsTo
     */
    public function user(): BelongsTo
    {
        $module = self::getCalledModule();
        return $this->belongsTo("app\\{$module}\\model\\User");
    }

    /**
     * 关联物流公司表 (仅用于兼容旧物流数据)
     * @return BelongsTo
     */
    public function express(): BelongsTo
    {
        $module = self::getCalledModule();
        return $this->belongsTo("app\\{$module}\\model\\Express");
    }

    /**
     * 关联模型:第三方交易记录
     * @return BelongsTo
     */
    public function trade(): BelongsTo
    {
        $module = self::getCalledModule();
        return $this->belongsTo("app\\{$module}\\model\\PaymentTrade", 'trade_id', 'trade_id');
    }

    /**
     * 获取器:订单状态文字描述
     * @param $value
     * @param $data
     * @return string
     */
    public function getStateTextAttr($value, $data): string
    {
        // 订单状态
        if ($data['order_status'] != OrderStatusEnum::NORMAL) {
            return OrderStatusEnum::data()[$data['order_status']]['name'];
        }
        // 付款状态
        if ($data['pay_status'] == PayStatusEnum::PENDING) {
            return '待支付';
        }
        // 发货状态
        if ($data['delivery_status'] != DeliveryStatusEnum::DELIVERED) {
            $enum = [DeliveryStatusEnum::NOT_DELIVERED => '待发货', DeliveryStatusEnum::PART_DELIVERED => '部分发货'];
            return $enum[$data['delivery_status']];
        }
        // 收货状态
        if ($data['receipt_status'] == ReceiptStatusEnum::NOT_RECEIVED) {
            return '待收货';
        }
        return $value;
    }

    /**
     * 获取器:订单金额(含优惠折扣)
     * @param $value
     * @param $data
     * @return string
     */
    public function getOrderPriceAttr($value, $data): string
    {
        // 兼容旧数据:订单金额
        if ($value == 0) {
            return helper::bcadd(helper::bcsub($data['total_price'], $data['coupon_money']), $data['update_price']);
        }
        return $value;
    }

    /**
     * 获取器:改价金额(差价)
     * @param $value
     * @return array
     */
    public function getUpdatePriceAttr($value): array
    {
        return [
            'symbol' => $value < 0 ? '-' : '+',
            'value' => sprintf('%.2f', abs((float)$value))
        ];
    }

    /**
     * 获取器:付款时间
     * @param $value
     * @return false|string
     */
    public function getPayTimeAttr($value)
    {
        return format_time($value);
    }

    /**
     * 获取器:发货时间
     * @param $value
     * @return false|string
     */
    public function getDeliveryTimeAttr($value)
    {
        return format_time($value);
    }

    /**
     * 获取器:收货时间
     * @param $value
     * @return false|string
     */
    public function getReceiptTimeAttr($value)
    {
        return format_time($value);
    }

    /**
     * 获取器:来源记录的参数
     * @param $json
     * @return array
     */
    public function getOrderSourceDataAttr($json): array
    {
        return $json ? helper::jsonDecode($json) : [];
    }

    /**
     * 修改器:来源记录的参数
     * @param array $data
     * @return string
     */
    public function setOrderSourceDataAttr(array $data): string
    {
        return helper::jsonEncode($data);
    }

    /**
     * 生成订单号
     * @return string
     */
    public function orderNo(): string
    {
        return OrderService::createOrderNo();
    }

    /**
     * 订单详情
     * @param $where
     * @param array $with
     * @return static|array|null
     */
    public static function detail($where, array $with = [])
    {
        is_array($where) ? $filter = $where : $filter['order_id'] = (int)$where;
        return self::get($filter, $with);
    }

    /**
     * 批量获取订单列表
     * @param $orderIds
     * @param array $with
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getListByIds(array $orderIds, array $with = [])
    {
        $data = $this->getListByInArray('order_id', $orderIds, $with);
        return helper::arrayColumn2Key($data, 'order_id');
    }

    /**
     * 批量获取订单列表
     * @param $field
     * @param $data
     * @param array $with
     * @return \think\Collection
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    private function getListByInArray($field, $data, array $with = []): \think\Collection
    {
        return $this->with($with)
            ->where($field, 'in', $data)
            ->where('is_delete', '=', 0)
            ->select();
    }

    /**
     * 根据订单号批量查询
     * @param $orderNos
     * @param array $with
     * @return \think\Collection
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getListByOrderNos($orderNos, array $with = []): \think\Collection
    {
        return $this->getListByInArray('order_no', $orderNos, $with);
    }

    /**
     * 批量更新订单
     * @param $orderIds
     * @param $data
     * @return bool|false
     */
    public function onBatchUpdate($orderIds, $data): bool
    {
        return static::updateBase($data, [['order_id', 'in', $orderIds]]);
    }

    /**
     * 确认核销(自提订单)
     * @param int $clerkId 门店店员ID
     * @param bool $syncMpWeixinShipping 同步至微信小程序
     * @return bool|false|int
     */
    public function confirmExtract(int $clerkId, bool $syncMpWeixinShipping = false)
    {
        if (
            $this['pay_status'] != PayStatusEnum::SUCCESS
            || $this['delivery_type'] != DeliveryTypeEnum::EXTRACT
            || $this['delivery_status'] == DeliveryStatusEnum::DELIVERED
            || in_array($this['order_status'], [OrderStatusEnum::CANCELLED, OrderStatusEnum::APPLY_CANCEL])
        ) {
            $this->error = '该订单不满足核销条件';
            return false;
        }
        // 验证订单是否满足发货条件
        if (!$this->verifyDeliveryForExtract($this)) {
            return false;
        }
        return $this->transaction(function () use ($clerkId, $syncMpWeixinShipping) {
            // 更新订单状态:已发货、已收货
            $status = $this->save([
                'extract_clerk_id' => $clerkId,  // 核销员
                'delivery_status' => DeliveryStatusEnum::DELIVERED,
                'delivery_time' => time(),
                'receipt_status' => ReceiptStatusEnum::RECEIVED,
                'receipt_time' => time(),
                'order_status' => OrderStatusEnum::COMPLETED
            ]);
            // 新增订单核销记录
            ShopOrder::add($this['order_id'], $this['extract_shop_id'], $this['extract_clerk_id']);
            // 执行订单完成后的操作
            $OrderCompleteService = new OrderCompleteService();
            $OrderCompleteService->complete([$this], static::$storeId);
            // 发货信息同步微信平台
            (new ShippingService)->syncMpWeixinShipping($this, [
                // 同步至微信小程序《发货信息录入》
                'syncMpWeixinShipping' => (int)$syncMpWeixinShipping,
                // 物流模式:1物流配送 3虚拟商品 4用户自提
                'logisticsType' => ShippingService::DELIVERY_EXTRACT,
            ]);
            return $status;
        });
    }

    /**
     * 验证订单是否满足发货条件
     * @param $order
     * @return bool
     */
    private function verifyDeliveryForExtract($order): bool
    {
        $orderSource = OrderSourceFactory::getFactory($order['order_source']);
        if (!$orderSource->checkOrderByDelivery($order)) {
            $this->error = $orderSource->getError();
            return false;
        }
        return true;
    }
}