<?php

namespace Modules\Mall\Facades;

use Exception;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Modules\Mall\Events\OrderCreated;
use Modules\Mall\Models\Address;
use Modules\Mall\Models\Goods;
use Modules\Mall\Models\Order as OrderModel;
use Modules\User\Models\User;

class Order
{

    /**
     * @var int
     */
    protected int $user_id;

    /**
     * @var float
     */
    protected float $total;

    /**
     * @var Address
     */
    protected Address $address;

    /**
     * @var array
     */
    protected Collection $items;

    /**
     * @var string
     */
    protected string $remark;

    /**
     * @var int
     */
    protected int $type;
    protected int $channel = 1;

    /**
     * @var array
     */
    protected array $source;

    /**
     * Notes: 设置当前用户
     *
     * @Author: 玄尘
     * @Date  : 2021/5/19 10:40
     * @param  User  $user
     * @return $this
     */
    public function user(User $user): Order
    {
        $this->user_id = $user->getAuthIdentifier();;

        return $this;
    }

    /**
     * Notes: 设置类型
     *
     * @Author: 玄尘
     * @Date  : 2021/10/18 10:03
     * @param $type
     * @return $this
     */
    public function type($type): Order
    {
        $this->type = $type;

        return $this;
    }

    /**
     * Notes: 订单渠道
     *
     * @Author: 玄尘
     * @Date: 2022/9/8 15:51
     */
    public function channel($channel): self
    {
        $this->channel = $channel;
        return $this;
    }

    /**
     * Notes: 设置订单备注信息
     *
     * @Author: 玄尘
     * @Date  : 2021/5/19 10:40
     * @param  string|null  $remark
     * @return $this
     */
    public function remark(?string $remark = ''): Order
    {
        $this->remark = $remark;

        return $this;
    }

    /**
     * Notes: 附加数据
     *
     * @Author: 玄尘
     * @Date  : 2021/5/19 10:40
     * @param  array  $source
     * @return $this
     */
    public function source(array $source): Order
    {
        $this->source = $source;

        return $this;
    }

    /**
     * Notes: 设置商品数据
     *
     * @Author: 玄尘
     * @Date  : 2021/5/14 13:58
     * @param $items
     * @return $this
     */
    public function items($items): Order
    {
        $this->items = new Collection($items);

        return $this;
    }

    /**
     * Notes: 设置订单收货地址
     *
     * @Author: 玄尘
     * @Date  : 2021/5/19 10:41
     * @param  Address  $address
     * @return $this
     */
    public function address(Address $address): Order
    {
        $this->address = $address;

        return $this;
    }

    /**
     * Notes: 创建订单
     *
     * @Author: 玄尘
     * @Date  : 2021/5/19 10:42
     * @return Collection
     * @throws \Exception
     */
    public function create(): Collection
    {
        if ($this->items->isEmpty()) {
            throw new Exception('无法创建无内容的订单');
        }

        if (! is_numeric($this->user_id)) {
            throw new Exception('必须先设置订单用户');
        }
        $splits = $this->splitOrderByShop();

        DB::beginTransaction();

        try {
            $orders = [];
            foreach ($splits as $split) {
                $orders[] = $this->createOne($split['items']);
            }

            DB::commit();
            $result = new Collection($orders);

            $result->total = $this->total();

            return $result;
        } catch (Exception $exception) {
            DB::rollBack();
            throw new Exception($exception->getMessage());
        }
    }

    /**
     * Notes: 按照商户，对订单进行分组
     *
     * @Author: 玄尘
     * @Date  : 2021/5/19 10:43
     * @return mixed
     * @throws \Exception
     */
    public function splitOrderByShop()
    {
        if (empty($this->items)) {
            throw new Exception('无法创建无内容的订单');
        }

        return $this->items->groupBy('orderby')->map(function ($items, $key) {
            /**
             * 计算分订单总价格
             */
            $items->amount = $items->reduce(function ($total, $item) {
                return $total + $item->total();
            });

            /**
             * 计算分订单总数量
             */
            $items->qty = $items->reduce(function ($qty, $item) {
                return $qty + $item->qty;
            });

            /**
             * 计算分订单运费
             */
            $items->freight = $items->reduce(function ($total, $item) {
                return $total + $item->freight();
            });

            /**
             * 回传店铺ID
             */
            $items->shop_id = $items->first()->shop_id;

            return [
                'shop'  => $items->first()->shop(),
                'items' => $items,
            ];
        });
    }

    /**
     * Notes: 创建一条订单记录
     *
     * @Author: 玄尘
     * @Date  : 2021/5/21 10:24
     * @param  Collection  $split
     * @return Builder|Model
     * @throws \Exception
     */
    protected function createOne(Collection $split)
    {
        /**
         * 创建主订单
         */
        $order = OrderModel::query()
            ->create([
                'shop_id' => $split->shop_id,
                'user_id' => $this->user_id,
                'amount'  => $split->amount,
                'type'    => $this->type,
                'channel' => $this->channel,
                'freight' => $split->freight,
                'remark'  => $this->remark,
                'source'  => $this->source ?? null,
            ]);

        /**
         * 创建订单子条目
         */
        foreach ($split as $item) {
            // 库存校验
            if ($item->qty > $item->model->getGoodsStock()) {
                throw new Exception(sprintf('[%s]库存不足', $item->model->getGoodsName()));
            }

            $order->items()
                ->create([
                    'item_type' => get_class($item->model),
                    'item_id'   => $item->sku_id,
                    'qty'       => $item->qty,
                    'price'     => $item->price,
                    'source'    => $item->getSource(),
                ]);

            if ($item->model->goods->deduct_stock_type == Goods::DEDUCT_STOCK_ORDER) {
                // 扣减库存
                $item->model->deductStock($item->qty, $order->user_id);
            }

        }

        /**
         * 自动更新地址
         */
        if ($this->address instanceof Address) {
            $this->setOrderAddress($order, $this->address);
        }

        /**
         * 订单自动审核
         */
        if (config('order.auto_audit')) {
            $order->audit();
        }

        event(new OrderCreated($order));

        return $order;
    }

    /**
     * Notes: 计算订单总价格
     *
     * @Author: 玄尘
     * @Date  : 2021/5/21 10:26
     * @return float|int|string
     */
    public function total()
    {
        $this->total = 0;
        foreach ($this->items as $item) {
            $this->total = bcadd($this->total, $item->total(), 2);
        }

        return $this->total;
    }

    /**
     * Notes: 设置订单收货地址
     *
     * @Author: 玄尘
     * @Date  : 2021/5/21 10:26
     * @param $order
     * @param $address
     */
    protected function setOrderAddress($order, $address)
    {
        $order->express()
            ->create([
                'name'        => $address->name,
                'mobile'      => $address->mobile,
                'province_id' => $address->province_id,
                'city_id'     => $address->city_id,
                'district_id' => $address->district_id,
                'address'     => $address->address,
            ]);
    }

    /**
     * Notes: 魔术方法，获取一些参数
     *
     * @Author: 玄尘
     * @Date  : 2021/5/21 10:27
     * @param $attr
     * @return float|int|string|null
     */
    public function __get($attr)
    {
        switch ($attr) {
            case 'total':
                return $this->total();
        }

        return null;
    }

    /**
     * Notes: 获取订单详情
     *
     * @Author: <C.Jason>
     * @Date  : 2019/11/22 1:51 下午
     * @param  string  $order_no
     * @return mixed
     */
    public function get(string $order_no)
    {
        return OrderModel::where('user_id', $this->user_id)
            ->with(['user', 'items', 'express', 'seller', 'logs'])
            ->where('order_no', $order_no)
            ->first();
    }

}
