<?php

namespace app\api\service\wholesaler;

use app\api\model\PaymentTrade as PaymentTradeModel;
use app\api\model\User as UserModel;
use app\api\model\user\BalanceLog as BalanceLogModel;
use app\api\model\wholesaler\Apply;
use app\api\model\wholesaler\Order;
use app\common\enum\order\PayStatus;
use app\common\enum\payment\Method as PaymentMethodEnum;
use app\common\enum\recharge\order\PayStatus as PayStatusEnum;
use app\common\enum\user\balanceLog\Scene as SceneEnum;
use app\common\library\Lock;
use app\common\library\Log;
use app\common\service\BaseService;
use cores\exception\BaseException;

class PaySuccess extends BaseService
{
    // 当前订单信息
    public Order $orderInfo;

    // 当前用户信息
    private UserModel $userInfo;

    // 当前订单号
    private string $orderNo;

    // 当前订单ID
    private int $orderId;

    // 订单支付方式
    private string $method;

    // 第三方交易记录ID
    private ?int $tradeId = null;

    // 第三方支付成功返回的数据
    private array $paymentData = [];

    /**
     * 设置支付的订单ID
     * @param int $orderId 订单ID
     */
    public function setOrderId(int $orderId): PaySuccess
    {
        $this->orderId = $orderId;
        return $this;
    }

    /**
     * 设置当前的订单号
     * @param string $orderNo
     * @return $this
     */
    public function setOrderNo(string $orderNo): PaySuccess
    {
        $this->orderNo = $orderNo;
        return $this;
    }

    /**
     * 设置订单支付方式
     * @param string $method
     * @return $this
     */
    public function setMethod(string $method): PaySuccess
    {
        $this->method = $method;
        return $this;
    }

    /**
     * 第三方支付交易记录ID
     * @param int|null $tradeId
     * @return $this
     */
    public function setTradeId(?int $tradeId = null): PaySuccess
    {
        $this->tradeId = $tradeId;
        return $this;
    }

    /**
     * 第三方支付成功返回的数据
     * @param array $paymentData
     * @return $this
     */
    public function setPaymentData(array $paymentData): PaySuccess
    {
        $this->paymentData = $paymentData;
        return $this;
    }

    /**
     * 订单支付成功业务处理
     * @return bool
     * @throws BaseException
     */
    public function handle(): bool
    {
        // 验证当前参数是否合法
        $this->verifyParameters();
        // 当前订单开启并发锁
        $this->lockUp();
        // 验证当前订单是否允许支付
        if ($this->checkOrderStatusOnPay()) {
            // 更新订单状态为已付款
            $this->updatePayStatus();
        }
        // 当前订单解除并发锁
        $this->unLock();
        return true;
    }

    /**
     * 订单模型
     * @return Order|null
     * @throws BaseException
     */
    private function orderModel(): ?Order
    {
        return $this->getOrderInfo();
    }

    /**
     * 订单已付款事件
     * @return void
     * @throws BaseException
     */
    private function updatePayStatus(): void
    {
        // 记录日志
        Log::append('PaySuccess --updatePayStatus', ['title' => '订单已付款事件']);
        // 当前订单信息
        $orderInfo = $this->getOrderInfo();
        // 事务处理
        $this->orderModel()->transaction(function () use ($orderInfo) {
            // 更新订单状态
            $this->updateOrderStatus();
            // 累积用户总消费金额
            UserModel::setIncPayMoney($orderInfo['user_id'], (float)$orderInfo['pay_price']);
            // 记录订单支付信息
            $this->updatePayInfo();
            //添加申请记录
            $this->apply();
        });
    }

    /**
     * 记录订单支付的信息
     * @throws BaseException
     */
    private function updatePayInfo()
    {
        // 当前订单信息
        $orderInfo = $this->getOrderInfo();
        // 余额支付
        if ($this->method == PaymentMethodEnum::BALANCE) {
            // 更新用户余额
            UserModel::setDecBalance((int)$orderInfo['user_id'], (float)$orderInfo['pay_price']);
            // 新增余额变动记录
            BalanceLogModel::add(SceneEnum::WHOLESALER, [
                'user_id' => (int)$orderInfo['user_id'],
                'money' => -$orderInfo['pay_price'],
            ], ['order_no' => $orderInfo['order_no']]);
        }
        // 将第三方交易记录更新为已支付状态
        if (in_array($this->method, [PaymentMethodEnum::WECHAT, PaymentMethodEnum::ALIPAY])) {
            $this->updateTradeRecord();
        }
    }

    /**
     * 将第三方交易记录更新为已支付状态
     */
    private function updateTradeRecord()
    {
        if ($this->tradeId && !empty($this->paymentData)) {
            PaymentTradeModel::updateToPaySuccess($this->tradeId, $this->paymentData['tradeNo']);
        }
    }

    /**
     * 更新订单状态
     * @throws BaseException
     */
    private function updateOrderStatus(): void
    {
        // 更新订单状态
        $this->orderModel()->save([
            'pay_method' => $this->method,
            'pay_status' => PayStatus::SUCCESS,
            'pay_time' => time(),
            'trade_id' => $this->tradeId ?: 0,
        ]);
    }

    /**
     * 获取买家用户信息
     * @return UserModel|array|null
     * @throws BaseException
     */
    private function getUserInfo()
    {
        if (empty($this->userInfo)) {
            $this->userInfo = UserModel::detail($this->getOrderInfo()['user_id']);
        }
        if (empty($this->userInfo)) {
            throwError('未找到买家用户信息');
        }
        return $this->userInfo;
    }

    /**
     * 验证当前参数是否合法
     * @throws BaseException
     */
    private function verifyParameters()
    {
        if (empty($this->orderNo)) {
            throwError('orderNo not found');
        }
        if (empty($this->method)) {
            throwError('method not found');
        }
        if ($this->tradeId) {
            empty($this->paymentData) && throwError('PaymentData not found');
            !isset($this->paymentData['tradeNo']) && throwError('PaymentData not found');
        }
        // 记录日志
        Log::append('PaySuccess', [
            'orderNo' => $this->orderNo, 'method' => $this->method,
            'tradeId' => $this->tradeId, 'paymentData' => $this->paymentData
        ]);
    }

    /**
     * 验证当前订单是否允许支付
     * @return bool
     * @throws BaseException
     */
    private function checkOrderStatusOnPay(): bool
    {
        // 当前订单信息
        $orderInfo = $this->getOrderInfo();
        // 验证余额支付时用户余额是否满足
        if ($this->method == PaymentMethodEnum::BALANCE) {
            if ($this->getUserInfo()['balance'] < $orderInfo['pay_price']) {
                throwError('账户余额不足,无法使用余额支付');
            }
        }
        // 检查订单状态是否为已支付
        if ($orderInfo['pay_status'] == PayStatusEnum::SUCCESS) {
            $this->onOrderPaid();
            return false;
        }
        return true;
    }

    /**
     * 处理订单已支付的情况
     * @throws BaseException
     */
    private function onOrderPaid()
    {
        // 记录日志
        Log::append('PaySuccess --onOrderPaid', ['title' => '处理订单已支付的情况']);
        // 当前订单信息
        $orderInfo = $this->getOrderInfo();
        // 余额支付直接返回错误信息
        if ($this->method == PaymentMethodEnum::BALANCE) {
            throwError('当前订单已支付,无需重复支付');
        }
        // 第三方支付判断是否为重复下单 (因异步回调可能存在网络延迟的原因,在并发的情况下会出现同时付款两次,这里需要容错)
        // 如果订单记录中已存在tradeId并且和当前支付的tradeId不一致, 那么判断为重复的订单, 需进行退款处理
        if ($this->tradeId > 0 && $orderInfo['trade_id'] != $this->tradeId) {
            // 执行原路退款
            throwError('当前订单异常');
        }
    }

    /**
     * 获取当前订单的详情信息
     * @return Order|null
     * @throws BaseException
     */
    private function getOrderInfo(): ?Order
    {
        // 获取订单详情 (待支付状态)
        if (empty($this->orderInfo)) {
            $this->orderInfo = Order::getPayDetail($this->orderNo);
        }
        // 判断订单是否存在
        if (empty($this->orderInfo)) {
            throwError('未找到该订单信息');
        }
        return $this->orderInfo;
    }

    /**
     * 订单锁:防止并发导致重复支付
     * @throws BaseException
     */
    private function lockUp()
    {
        $orderInfo = $this->getOrderInfo();
        Lock::lockUp("OrderPaySuccess_{$orderInfo['order_id']}");
    }

    /**
     * 订单锁:防止并发导致重复支付
     * @throws BaseException
     */
    private function unLock()
    {
        $orderInfo = $this->getOrderInfo();
        Lock::unLock("OrderPaySuccess_{$orderInfo['order_id']}");
    }

    /**
     * @notes:修改申请信息
     * @throws BaseException
     * @author: wanghousheng
     */
    private function apply(): void
    {
        $orderInfo = $this->getOrderInfo();
        $add['order_id'] = $orderInfo['order_id'];
        $add['year'] = $orderInfo['year'];
        $add['store_id'] = $orderInfo['store_id'];
        $add['username'] = $orderInfo['username'];
        $add['mobile'] = $orderInfo['mobile'];
        $add['card_front_img_id'] = $orderInfo['card_front_img_id'];
        $add['card_back_img_id'] = $orderInfo['card_back_img_id'];
        $add['license_img_id'] = $orderInfo['license_img_id'];
        $add['card_no'] = $orderInfo['card_no'];
        $add['province_id'] = $orderInfo['province_id'];
        $add['door_img_id'] = $orderInfo['door_img_id'];
        $add['city_id'] = $orderInfo['city_id'];
        $add['company_name'] = $orderInfo['company_name'];
        $add['credit_code'] = $orderInfo['credit_code'];
        $add['business'] = $orderInfo['business'];
        $add['user_id'] = $orderInfo['user_id'];
        $add['avatar_id'] = $orderInfo['avatar_id'];
        $info = Apply::detail(['user_id' => $orderInfo['user_id']]);
        if (empty($info->id)) {
            (new Apply())->add($add);
        } else {
            $add['status'] = 10;
            (new Apply())->where(['user_id' => $orderInfo['user_id']])->update($add);
        }
    }
}