<?php

namespace App\Services\Pay;


use App\Jobs\ReportDy;
use App\Models\Product\Product;
use App\Models\User\User;
use App\Models\User\UserEarningsLogs;
use App\Models\User\UserPeroidOrderLog;
use App\Services\Order\OrderService;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Redis;
use Vinkla\Hashids\Facades\Hashids;
use Ycpay\Factory as PayFactory;
use Log;
use App\Services\User\UserService;
use App\Models\Channel\Channel;
use App\Models\Order\Order;
use App\Models\Pay\PayMerchant;
use App\Services\OpenApi\OpenService;

class PayService
{
    private $openService;

    public function __construct(
        OpenService $openService
        )
    {
        $this->openService = $openService;
    }

    public function create_order($data)
    {

        #
        $channel = Channel::find($data['distribution_channel_id']);
        \Log::info('$channel:'.$data['distribution_channel_id'].' param:'.json_encode($channel));

        $pay_merchant = PayMerchant::find($channel->pay_merchant_id);
        if(empty($pay_merchant)){
            \Log::info('create_order_pay_merchant_null:'.$channel->pay_merchant_id);

            return $this->error('50013:参数异常');
        }
        $pay_config_info = $pay_merchant->config_info;
        $pay_config_infos = json_decode($pay_config_info,true);

        \Log::info('create_order_pay_merchant:'.json_encode($pay_merchant));
        \Log::info('$pay_config_infos:');\Log::info($pay_config_infos);

        $insert_data = [];
        $insert_data['uid'] = isset($data['uid'])?$data['uid']:-1;
        $insert_data['price'] = isset($data['price'])?$data['price']:-1;
        $insert_data['status'] = 'UNPAID';
        $insert_data['pay_num'] = OrderService::getChargeNum($data['uid']);
        $insert_data['product_id'] = isset($data['product_id'])?$data['product_id']:-1;
        $insert_data['distribution_channel_id'] = isset($data['distribution_channel_id'])?$data['distribution_channel_id']:-1;
        $insert_data['trade_no'] = isset($data['trade_no'])?$data['trade_no']:'';
        $insert_data['pay_merchant_source'] = $pay_merchant->source;
        $insert_data['pay_merchant_id'] = isset($pay_merchant->id)?$pay_merchant->id:0;
        $insert_data['transaction_id'] = isset($data['transaction_id'])?$data['transaction_id']:'';
        $insert_data['from_bid'] = isset($data['from_bid'])?$data['from_bid']:'';
        $insert_data['pay_end_at'] = null;
        $insert_data['order_type'] = isset($data['order_type'])?$data['order_type']:'';
        $insert_data['create_ip'] = isset($data['create_ip'])?$data['create_ip']:'';
        $insert_data['send_order_id'] = isset($data['send_order_id'])?$data['send_order_id']:-1;

        // 生成订单
        OrderService::create_order($insert_data);

        $pay_type = $data['pay_type'];
        if(in_array($pay_type, ['Ali','Weixin','Byte'])){
            $config = [];
            if($pay_type == 'Byte'){
                $config = [
                    'app_id'=>isset($pay_config_infos['appid'])?$pay_config_infos['appid']:'',
                    'secret'=>isset($pay_config_infos['secret'])?$pay_config_infos['secret']:'',
                    'notify_url'=>isset($pay_config_infos['notify_url'])?$pay_config_infos['notify_url']:'',
                    'token'=>isset($pay_config_infos['token'])?$pay_config_infos['token']:'',
                    'salt'=>isset($pay_config_infos['salt'])?$pay_config_infos['salt']:'',
                    'store_uid'=>isset($pay_config_infos['store_uid'])?$pay_config_infos['store_uid']:'',// 收款商户号，1个主体下面有多个商户号
                    'merchant_id'=>$channel->pay_merchant_id,
                    'out_order_no'=>$data['trade_no'],
                    'money'=>intval($data['price']*100),// 单位是分
                    'title'=>isset($pay_config_infos['title'])?$pay_config_infos['title']:'',
                    'desc'=>isset($pay_config_infos['desc'])?$pay_config_infos['desc']:'',
                ];
                \Log::info('$config:'.json_encode($config));

                $pay_client = PayFactory::getInstance($pay_type);
                $order_create_res = $pay_client->init($config)->applyOrderCreate($config['out_order_no'],$config['money'],$config['store_uid'],$config['title'],$config['desc'],$data['phone_type']);
                \Log::info('applyOrderCreateRes:'.json_encode($order_create_res));

                $this->report_to_douyin($data['trade_no'],$data['openid'],0);

                return $order_create_res;
            }

        }

    }

    function orderCallBack($trade_no, $transaction_id,$order_id)
    {

        $order_info = OrderService::getByTradeNo($trade_no);
        if(empty($order_info)){
            \Log::info('orderCallBack_orderinfo_isnull:'.$trade_no);
            return false;
        }
        // 防止重复回调
        if($order_info->status == 'PAID'){
            \Log::info('orderCallBack_orderinfo_hascallback_return:'.$trade_no);
            return false;
        }
        if(empty($transaction_id)){
            \Log::info('orderCallBack_transaction_id_isnull:'.$transaction_id.' trade_no:'.$trade_no);
            return false;
        }

        # 签名校验
        if(! $this->sign_check($order_info->pay_merchant_id)){
            \Log::info('orderCallBack_sign_check_false:'.$order_info->pay_merchant_id.' trade_no:'.$trade_no);
            return false;
        }

        //跨日期订单处理
        $is_change = $this->orderAcrossDay($order_info);
        DB::beginTransaction();

        $uid = $order_info->uid;
        $user = User::find($uid);
        $openid = $user->openid;
        $distribution_channel_id = $order_info->distribution_channel_id;
        $product_id = $order_info->product_id;
        $send_order_id = $order_info->send_order_id;
        $fee = $order_info->price;

        try {

            //获取用户充值次数
            $order_info->pay_num = $this->getChargeTimes($uid);
            $time_type_days = [
                'YEAR'=>365,
                'HALF_YEAR'=>180,
                'QUATER'=>92,
                'MONTH'=>30,
                'WEEK'=>7,
            ];

            if (in_array($order_info->order_type ,['YEAR','HALF_YEAR','QUATER','MONTH','WEEK'])) {
                $period_order_param = [
                    'uid'=>$uid,
                    'distribution_channel_id'=>$distribution_channel_id,
                    'fee'=>$fee,
                    'send_order_id' => $send_order_id,
                    'add_days' => $time_type_days[$order_info->order_type],
                    'trade_no' => $trade_no,
                    'order_type' => $order_info->order_type,
                ];
                $this->vipPeriodOrder($period_order_param);
            }
            elseif ($order_info->order_type == 'RECHARGE') {
                \Log::info('RECHARGE_product_type:'.$order_info->order_type.' trade_no:'.$trade_no);
                $product = Product::detail($product_id);
                $this->userCharge($product, $uid, $fee, $trade_no);
            }
            else {
                \Log::info('invalid_product_type:'.$order_info->order_type.' trade_no:'.$trade_no);
                DB::rollback();
                return false;
            }

            $order_info->status = 'PAID';
            $order_info->pay_end_at = date('Y-m-d H:i:s');
            $order_info->transaction_id = $transaction_id;
//             $order_info->tiktok_order_id = $order_id;

            if ($is_change) {
                $order_info->updated_at = $order_info->pay_end_at = date('Y-m-d H:i:s', time() + 5);
            }
            $order_info->save();

            DB::commit();
        } catch (\Exception $e) {
            \Log::error($e);
            DB::rollback();
            return false;
        }

        // 已支付状态回传给抖音
        $this->report_to_douyin($order_info->trade_no,$openid,1);

        return true;
    }

    function report_to_douyin($trade_no,$openid,$order_status){

        /**
         0：待支付
         1：已支付
         2：已取消（用户主动取消或者超时未支付导致的关单）
         4：已核销（核销状态是整单核销,即一笔订单买了 3 个券，核销是指 3 个券核销的整单）
         5：退款中
         6：已退款
         8：退款失败
         **/
        $order_detail = [
            'order_id'=>$trade_no,
        ];

        \Log::info('report_to_douyin:trade_no:'.$trade_no.' openid:'.$openid.' order_status:'.$order_status);

        $push_res = $this->openService->getInstance()->pushOrder($openid,$order_status, $order_detail);
        \Log::info('report_to_douyin_res:'.json_encode($push_res,JSON_UNESCAPED_UNICODE));
    }

    function sign_check($pay_merchant_id){

        $pay_merchant = PayMerchant::find($pay_merchant_id);
        if(empty($pay_merchant)){
            \Log::info('orderCallBack_pay_merchant_null:'.$pay_merchant_id);
            return false;
        }
        $pay_config_info = $pay_merchant->config_info;
        $pay_config_infos = json_decode($pay_config_info,true);

        \Log::info('orderCallBack_pay_merchant:'.json_encode($pay_merchant));
        \Log::info('$pay_config_infos:');\Log::info($pay_config_infos);
        $config = [
            'app_id'=>isset($pay_config_infos['appid'])?$pay_config_infos['appid']:'',
            'secret'=>isset($pay_config_infos['secret'])?$pay_config_infos['secret']:'',
            'token'=>isset($pay_config_infos['token'])?$pay_config_infos['token']:'',
            'salt'=>isset($pay_config_infos['salt'])?$pay_config_infos['salt']:'',
        ];
        \Log::info('$config:'.json_encode($config));
        $pay_client = PayFactory::getInstance($pay_config_infos['pay_type']);
        $sign_check = $pay_client->init($config)->notifyCheck();
        if(!$sign_check){
            \Log::info('orderCallBack_sign_check_false:'.$pay_merchant_id);

            return false;
        }
        \Log::info('orderCallBack_sign_check_true:'.$pay_merchant_id);

        return true;
    }

    function vipPeriodOrder($data)
    {
        \Log::info('start_vipPeriodOrder：uid:'.$data['uid'].' trade_no:'.$data['trade_no']);

        $user = User::find($data['uid']);
        if(empty($user)){
            \Log::info('vipPeriodOrder_user_isnull:'.$data['uid']);
            return false;
        }
        $data['origin_end_time'] = $user->vip_limit_date;
        // 没设置就默认当前时间
        if($user->vip_limit_date == '' || $user->vip_limit_date == '0000 00:00'){
            $data['origin_end_time'] = date('Y-m-d H:i:s');
        }

        $insert_data = [];
        $insert_data['uid'] = $data['uid'];
        $insert_data['distribution_channel_id'] = $data['distribution_channel_id'];
        $insert_data['fee'] = $data['fee'];
        $insert_data['send_order_id'] = $data['send_order_id'];
        $insert_data['add_days'] = $data['add_days'];
        $insert_data['trade_no'] = $data['trade_no'];
        $insert_data['begin_time'] = $data['origin_end_time'];// 原先结束的时间作为开始
        $insert_data['origin_end_time'] = $data['origin_end_time'];
        $insert_data['end_time'] = date('Y-m-d H:i:s', strtotime($data['origin_end_time'])+$data['add_days']*86400);

        $insert_data['vip_type'] = $data['order_type'];

        // 如果已经加过时间了，就不再加、
        $old = UserPeroidOrderLog::getByUidAndTradeNo($data['uid'],$data['trade_no']);
        \Log::info('vipPeriodOrder_old:'.json_encode($old));
        if (!empty($old)) {
            \Log::info('vipPeriodOrder_old_is_not_null:');
            return $old;
        } else {
            \Log::info('vipPeriodOrder_old_isnull:');

            # 给用户延长结束时间
            $user->vip_limit_date = $insert_data['end_time'];
            $user->save();

            // 上报支付
            $this->reportPay($data['uid'], $data['trade_no']);

            // 给邀请者发放分成收益
            $this->giveRewardToFromUser($user->id, $user->from_uid, $data['fee']);

            # 插入记录
            return UserPeroidOrderLog::firstOrCreate($insert_data);
        }

    }

    private function userCharge($product, $uid, $fee, $trade_no)
    {
        // 获取充值配置信息
        $price = getProp($product, 'price');
        $coin  = (int)$price * 100; // 充值币
        $given = (int)getProp($product, 'given'); // 奖励币

        // 更新用户书币余额
        $user = User::find($uid);
        if(empty($user)){
            \Log::info('userCharge_user_isnull:'.$uid);
            return false;
        }

        $user->balance += $coin + $given;
        $user->charge_balance += $coin;
        $user->reward_balance += $given;
        $user->save();

        // 上报支付
        $this->reportPay($uid, $trade_no);

        // 给邀请者发放分成收益
        $this->giveRewardToFromUser($user->id, $user->from_uid, $fee);

        return true;
    }

    public function reportPay($uid, $trade_no): bool
    {
        // 上报前判断是否符合条件(当日注册当日首充)
        if ((int)DB::table('orders')->where('uid', $uid)->where('status', 'PAID')->count('id') !== 0) {
            // 用户当天已有充值成功订单则跳过
            return false;
        }
        if (DB::table('users')->where('id', $uid)->value('created_at') < date('Y-m-d 00:00:00')) {
            // 用户不是当天注册则跳过
            return false;
        }

        # 付费回传逻辑(丢入队列)
        $clickid = Redis::lpop($uid.'-clickids');
        if ($clickid) {
            $report_log = DB::table('dy_report_logs')->where(['clickid'=>$clickid, 'uid'=>$uid])->first();
            if ($report_log) {          // 有上报初始记录则继续
                $send_order_id = getProp($report_log, 'send_order_id');
                $send_order = DB::table('send_orders')->where('id', $send_order_id)->first();
                if ($send_order) {      // 有派单信息则继续
                    // 先将派单总数+1
                    DB::table('send_orders')->where('id', $send_order_id)->increment('report_receive_num');
                    // 获取当前比例和上报总数|成功数
                    $report_percent = getProp($send_order, 'report_percent');
                    $report_receive_num = (int)getProp($send_order, 'report_receive_num') + 1;
                    $report_post_num = getProp($send_order, 'report_post_num');

                    if ($report_post_num/$report_receive_num*100 < $report_percent) {       // 未达到上报比例才上报
                        $params = [
                            'clickid'       => $clickid,
                            'event_type'    => 'active_pay',
                            'context'       => [
                                'ad'    => [
                                    'callback'  => $clickid,
                                ]
                            ],
                            'timestamp'     => (int)(microtime(true)*1000),
                            'trade_no'      => $trade_no,
                        ];
                        ReportDy::dispatch($params)->onConnection('redis');
                    }else {
                        DB::table('dy_report_logs')->where(['clickid'=>$clickid, 'event_type'=>''])->update([
                            'trade_no'          => $trade_no,
                            'event_type'        => 'active_pay',
                            'callback_result'   => '失败',
                            'remark'            => '比例过滤'
                        ]);
                    }
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 给邀请者发放分成收益
     * @param $uid
     * @param $fromUid
     * @param $fee
     * @return false|void
     */
    private function giveRewardToFromUser($uid, $fromUid, $fee)
    {
        # 给邀请者发放分成收益
        $from_user = User::leftJoin('user_share_configs', 'user_share_configs.level', 'users.share_level')
            ->where('users.id', $fromUid)->select('users.total_earnings', 'users.enable_withdraw_earnings',
                'user_share_configs.share_percent')->first();
        if ($from_user) {
            $change_amount = round($fee * $from_user->share_percent, 2);

            // 写入收益明细表
            $insert_log = [
                'uid'               => $fromUid,
                'from_uid'          => $uid,
                'charge_price'      => $fee,
                'share_percent'     => $from_user->share_percent,
                'withdraw_info'     => '',
                'charge_type'       => 1,
                'before_amount'      => $from_user->enable_withdraw_earnings,
                'change_amount'      => $change_amount,
                'after_amount'       => $from_user->enable_withdraw_earnings + $change_amount,
                'created_at'        => date('Y-m-d H:i:s'),
                'updated_at'        => date('Y-m-d H:i:s'),
            ];
            $boolen1 = UserEarningsLogs::create($insert_log);
            if (!$boolen1) {
                \Log::error('写入收益明细失败: '.json_encode($insert_log, 256));
                DB::rollBack();
                return false;
            }

            $from_user->total_earnings += $change_amount;
            $from_user->enable_withdraw_earnings += $change_amount;
            $boolen2 = User::where('id', $fromUid)->update([
                'total_earnings'                => $from_user->total_earnings,
                'enable_withdraw_earnings'      => $from_user->enable_withdraw_earnings,
                'updated_at'                    => date('Y-m-d H:i:s'),
            ]);
            if (!$boolen2) {
                \Log::error('给邀请者发放分成收益失败: from_uid: '.$fromUid);
                DB::rollBack();
                return false;
            }
        }
    }

    # 处理跨天订单，将第二天到账的订单的创建时间改为第二天，保证created_at和pay_end_at同一天，统计口径一致
    function orderAcrossDay($order_info)
    {
        if (date('Y-m-d', strtotime($order_info->created_at)) == date('Y-m-d')) return false;
        $created_at = date('Y-m-d H:i:s', strtotime($order_info->created_at));
        $trade_no = $order_info->trade_no;
        $init_order = [
            'distribution_channel_id' => $order_info->distribution_channel_id,
            'uid' => $order_info->uid,
            'product_id' => $order_info->product_id,
            'price' => $order_info->price,
            'pay_type' => $order_info->pay_type,
            'trade_no' => 'cd-' . $trade_no,
            'pay_merchant_source' => $order_info->pay_merchant_source,
            'pay_merchant_id' => $order_info->pay_merchant_id,
            'create_ip' => $order_info->create_ip,
            'send_order_id' => $order_info->send_order_id,
            'send_order_name' => $order_info->send_order_name,
            'order_type' => $order_info->order_type,
            'from_bid' => $order_info->from_bid,
            'from_type' => $order_info->from_type,
            'activity_id' => $order_info->activity_id,
            'inner_send_order_id' => $order_info->inner_send_order_id,
            'status' => 'UNPAID',
            'transaction_id' => '',
            'pay_end_at' => '0000-00-00 00:00:00',
            'created_at' => $created_at,
            'updated_at' => $created_at
        ];
        try {
            DB::table('orders')->where('id', $order_info->id)->update([
                'created_at' => date('Y-m-d H:i:s'),
                'pay_end_at' => date('Y-m-d H:i:s', time() + 5),
                'updated_at' => date('Y-m-d H:i:s', time() + 5),

            ]);
            //$order_info->created_at = Carbon::now();
            $order_info->created_at = date('Y-m-d H:i:s');
            DB::table('orders')->insert($init_order);
        } catch (\Exception $e) {
            \Log::error('orderAcrossDay_ept:'.$e);
            return false;
        }
        return true;
    }

    /**
     * 获取用户第几次充值
     * @param $uid
     * @return int
     */
    function getChargeTimes($uid)
    {
        $count = OrderService::getUserChargeTimes($uid);
        return $count + 1;
    }

    function getByTradeNo($trade_no){
        return Order::getByTradeNo($trade_no);
    }

}
