<?php
/**
 *
 * @author: xaboy<365615158@qq.com>
 * @day: 2017/12/20
 */

namespace app\models\store;

use crmeb\basic\BaseModel;
use think\facade\Cache;
use crmeb\traits\ModelTrait;
use app\models\system\SystemStore;
use app\models\user\{
    User, UserAddress, UserBill, WechatUser
};
use crmeb\repositories\{
    GoodsRepository, PaymentRepositories, OrderRepository, ShortLetterRepositories, UserRepository
};
use EasyWeChat\Store\Store;

/**
 *  正常情況， 一次性下單購物車中多個商品，只產生一個訂單。 支付，積分，發貨，退貨，等等都是基於這一個訂單操作。
 * 一個明顯的問題是：當此訂單中多個商品不能在一個快遞中發貨時，訂單就變得不易管理
 * 
 *  這個類的目的就是。已最小的侵入性來實現：一個訂單中包含多個商品時，自動按照一定的邏輯把這個訂單拆分爲若幹個訂單
 * 每個訂單中的商品可在同一個快遞中發貨。
 * 
 *  有點類似一個電商平臺多個商家的情況，肯定是各個商家派發購物車中屬於自己家的商品。
 * 
 *  實現：
 * 
 *  單獨建一個表，此表只記錄一個大訂單號(用於支付接口傳遞訂單參數)和關聯的所有子訂單
 *  支付接口按大訂單號走，發貨等訂單管理按子訂單走。
 *  這樣有一個問題：子訂單退貨沒法執行三方支付退款(暫定)
 */
class StoreOrderBatch extends BaseModel {
    /**
     * 数据表主键
     * @var string
     */
    protected $pk = 'id';

    /**
     * 模型名称
     * @var string
     */
    protected $name = 'store_order_batch';

    use ModelTrait;

    /**
     * 获取订单各项金额
     * 
     */
    public static function getOrderPriceGroup($cartInfo, $addr)
    {
        return StoreOrder::getOrderPriceGroup($cartInfo, $addr);
    }

    /**
     * 缓存订单信息
     * @param $uid
     * @param $cartInfo
     * @param $priceGroup
     * @param array $other
     * @param int $cacheTime
     * @return string 总订单ID
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public static function cacheOrderInfo($uid, $cartInfo, $priceGroup, $other = [], $cacheTime = 600)
    {
        $key = md5(time() . $uid);
        Cache::set('user_order_' . $uid . $key, compact('cartInfo', 'priceGroup', 'other'), $cacheTime);
        return $key;
    }

    /**
     * 获取订单缓存信息
     * @param $uid
     * @param $key
     * @return mixed|null
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public static function getCacheOrderInfo($uid, $key)
    {
        $cacheName = 'user_order_' . $uid . $key;
        if (!Cache::has($cacheName)) return null;
        return Cache::get($cacheName);
    }

    /**
     * 生成总订单唯一id
     * @param $uid 用户uid
     * @return string
     */
    public static function getNewPOrderId()
    {
        $prefix = 'wxcn';
        list($msec, $sec) = explode(' ', microtime());
        $msectime = number_format((floatval($msec) + floatval($sec)) * 1000, 0, '', '');
        $porderId = $prefix . $msectime . mt_rand(10000, 99999);
        if (self::be(['porder_id' => $porderId])) {
            $porderId = $prefix . $msectime . mt_rand(10000, 99999);
        }
        return $porderId;
    }

    /**
     * 获取所有子订单
     * @param string $porderId: porder_id
     */
    public static function getAllSubOrders(string $porderId): Array
    {
        $subOrders = self::alias('b')->join('StoreOrder o', 'o.id=b.oid')
            ->where('b.porder_id', $porderId)->select()->toArray();
        foreach($subOrders as &$subOrder) {
            $subOrder['cart_id'] = json_decode($subOrder['cart_id'], true);
        }
        return $subOrders;
    }

    /**
     * 字段累加
     * @param Array $subOrders
     * @param Array $field_name_list
     * @return Array
     */
    public static function sumFields(Array $marr, Array $field_name_list): Array
    {
        $sums = [];
        foreach($marr as $row) {
            foreach($field_name_list as $fname) {
                $fv = $row[$fname] ?? 0;    // field value
                $sums[$fname] = (float)bcadd($sums[$fname] ?? 0, $fv, 2);
            }
        }
        return $sums;
    }

     /**
     * 微信支付 为 0元时
     * @param $order_id
     * @param $uid
     * @param string $formId
     * @return bool
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public static function jsPayPriceBatch($porder_id, $uid, $formId = '')
    {
        $subOrders = self::getAllSubOrders($porder_id);
        if (!$subOrders) return self::setErrorInfo('订单不存在!');
        $fieldValues = self::sumFields($subOrders, ['paid']);
        if ($fieldValues['paid']) return self::setErrorInfo('该订单已支付!');
        $userInfo = User::getUserInfo($uid);
        self::beginTrans();
        $res1 = $res2 = true;
        foreach($subOrders as $orderInfo) {
            $res1 = $res1 && UserBill::expend('购买商品', $uid, 'now_money', 'pay_product', $orderInfo['pay_price'], $orderInfo['id'], $userInfo['now_money'], '微信支付' . floatval($orderInfo['pay_price']) . '元购买商品');
            $res2 = $res2 && StoreOrder::paySuccess($orderInfo['order_id'], 'weixin', $formId);//微信支付为0时
        }
        $res = $res1 && $res2;
        self::checkTrans($res);
        return $res;
    }

    /**
     * 查找购物车里的所有产品标题
     * @param $cartId 购物车id
     * @return bool|string
     */
    public static function getProductTitleBatch($subOrders)
    {
        $title = '';
        $cartId = [];
        foreach($subOrders as $order) {
            $cartId = array_merge($cartId, $order['cart_id']);
        }
        try {
            $orderCart = StoreOrderCartInfo::where('cart_id', 'in', $cartId)->field('cart_info')->select();
            foreach ($orderCart as $item) {
                if (isset($item['cart_info']['productInfo']['store_name'])) {
                    $title .= $item['cart_info']['productInfo']['store_name'] . '|';
                }
            }
            unset($item);
            if (!$title) {
                $productIds = StoreCart::where('id', 'in', $cartId)->column('product_id');
                $productlist = ($productlist = StoreProduct::getProductField($productIds, 'store_name')) ? $productlist->toArray() : [];
                foreach ($productlist as $item) {
                    if (isset($item['store_name'])) $title .= $item['store_name'] . '|';
                }
            }
            if ($title) $title = substr($title, 0, strlen($title) - 1);
            unset($item);
        } catch (\Exception $e) {
        }
        return $title;
    }

    /**
     * 余额支付
     * @param $order_id
     * @param $uid
     * @param string $formId
     * @return bool
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public static function yuePayBatch($porder_id, $uid, $formId = '')
    {
        $porderInfo = self::where('uid', $uid)->where('porder_id', $porder_id)->find();
        if (!$porderInfo) {
            return self::setErrorInfo('订单不存在!');
        }
        $subOrders = self::getAllSubOrders($porder_id);
        if (!$subOrders) {
            return self::setErrorInfo('订单不存在!');
        }
        $fieldValues = self::sumFields($subOrders, ['paid']);
        if ($fieldValues['paid']) {
            return self::setErrorInfo('该订单已支付!');
        }
        $userInfo = User::getUserInfo($uid);
        if ($userInfo['now_money'] < $porderInfo['total_price'])
            return self::setErrorInfo(['status' => 'pay_deficiency', 'msg' => '余额不足' . floatval($porderInfo['total_price'])]);

        self::beginTrans();
        $res1 = $res2 = $res3 = true;
        foreach($subOrders as $orderInfo) {
            $res1 = $res1 && (false !== User::bcDec($uid, 'now_money', $orderInfo['pay_price'], 'uid'));
            $res2 = $res2 && UserBill::expend('购买商品', $uid, 'now_money', 'pay_product', $orderInfo['pay_price'], $orderInfo['id'], $userInfo['now_money'], '余额支付' . floatval($orderInfo['pay_price']) . '元购买商品');
            $res3 = $res3 && StoreOrder::paySuccess($orderInfo['order_id'], 'yue', $formId);//余额支付成功
            try {
                PaymentRepositories::yuePayProduct($userInfo, $orderInfo);
            } catch (\Exception $e) {
                self::rollbackTrans();
                return self::setErrorInfo($e->getMessage());
            }
        }
       
        $res = $res1 && $res2 && $res3;
        self::checkTrans($res);
        return $res;
    }

    public static function paySuccessBatch($porderId, $paytype = 'weixin', $formId = '')
    {
        $subOrders = self::getAllSubOrders($porderId);
        $res = true;
        foreach($subOrders as $order) {
            $res = $res && StoreOrder::paySuccess($order['order_id'], $paytype, $formId);
        }
        return $res;
    }

    /**
     * 生成订单
     * @param $uid
     * @param $key  orderKey 由客户端传入,总订单ID
     * @param $addressId
     * @param $payType  weixin / yue
     * @param bool $useIntegral: 是否使用积分
     * @param int $couponId
     * @param string $mark: 订单留言
     * @param int $combinationId
     * @param int $pinkId
     * @param int $seckill_id
     * @param int $bargain_id
     * @param bool $test
     * @param int $isChannel 1, 0, 2 : routine, weixin, weixinh5
     * @param int $shipping_type
     * @param string $real_name
     * @param string $phone
     * @param int $storeId: 门店ID
     * @return StoreOrder|bool|\think\Model
     * @throws \think\Exception
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\ModelNotFoundException
     * @throws \think\exception\DbException
     */
    public static function cacheKeyCreateOrderBatch($uid, $key, $addressId, $payType, $useIntegral = false, $couponId = 0, 
        $mark = '', $combinationId = 0, $pinkId = 0, $seckill_id = 0, $bargain_id = 0, $test = false, $isChannel = 0, 
        $shipping_type = 1, $real_name = '', $phone = '', $storeId = 0)
    {
        StoreOrder::beginTrans();
        try {
            $shipping_type = (int)$shipping_type;
            // 支付方式判断
            $offlinePayStatus = (int)sys_config('offline_pay_status') ?? (int)2;    //线下支付状态 2 关闭
            if ($offlinePayStatus == 2) {
                unset(StoreOrder::$payType['offline']);
            }
            if (!array_key_exists($payType, StoreOrder::$payType)) {
                return self::setErrorInfo('选择支付方式有误!', true);
            }
            // 查总订单 sql-1
            if (self::be(['unique' => $key, 'uid' => $uid])) {
                return self::setErrorInfo('请勿重复提交订单', true);
            }

            // 用户信息 sql-2
            $userInfo = User::getUserInfo($uid);
            if (!$userInfo) return self::setErrorInfo('用户不存在!', true);
            // 取订单缓存
            $cartGroup = self::getCacheOrderInfo($uid, $key);
            if (!$cartGroup) return self::setErrorInfo('订单已过期,请刷新当前页面!', true);
            $cartInfo = $cartGroup['cartInfo'];
            $priceGroup = $cartGroup['priceGroup'];
            $other = $cartGroup['other'];
            // $payPrice = (float)$priceGroup['totalPrice'];
            // 收货地址 sql-3
            $addr = UserAddress::where('uid', $uid)->where('id', $addressId)->find();
            // if ($payType == 'offline' && sys_config('offline_postage') == 1) {
            //     $payPostage = 0;
            // } else {
            //     $payPostage = $priceGroup['storePostage'];
            // }
            if ($shipping_type === 1) {
                if (!$test && !$addressId) {
                    return self::setErrorInfo('请选择收货地址!', true);
                }
                // sql-4,5
                if (!$test && (!UserAddress::be(['uid' => $uid, 'id' => $addressId, 'is_del' => 0]) || !($addressInfo = UserAddress::find($addressId))))
                    return self::setErrorInfo('地址选择有误!', true);
            } else {
                if ((!$real_name || !$phone) && !$test) {
                    return self::setErrorInfo('请填写姓名和电话', true);
                }
                $addressInfo['real_name'] = $real_name;
                $addressInfo['phone'] = $phone;
                $addressInfo['province'] = '';
                $addressInfo['city'] = '';
                $addressInfo['district'] = '';
                $addressInfo['detail'] = '';
            }

            $totalPrice = 0.0;
            $totalPayPrice = 0.0;
            $SurplusIntegral = 0;
            $totalPostage = 0.0;
            $totalDeductionPrice = 0.0;
            $totalCouponPrice = 0.0;
            $counter = 0;
            $subOrders = [];
            // 子订单逐个处理
            foreach ($cartInfo as $ci) {
                $counter += 1;

                // 一个订单的购物车
                $subOrderCart = [$ci];
                
                $subOrderId = StoreOrder::getNewOrderId();
                $subPriceGroup = StoreOrder::getOrderPriceGroup($subOrderCart, $addr);
                if (!$subPriceGroup) {
                    return false;
                }
                $payPrice = (float)$subPriceGroup['totalPrice'];
                $totalPrice = (float)bcadd($totalPrice, $payPrice, 2);

                $cartIds = [];      // 子订单购物车ID
                $totalNum = 0;      // 总数量
                $gainIntegral = 0;  // 总可得积分
                foreach ($subOrderCart as $cart){
                    // sql-6 x n
                    if (!$test && !StoreOrder::checkProductStock($uid, $cart['product_id'], $cart['cart_num'], $cart['product_attr_unique'], $cart['combination_id'], $cart['seckill_id'], $cart['bargain_id'])) {
                        return false;
                    }
                    $cartIds[] = $cart['id'];
                    $totalNum += $cart['cart_num'];
                    if (!$seckill_id) $seckill_id = $cart['seckill_id'];
                    if (!$bargain_id) $bargain_id = $cart['bargain_id'];
                    if (!$combinationId) $combinationId = $cart['combination_id'];
                    // 可赚积分
                    $cartInfoGainIntegral = isset($cart['productInfo']['give_integral']) ? bcmul($cart['cart_num'], $cart['productInfo']['give_integral'], 2) : 0;
                    // 汇总
                    $gainIntegral = bcadd($gainIntegral, $cartInfoGainIntegral, 2);
                } // foreach
                
                // 活动
                $deduction = $seckill_id || $bargain_id || $combinationId;
                if ($deduction) {
                    $couponId = 0;
                    $useIntegral = false;
                    if (!$test) {
                        unset(StoreOrder::$payType['offline']);
                        if (!array_key_exists($payType, StoreOrder::$payType)) {
                            return self::setErrorInfo('营销产品不能使用线下支付!', true);
                        } 
                    }
                }// 

                //使用优惠劵
                $res1 = true;
                if ($couponId) {
                    $couponInfo = StoreCouponUser::validAddressWhere()->where('id', $couponId)->where('uid', $uid)->find();
                    if (!$couponInfo) return self::setErrorInfo('选择的优惠劵无效!', true);
                    $coupons = StoreCouponUser::getUsableCouponList($uid, ['valid' => $subOrderCart], $payPrice);
                    $flag = false;
                    foreach ($coupons as $coupon) {
                        if ($coupon['id'] == $couponId) {
                            $flag = true;
                            continue;
                        }
                    }
                    if (!$flag)
                        return self::setErrorInfo('不满足优惠劵的使用条件!', true);
                    $payPrice = (float)bcsub($payPrice, $couponInfo['coupon_price'], 2);
                    $res1 = StoreCouponUser::useCoupon($couponId);
                    $couponPrice = $couponInfo['coupon_price'];
                } else {
                    $couponId = 0;
                    $couponPrice = 0;
                }
                if (!$res1) {
                    return self::setErrorInfo('使用优惠劵失败!', true);
                }
                $totalCouponPrice = (float)bcadd($totalCouponPrice, $couponPrice, 2);

                //$shipping_type = 1 快递发货 $shipping_type = 2 门店自提
                $store_self_mention = sys_config('store_self_mention') ?? 0;
                if (!$store_self_mention) $shipping_type = 1;
                $payPostage = 0;
                if ($shipping_type === 1) {
                    //是否包邮
                    if ((isset($other['offlinePostage']) && $other['offlinePostage'] && $payType == 'offline')) {
                        $payPostage = 0;
                    }
                    $payPrice = (float)bcadd($payPrice, $payPostage, 2);
                } else if ($shipping_type === 2) {
                    //门店自提没有邮费支付
                    $subPriceGroup['storePostage'] = 0;
                    $payPostage = 0;
                    if (!$storeId && !$test) {
                        return self::setErrorInfo('请选择门店', true);
                    }
                }
                $totalPostage = (float)bcadd($totalPostage, $payPostage, 2);

                //积分抵扣
                $res2 = true;
                $usedIntegral = 0.0;
                if ($useIntegral && $userInfo['integral'] > 0) {
                    $deductionPrice = (float)bcmul($userInfo['integral'], $other['integralRatio'], 2);
                    if ($deductionPrice < $payPrice) {
                        $payPrice = bcsub($payPrice, $deductionPrice, 2);
                        $usedIntegral = $userInfo['integral'];
                        $SurplusIntegral = 0;
                        // sql
                        $res2 = false !== User::edit(['integral' => 0], $userInfo['uid'], 'uid');
                    } else {
                        $deductionPrice = $payPrice;
                        $usedIntegral = (float)bcdiv($payPrice, $other['integralRatio'], 2);
                        $SurplusIntegral = bcsub($userInfo['integral'], $usedIntegral, 2);
                        $res2 = false !== User::bcDec($userInfo['uid'], 'integral', $usedIntegral, 'uid');
                        $payPrice = 0;
                    }
                    $res2 = $res2 && false != UserBill::expend('积分抵扣', $uid, 'integral', 'deduction', $usedIntegral, $subOrderId, $userInfo['integral'], '购买商品使用' . floatval($usedIntegral) . '积分抵扣' . floatval($deductionPrice) . '元');
                } else {
                    $deductionPrice = 0;
                    $usedIntegral = 0;
                }
                if (!$res2) {
                    return self::setErrorInfo('使用积分抵扣失败!', true);
                }
                // fix at runtime
                $userInfo['integral'] = (float)bcsub($userInfo['integral'], $usedIntegral, 2);
                if ($userInfo['integral'] < 0) {
                    $userInfo['integral'] = 0;
                }
                
                $totalDeductionPrice = (float)bcadd($totalDeductionPrice, $deductionPrice, 2);
                $totalPayPrice = (float)bcadd($totalPayPrice, $payPrice, 2);

                if ($test) {
                    continue;
                }

                $unique = mb_substr($key, 0, mb_strlen($key)-3) . $counter;
                // 增加子订单
                $orderInfo = [
                    'uid' => $uid,
                    'order_id' => $test ? 0 : $subOrderId,
                    'real_name' => $addressInfo['real_name'],
                    'user_phone' => $addressInfo['phone'],
                    'user_address' => $addressInfo['province'] . ' ' . $addressInfo['city'] . ' ' . $addressInfo['district'] . ' ' . $addressInfo['detail'],
                    'cart_id' => $cartIds,
                    'total_num' => $totalNum,
                    'total_price' => $subPriceGroup['totalPrice'],
                    'total_postage' => $subPriceGroup['storePostage'],
                    'coupon_id' => $couponId,
                    'coupon_price' => $couponPrice,
                    'pay_price' => $payPrice,
                    'pay_postage' => $payPostage,
                    'deduction_price' => $deductionPrice,
                    'paid' => 0,
                    'pay_type' => $payType,
                    'use_integral' => $usedIntegral,
                    'gain_integral' => $gainIntegral,
                    'mark' => htmlspecialchars($mark),
                    'combination_id' => $combinationId,
                    'pink_id' => $pinkId,
                    'seckill_id' => $seckill_id,
                    'bargain_id' => $bargain_id,
                    'cost' => $subPriceGroup['costPrice'],
                    'is_channel' => $isChannel,
                    'add_time' => time(),
                    'unique' => $unique,
                    'shipping_type' => $shipping_type,
                ];
                if ($shipping_type === 2) {
                    $orderInfo['verify_code'] = StoreOrder::getStoreCode();
                    $orderInfo['store_id'] = SystemStore::getStoreDispose($storeId, 'id');
                    if (!$orderInfo['store_id']) return self::setErrorInfo('暂无门店无法选择门店自提！', true);
                }
                $order = StoreOrder::create($orderInfo);
                if (!$order) {
                    return self::setErrorInfo('订单生成失败!', true);
                }
                $res5 = true;
                foreach ($subOrderCart as $cart) {
                    //减库存加销量
                    if ($combinationId) {
                        $res5 = $res5 && StoreCombination::decCombinationStock($cart['cart_num'], $combinationId, isset($cart['productInfo']['attrInfo']) ? $cart['productInfo']['attrInfo']['unique'] : '');
                    } else if ($seckill_id) {
                        $res5 = $res5 && StoreSeckill::decSeckillStock($cart['cart_num'], $seckill_id, isset($cart['productInfo']['attrInfo']) ? $cart['productInfo']['attrInfo']['unique'] : '');
                    } else if ($bargain_id) {
                        $res5 = $res5 && StoreBargain::decBargainStock($cart['cart_num'], $bargain_id, isset($cart['productInfo']['attrInfo']) ? $cart['productInfo']['attrInfo']['unique'] : '');
                    } else {
                        $res5 = $res5 && StoreProduct::decProductStock($cart['cart_num'], $cart['productInfo']['id'], isset($cart['productInfo']['attrInfo']) ? $cart['productInfo']['attrInfo']['unique'] : '');
                    }
                }
                //保存购物车商品信息
                $res4 = false !== StoreOrderCartInfo::setCartInfo($order['id'], $subOrderCart);
                //购物车状态修改
                $res6 = false !== StoreCart::where('id', 'IN', $cartIds)->update(['is_pay' => 1]);
                if (!$res4 || !$res5 || !$res6) {
                    return self::setErrorInfo('订单生成失败!', true);
                }
                StoreOrderStatus::status($order['id'], 'cache_key_create_order', '订单生成');
                $subOrders[] = $order;
            } // foreach 
            
            if ($totalPayPrice <= 0) $totalPayPrice = 0;

            if ($test) {
                StoreOrder::rollbackTrans();
                return [
                    'total_price' => $totalPrice,
                    'pay_price' => $totalPayPrice,
                    'pay_postage' => $totalPostage,
                    'coupon_price' => $totalCouponPrice,
                    'deduction_price' => $totalDeductionPrice,
                    'SurplusIntegral' => $SurplusIntegral,
                ];
            }

            $pOrderId = self::getNewPOrderId();
            foreach($subOrders as $subOrder) {
                $porderInfo = [
                    'porder_id' => $pOrderId,
                    'oid' => $subOrder['id'],
                    'total_price' => $totalPayPrice,
                    'unique' => $key,
                    'uid' => $uid,
                ];
                $porder = self::create($porderInfo);
                if(!$porder) {
                    return self::setErrorInfo('订单生成失败!', true);
                }
            }
            //自动设置默认地址
            if (count($subOrders)) {
                UserRepository::storeProductOrderCreateEbApi($subOrders[0], compact('cartInfo', 'addressId'));
            }
            StoreOrder::clearCacheOrderInfo($uid, $key);
            StoreOrder::commitTrans();
            return $pOrderId;
        } catch (\PDOException $e) {
            StoreOrder::rollbackTrans();
            return self::setErrorInfo('生成订单时SQL执行错误错误原因：' . $e->getMessage());
        } catch (\Exception $e) {
            StoreOrder::rollbackTrans();
            return self::setErrorInfo('生成订单时系统错误错误原因：' . $e->getMessage());
        }
    }
}