<?php

namespace app\traits\order;

use app\exception\RepositoryException;
use app\model\mall\SpuLimitTime;
use app\model\OrderSku;
use app\model\ShoppingCart;
use app\model\Sku;
use app\model\Spu;
use app\repository\OrderRepository;
use app\repository\SpuRepository;
use Exception;
use think\Collection;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;
use think\facade\Db;

/**
 * 购物车相关
 *
 * Trait ShoppingCartTrait
 * @package app\traits\order
 */
trait ShoppingCartTrait
{
    /**
     * 获取购物车列表
     *
     * @param  int  $accountId
     * @param  string  $type
     * @param  int  $page
     * @param  int  $size
     * @return array
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public function shoppingCart(int $accountId, string $type = '', int $page = 1, int $size = 20): array
    {
        $isScore = $type == 'score' ? 1 : 0;
        $res     = [
            'total'   => 0,
            'current' => $page,
            'size'    => $size,
            'list'    => new Collection(),
        ];

        $query        = ShoppingCart::where('account_id', $accountId)
            ->where('is_score', $isScore)
            ->with([
                'spu'              => function ($query) {
                    $query->field('id,name as spu_name,cover as spu_cover,original_price,unit');
                },
                'sku'           => function ($query) {
                    $query->field('id,title as sku_name,main_image as sku_cover,price as sku_price,coding,spec_text,type as activity_type');
                }
            ]);
        $res['total'] = $query->count();
        if ($res['total'] > 0) {
            $list = $query->order('updated_at', 'desc')
                ->page($page)
                ->limit($size)
                ->select();
            $res['list'] = $list->each(function ($item) {
                if (!isset($item->sku) || !isset($item->sku->activity_type)) {
                    unset($item);
                } else {
                    if (!isset($item->sku->spec_text)) {
                        $item->sku->spec_text = [];
                    } else {
                        $specArr = json_decode($item->sku->spec_text, true);
                        $item->sku->spec_text = $specArr;
                        $item->sku->spec_info = SpuRepository::getInstance()->convertSpecInfo($specArr);
                    }
                }


                unset($item->spu_activity);
                unset($item->activity_skus);
            });
        }

        return $res;
    }

    /**
     * 购物车保存
     *
     * @param  int  $accountId
     * @param  int  $skuId
     * @param  int  $num  最终购物车数量 注意 不是新增的数量 而是最终保存的数量。若=0 则删除
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws RepositoryException
     */
    public function shoppingCartAdd(int $accountId, int $skuId, int $num = 1): bool
    {
        if (!$skuInfo = SpuRepository::getInstance()->infoBySkuId($skuId)) {
            throw new RepositoryException('商品不存在或已下架');
        }

        if ($item = ShoppingCart::where('account_id', $accountId)->where('sku_id', $skuId)->find()) {
            $update['updated_at'] = date('Y-m-d H:i:s');
            $update['spu_name']   = $skuInfo['spu_name'] ?? ($skuInfo['spu_activity_name'] ?? '');
            $update['sku_price']  = $skuInfo['sku_price'];
            $update['score']      = $skuInfo['sku_score'];
            $update['sku_name']   = $skuInfo['sku_name'];
            $update['sku_cover']  = $skuInfo['sku_cover'];

            $item->inc('num', $num)->update($update);
            return true;
        }

        ShoppingCart::create([
            'account_id'      => $accountId,
            'spu_activity_id' => $skuInfo['spu_activity_id'] ?? 0,
            'spu_id'          => $skuInfo['spu_id'],
            'sku_id'          => $skuInfo['id'],
            'original_price'  => $skuInfo['original_price'],
            'price'           => $skuInfo['price'],
            'score'           => $skuInfo['sku_score'],
            'num'             => $num,
            'created_at'      => date('Y-m-d H:i:s'),
            'spu_name'        => $skuInfo['spu_name'] ?? ($skuInfo['spu_activity_name'] ?? ''),
            'sku_price'       => $skuInfo['price'],
            'sku_name'        => $skuInfo['sku_name'],
            'sku_cover'       => $skuInfo['main_image'],
            'is_score'        => $skuInfo['is_score'] ?? 0,
        ]);
        return true;
    }

    /**
     * 购物车数量变更
     *
     * @param  int  $id
     * @param  int  $num
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws RepositoryException
     */
    public function shoppingCartChangeNum(int $id, int $num = 1): bool
    {
        if (!$item = ShoppingCart::where('id', $id)->find()) {
            throw new RepositoryException('记录不存在');
        }
        return $item->save(['num' => $num]);
    }

    /**
     * 购物车删除
     *
     * @param  int  $accountId
     * @param  int  $id
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     * @throws RepositoryException
     */
    public function shoppingCartDel(int $accountId, int $id): bool
    {
        if (!$item = ShoppingCart::where('id', $id)->find()) {
            return true;
        }

        if ($item['account_id'] != $accountId) {
            throw new RepositoryException('这不是您的购物车商品');
        }

        return $item->delete();
    }

    /**
     * 删除指定订单相关的购物车数据
     *
     * @param  int  $accountId
     * @param  string  $orderId
     * @return bool
     */
    public function delShoppingCartByOrder(int $accountId, string $orderId): bool
    {
        Db::startTrans();
        try {
            $orderSkus = OrderSku::where('coding', $orderId)->field('id,coding,sku_id,num')->select();
            $sku2Num   = $orderSkus->column('num', 'sku_id');
            if (empty($orderSkus)) {
                return true;
            }

            $list = ShoppingCart::where('account_id', $accountId)
                ->field('id,sku_id,num')
                ->whereIn('sku_id', $orderSkus->column('sku_id'))
                ->lock(true)->select();

            $update = [];
            foreach ($list as $v) {
                $arr        = [];
                $arr['num'] = $v['num'] - ($sku2Num[$v['sku_id']] ?? 1);
                $arr['num'] = $arr['num'] < 0 ? 0 : $arr['num'];
                $arr['id']  = $v['id'];

                $update[] = $arr;
            }

            (new ShoppingCart())->saveAll($update);

            ShoppingCart::where('account_id', $accountId)->where('num', '<=', 0)->delete();

            Db::commit();
            return true;
        } catch (Exception $e) {
            Db::rollback();
            OrderRepository::log('清理购物车数据失败', $e);
            return false;
        }

    }

    /**
     * 检查并清理购物车不存在的商品和sku
     * @param  int  $accountId
     * @return bool
     */
    public function checkShoppingCart(int $accountId = 0): bool
    {
        $where = [];
        if ($accountId > 0) {
            $where[] = ['account_id', '=', $accountId];
        }
        $items = ShoppingCart::where($where)->column('spu_id,sku_id');
        $spuIds = array_unique(array_column($items, 'spu_id'));
        $skuIds = array_unique(array_column($items, 'sku_id'));

        $notExistSpu = [];
        if ($spuIds) {
            $existSpu = Spu::whereIn('id', $spuIds)->whereNull('deleted_at')->column('id');
            $notExistSpu = array_diff($spuIds, $existSpu);
        }

        $notExistSku = [];
        if ($skuIds) {
            $existSku = Sku::whereIn('id', $skuIds)->whereNull('deleted_at')->column('id');
            $notExistSku = array_diff($skuIds, $existSku);
        }

        if (!empty($notExistSku) || !empty($notExistSpu)) {
            ShoppingCart::where($where)
                ->where(function ($q) use ($notExistSku, $notExistSpu) {
                    if (!empty($notExistSpu)) {
                        $q->whereOr('spu_id', 'in', $notExistSpu);
                    }
                    if (!empty($notExistSku)) {
                        $q->whereOr('sku_id', 'in', $notExistSku);
                    }
                })
                ->delete();
        }
        return true;
        //        return ['noteSpu' => $notExistSpu, 'notSku' => $notExistSku, 'needDel' => $needDel, 'count' => count($needDel)];
    }
}