<?php


namespace App\Services\refund;

use Api\PhpServices\Idgen\Idgen;
use Api\PhpUtils\Log\FileLog;
use App\Exception\BaseException;
use App\Models\Dictionary;
use App\Models\order\mysql\PayOrder;
use App\Models\order\mysql\PayOrderClearing;
use App\Models\order\mysql\PayOrderClearingItem;
use App\Models\order\mysql\PayOrderItem;
use App\Models\order\mysql\RefundOrder;
use App\Services\order\OrderService;

use App\Services\pingxx\PingxxService;
use Helpers\Strategy;

use App\Exception\custom\RefundException;
use Yaf\Application;

class RefundService
{

    /**
     * @param $order_item_id
     * @param $user_id
     * @throws RefundException
     * 发起退款请求
     */
    public function do_refund($order_id, $order_item_ids = []) {

        if(empty($order_id)) {
            throw new RefundException(['cus' => 1]);
        }

        $data = $this->can_refund($order_id, $order_item_ids);

        /*
        https://www.pingxx.com/api/%E8%AE%A2%E5%8D%95%20Refunds%20%E9%80%80%E6%AC%BE%E6%A6%82%E8%BF%B0.html
        订单退款 paid = true, status = canceled, amount_paid > 0 则表明可以退款
        amount_paid 大于 actual_amount 需要最后一单全退。
        具体见ping++ 文档
        */

        $pay = PingxxService::getInstance($data['pay_order']['pingxx_id'])->getOrder($data['pay_order']['third_order_id']);
        if(empty($pay['paid']) || $pay['paid'] != true) {
            throw new RefundException(['cus' => 12]);
        }

        // 存在多次支付的场景需要人工介入处理， 修复订单支付金额，退款金额
        if($pay['amount_paid'] > $pay['actual_amount']) {
            FileLog::error('pay-service: 订单可能存在多次成功支付，需人工确认处理。', json_encode($pay, JSON_UNESCAPED_UNICODE));
            throw new RefundException(['cus' => 13]);
        }


        // 构造退款数据
        $refund = [
            'id'=>$pay['id'], //pingxx订单ID
            'charge'=>$pay['charge'], //pingxx 支付订单ID
            'refund_mode' => 'to_source', //默认返回
            'charge_amount'=>$data['refund_order']['refund_amount'],
            'metadata' => [
                'app_id'=>'merchant-c',
                'pingxx_id'=>PingxxService::getInstance($data['pay_order']['pingxx_id'])->getAppId(),
                'environ'=>Application::app()->environ(),
                'refund_order_id'=>$data['refund_order']['refund_order_id'],
                'order_item_id'=>$data['order_item_id'],
                'order_id'=>$order_id,
            ],
        ];

        try{
            $ret = PingxxService::getInstance($data['pay_order']['pingxx_id'])->sendRefund($refund);
            if(!empty($ret["data"])) {
                $edit = ['request_pingxx_success_time'=>date('Y-m-d H:i:s')];
                RefundOrder::update($edit, ['refund_order_id'=>$data['refund_order']['refund_order_id']]);
            } else {
                throw new BaseException(['msg'=>$ret['error']['message'], 'code'=>'2011']);
            }

            return [
                'order_item_id'=>$data['order_item_id'],
                'refund_order_id'=>$data['refund_order']['refund_order_id'],
            ];

        }catch (\Exception $e) {
            $edit = ['refund_order_status'=>0];
            RefundOrder::update($edit, ['refund_order_id'=>$data['refund_order']['refund_order_id']]);
            PayOrderItem::update(['refund_order_status'=>0], ['order_item_id'=>$data['order_item_id']]);

            throw $e;
        }
    }

    /**
     * @param $data
     * @return array
     * @throws RefundException
     * 退款回调
     */
    public function call_back($data) {

        if (!in_array($data['type'], ['refund.succeeded', 'order.refunded'])) {
            return ['error'=>'退款回调类型不正确'];
        }

        if (empty($data['data']['object'])) {
            return ['error'=>'退款回调格式不正确'];
        }

        $object = $data['data']['object'];
        if(empty($object['metadata']['app_id']) || $object['metadata']['app_id'] != "merchant-c") {
            //非支付中心发出的订单，需要忽略
            return ['error'=>'非merchant渠道订单，不做处理。'];
        }

        if($data['type'] == 'refund.succeeded') {
            $refund_order_id = $object['order_no'];
            $refunded = $object['succeed']; //"succeed": true,
        } else {
            $refund_order_id = $object['merchant_order_no'];
            $refunded = $object['refunded'];
        }

        if($refunded != true) {
            return ['error'=>'退款状态不正确，不做处理。'];
        }

        if(empty($object['metadata']['refund_order_id'])) {
            return ['error'=>'退款订单缺少matadata，不做处理。'];
        }

        if(empty($object['metadata']['order_item_id'])) {
            return ['error'=>'退款订单缺少order_item_id，不做处理。'];
        }

        // 修改状态，锁定业务
        RefundOrder::beginTransaction();

        $refund_order_id = $object['metadata']['refund_order_id'];
        $edit = [
            'refund_order_status'=>2, //pingxx回调成功,
            'pingxx_callback_success_time'=>date('Y-m-d H:i:s'), //回调成功时间
        ];

        $cnt_r = RefundOrder::update($edit, ['refund_order_id'=>$refund_order_id, 'refund_order_status'=>1]);

        $cnt_i = PayOrderItem::update(['refund_order_status'=>2], [
            'order_item_id'=>$object['metadata']['order_item_id']
        ]);

        if($cnt_r && $cnt_i) {
            RefundOrder::commit();
        } else {
            RefundOrder::rollback();
        }

        $refund = RefundOrder::getMaster('*', ['refund_order_id'=>$refund_order_id]);

        if($refund['refund_order_status'] != 2) {
            throw new RefundException(['cus' => 16]);
        }

        return ['refund_order_id'=>$refund_order_id];

        //不用搞清分核算，是否需要平台记账
    }

    /**
     * @param $where
     * @return \Api\PhpUtils\Mysql\MysqlBase|array
     * @throws RefundException
     * 判断订单是否可退，判断是否存在退款申请
     */
    private function can_refund($order_id, $order_item_ids) {

        /**
         * 业务方自行解决，订单关闭、订单超过时长不允许退款等逻辑
         * 支付中心退款判断仅做主单是否支付，主单是否支持退款的判断
         */
        $refund_payment = 0;
        try{
            RefundOrder::beginTransaction();
            $pay_order = PayOrder::getMaster('*', ['order_id'=>$order_id]);
            if(empty($pay_order)) {
                throw new RefundException(['cus' => 15]);
            }

            $pay_order_items = PayOrderItem::selectMaster('*', ['order_id'=>$order_id]);
            if(empty($pay_order_items)) {
                throw new RefundException(['cus' => 4]);
            }

            foreach ($pay_order_items as $item) {
                if($item['can_notify_account'] == 1) {
                    continue; //已结算不允许退款
                }

                if(in_array($item['refund_order_status'], [1, 2])) {
                    continue; //退款中、已退款不允许再次申请
                }

                if(!empty($order_item_ids) && !in_array($item['order_item_id'], $order_item_ids)) {
                    continue; //部分退款，且不再退款子单列表，不处理
                }

                $items[] = $item;

                $refund_payment += $item['payment'];
            }

            if(empty($items)) {
                throw new RefundException(['cus' => 14]); //
            }

            //重新计算微信手续费
            $cleared_amount = $cleared_tip = 0;

            // 计算已退金额、已退手续费 sum(refund_wx_tip), sum(refund_amount)
            $list = RefundOrder::selectMaster('*', ['order_id'=>$pay_order['order_id']]);
            foreach ($list as $r) {
                if($r['refund_order_status'] == Dictionary::REFUND_ORDER_STATUS_UNDO) {
                    continue;
                }

                $cleared_amount += $r['refund_amount'];
                $cleared_tip += $r['refund_wx_tip'];
            }

            // 总共手续费
            $wx_clear = PayOrderClearing::getMaster('*', [
                    'order_id'=>$pay_order['order_id'],
                    'pay_order_id'=>$pay_order['pay_order_id'],
                    'pay_sub_type'=>406 //三方渠道手续费
                ]
            );
            $wx_total_tip = 0;
            if($wx_clear) {
                $wx_total_tip = $wx_clear['pay_amount'];
            }

            $wx_tip = Strategy::getWxTip($refund_payment, $pay_order['pay_amount'], $cleared_amount, $wx_total_tip, $cleared_tip);

            $refund = [
                'refund_order_id'=>$this->gen_refund_order_id($pay_order['user_id']),
                'user_id'=>$pay_order['user_id'],
                'order_id'=>$pay_order['order_id'],
                'refund_order_status'=>1,
                'refund_amount'=>$refund_payment,
                'refund_wx_tip'=>$wx_tip,
                'source_name'=>$pay_order['source_name'],
                'extra'=> json_encode(['order_item_id'=>array_column($items, 'order_item_id')]),
            ];

            $cnt = RefundOrder::insert($refund, ['rowCount'=>true]);
            if($cnt == 0) {
                throw new RefundException(['cus' => 0]);
            }

            //批量更新子单状态  "user_id" => [2, 123, 234, 54],
            PayOrderItem::update(['refund_order_status'=>1], ['order_item_id'=>array_column($items, 'order_item_id')]);

            $data = [
                'refund_order'=>$refund,
                'pay_order'=>$pay_order,
                'order_item_id'=>array_column($items, 'order_item_id'),
            ];

            RefundOrder::commit();

        }catch (\PDOException $e) {
            RefundOrder::rollback();
            throw new BaseException(['msg'=>$e->getMessage(), 'code'=>$e->getCode()]);
        }

        return $data;
    }

    private function gen_refund_order_id($user_id)
    {
        $number = substr($user_id, -2);
        $ids = $this->get_idgen_id($number, 1);
        return array_pop($ids);
    }

    private function get_idgen_id($number, $count = 1)
    {
        $number = intval($number);
        $res = Idgen::get(appConfig('idgen.partner'), appConfig('idgen.key'), [], [[
            "type" => "55c768",
            'number' => $number,
            "count" => $count]]);
        $id = $res['id_datetime']['55c768'] ?? [];
        return $id;
    }

}