<?php

namespace App\Modules\Finance\Services;

use App\Libs\Pay\SandPay;
use App\Modules\Channel\Models\Channel;
use App\Modules\Finance\Models\BatchPayments;
use App\Modules\Finance\Models\FinancialConfig;
use App\Modules\Finance\Models\FinancialStat;
use App\Modules\Finance\Models\Payment;
use App\Modules\Finance\Models\WithdrawCash;
use App\Modules\Manage\Models\Manage;
use Exception;
use Illuminate\Support\Collection;
use Illuminate\Support\Facades\DB;
use Illuminate\Support\Facades\Log;
use Redis;

/**
 * 更优的打款结构
 */
class AdvancedPaymentService
{
    const wait_merge = '待合并';

    const wait_pay = '待打款';
    const auto_wait_pay = '自动待打款';
    const artifical_wait_pay = '人工待打款';
    const wait_pay_status = [
        self::wait_pay,
        self::auto_wait_pay,
        self::artifical_wait_pay,
    ];

    const artifical_success = '人工打款成功';
    const auto_success = '自动打款成功';
    const success = "打款成功";
    const success_status = [
        self::success,
        self::auto_success,
        self::artifical_success,
    ];

    const paying = "打款中";
    const auto_paying = "自动打款中";
    const artifical_paying = "人工打款中";
    const paying_status = [
        self::paying,
        self::auto_paying,
        self::artifical_paying,
    ];

    const auto_failure = '自动打款失败';

    public function findConfig(int $channel_id)
    {
        FinancialConfig::where('distribution_channel_id', $channel_id)->first();
    }

    public function findFinancialStat(int $channel_id)
    {
        return FinancialStat::where('distribution_channel_id', $channel_id)->first();
    }

    public function findWithdrawCash(int $withdraw_cash_id)
    {
        return WithdrawCash::where('id', $withdraw_cash_id)->first();
    }

    public function isWithdrawCashSuccess(string $status)
    {
        if (in_array($status, self::success_status)) {
            return true;
        }
        return false;
    }

    /**
     * 添加人工打款记录
     */
    private function addPayment(WithdrawCash $withdraw, string $trade_no, int $uid, string $remark)
    {
        $isCompanyName = $withdraw->is_company == 0 ? "对私" : "对公";
        $remark = "[" . $isCompanyName . "]" . $remark;
        Payment::create(
            [
                'withdraw_cash_id' => $withdraw->id,
                'amount' => (float) $withdraw->amount - (float) $withdraw->tallage,
                'remark' => $remark,
                'pay_time' => date('Y-m-d H:i:s'),
                'pay_merchant_source' => "人工",
                'trade_no' => $trade_no,
                'status' => self::artifical_success,
                'pay_merchant_source_msg' => '',
                'is_company' => $withdraw->is_company,
                'amount_person' => $uid,
            ]
        );
    }

    /**
     * 查找操作账号
     */
    private function findManageUser(int $id)
    {
        return Manage::find($id);
    }

    /**
     * 更新提现信息状态
     * @param WithdrawCash $withdraw
     * @param string $status
     * @param int $uid
     * @param string $serial_number
     * @param string $remark
     * @return WithdrawCash
     */
    public function updateWithdrawCashStatus(WithdrawCash $withdraw, string $status, int $uid = 0, string $serial_number = '', string $remark = '')
    {
        if (in_array($withdraw->status, self::success_status)) {
            return;
        }
        $withdraw->status = $status;
        if ($uid) {
            $user = $this->findManageUser($uid);
            $withdraw->check_user_id = $uid;
            $withdraw->check_user_name = $user ? $user->nickname : '';
        }
        if ($remark) {
            $withdraw->remark = $remark;
        }
        if ($serial_number) {
            if (!strpos($withdraw->serial_number, $serial_number)) {
                $serial_number = $withdraw->serial_number . "," . $serial_number;
            }
            $withdraw->serial_number = $serial_number;
        }
        $withdraw->save();
        if (in_array($status, self::success_status)) {
            //成功提现
            $financialStat = $this->findFinancialStat($withdraw->distribution_channel_id);
            //修改累计提现金额
            $financialStat->accumulative_withdrawal_amount = (float) $financialStat->accumulative_withdrawal_amount + (float) $withdraw->amount;
            //修改提现中金额
            $financialStat->withdraw_pending_amount = (float) $financialStat->withdraw_pending_amount - (float) $withdraw->amount;
            $financialStat->save();
        }

        return $withdraw;
    }

    /**
     * 创建人工打款
     * @param $userId
     * @param $withdrawCashId
     * @param $amount
     * @param $remark
     * @param $tradeNo
     * @param int $amount_person
     * @return bool
     */
    public function makePersonMadePayment($userId, $withdrawCashId, $remark, $trade_no, $amount_person = 0)
    {
        try {
            DB::beginTransaction();
            $withdraw = $this->findWithdrawCash($withdrawCashId);
            //插入人工打款记录
            $this->addPayment($withdraw, $trade_no, $amount_person, $remark);
            //更新当然提现记录为 人工打款成功状态
            $this->updateWithdrawCashStatus($withdraw, self::artifical_success, $userId, $trade_no, $remark);
            DB::commit();
        } catch (Exception $e) {
            Log::error("人工打款失败日志" . $e->getTraceAsString());
            DB::rollback();
            return false;
        }
        return true;
    }

    /**
     * 对私打款改为对公打款
     */
    public function changePrivateToPublicPayment(int $uid, int $batch_payment_id, string $trade_no, string $account_name, string $card_number, string $account_bank)
    {
        $withdraws = WithdrawCash::where('batch_payment_id', $batch_payment_id)->get();
        DB::beginTransaction();
        foreach ($withdraws as $withdraw) {
            $withdraw->account_name = $account_name;
            $withdraw->bank_account = $card_number;
            $withdraw->account_bank = $account_bank;
            $withdraw->batch_payment_id = 0;
            $withdraw->status = '人工待打款';
            $withdraw->is_company = 1;
            $withdraw->is_batch_operate = 0;
            $withdraw->save();
            if (!$this->makePersonMadePayment($uid, $withdraw->id, "[对私转对公]", $trade_no)) {
                DB::rollback();
                return;
            }
        }
        BatchPayments::where('id', $batch_payment_id)->delete();
        DB::commit();
    }

    /**
     * 更新合并打款对应的提现状态
     */
    private function updateBatchPaymentWithdrawCashStatus(int $batch_payment_id, string $status, int $uid = 0, string $serial_number = '', string $remark = '')
    {
        $withdraws = WithdrawCash::where('batch_payment_id', $batch_payment_id)->get();
        foreach ($withdraws as $withdraw) {
            $this->updateWithdrawCashStatus($withdraw, $status, $uid, $serial_number, $remark);
            if (in_array($status, self::success_status)) {
                Payment::create([
                    'withdraw_cash_id' => $withdraw->id,
                    'amount' => $withdraw->amount,
                    'remark' => '合并打款',
                    'pay_time' => date('Y-m-d H:i:s'),
                    'pay_merchant_source' => 'SANDPAY',
                    'trade_no' => $serial_number,
                    'status' => self::auto_success,
                    'is_company' => $withdraw->is_company
                ]);
            }
        }
    }

    /**
     * 更新合并打款状态
     */
    public function updateBatchPaymentStatus(BatchPayments $payment, string $status, int $uid = 0, array $query_result = [], string $serial_number = '')
    {
        try {
            DB::beginTransaction();
            $payment->serial_number = $serial_number ? $serial_number : ($payment->serial_number ?? '');
            $payment->status = $status;
            $payment->pay_merchant_source_msg = $query_result ? json_encode($query_result) : '';
            $payment->pay_merchant_source_result = in_array($status, self::success_status) ? '处理成功' : '';
            $payment->save();
            $this->updateBatchPaymentWithdrawCashStatus($payment->id, $status, $uid, $payment->serial_number);
            DB::commit();
        } catch (Exception $e) {
            myLog('test')->info($e->getTraceAsString());
            DB::rollback();
        }
    }


    /**
     * 查找自动待打款的批量提现记录
     */
    public function findPayingBatchPayments()
    {
        return BatchPayments::where('status', self::auto_paying)->get();
    }

    public function findBatchPayment(int $batch_payment_id)
    {
        return BatchPayments::find($batch_payment_id);
    }

    /**
     * 设置打款的redis缓存
     */
    private function setRedis(string $redis_key, int $id)
    {
        Redis::sAdd($redis_key, $id);
        Redis::expire($redis_key, 60 * 60 * 24);
    }

    /**
     * 判断是否在合并打款中
     */
    public function judgeIsPayment($id)
    {
        $key = 'batch_payment:' . date('Y-m-d');
        if (Redis::sIsmember($key, $id)) {
            return true;
        } else {
            $result = BatchPayments::where('id', $id)->whereIn('status', self::paying_status)->exists();
            if ($result) {
                $this->setRedis($key, $id);
            }
            return $result;
        }
    }

    /**
     * 合并打款
     * @param $uid
     * @param $batch_payment_id
     * @param $amount
     * @param $remark
     * @return bool
     */
    public function makeThreeSourcePayment(BatchPayments $batch_payment, int $uid, float $amount, string $remark)
    {
        $trade_no = date('YmdHis') . ($batch_payment->channel_user_id);
        $pay_service = new SandPay;
        //对私
        $balance = $pay_service->queryBalance([
            'order_no' => date('YmdHis'),
            'pay_time' => $batch_payment->pay_time,
        ]);
        if ($balance > 0 && $balance >= $amount) {
            try {
                DB::beginTransaction();
                $this->setRedis('batch_payment:' . date('Y-m-d'), $batch_payment->id);
                //修改状态打款中
                $batch_payment->trade_no = $trade_no;
                $batch_payment->status = self::auto_paying;
                $batch_payment->pay_time = now();
                $batch_payment->save();

                $this->updateBatchPaymentWithdrawCashStatus($batch_payment->id, self::auto_paying, $uid, '', $remark);

                $result = $pay_service->agentPay([
                    'order_no' => $trade_no,
                    'pay_time' => (string) $batch_payment->pay_time,
                    'account_name' => $batch_payment->account_name,
                    'bank_account' =>  $batch_payment->bank_account,
                    'account_bank' => $batch_payment->account_bank,
                    'remark' => $remark,
                    'amount' => $amount,
                ]);

                $batch_payment->pay_merchant_source_msg = json_encode($result);
                $batch_payment->save();

                DB::commit();
                return true;
            } catch (Exception $e) {
                Log::error($e->getTraceAsString());
                DB::rollback();
            }
        }
    }

    /**
     * 添加合并打款数据
     */
    private function addBatchPayment(string $status, float $amount, int $is_company, WithdrawCash $model, array $channel_ids, array $ids)
    {
        $payment = [
            'amount' => $amount,
            'status' => self::wait_pay,
            'is_company' => $is_company,
            'channel_user_id' => $model->channel_user_id,
            'bank_account' => $model->bank_account,
            'account_name' => $model->account_name,
            'account_bank' => $model->account_bank,
            'remark' => "channel_ids:" . (implode(',', $channel_ids)) . ';withdraw_cash_ids:' . implode(',', $ids)
        ];
        $added = BatchPayments::create($payment);
        WithdrawCash::whereIn('id', $ids)->update([
            'batch_payment_id' => $added->id,
            'status' => $status
        ]);
    }

    /**
     * 查找待合并提现记录
     * @return Collection
     */
    private function findWaitMergeWithdrawCash(int $is_company)
    {
        $withdraws = WithdrawCash::where('distribution_channel_id', '>', 0)
            ->where('is_batch_operate', 1)
            ->where('is_company', $is_company)
            ->where('status', self::wait_merge)
            ->where(function ($query) {
                $query->whereNull('batch_payment_id')->orwhere('batch_payment_id', 0);
            })
            ->get();
        $channel_ids = $withdraws->pluck('distribution_channel_id')->all();
        $channels = Channel::whereIn('id', $channel_ids)->select('id', 'channel_user_id')->get();
        $withdraws->transform(function ($item) use ($channels) {
            $channel = $channels->where('id', $item->distribution_channel_id)->first();
            $item->channel_user_id = $channel ? $channel->channel_user_id : 0;
            return $item;
        });
        return $withdraws->sortByDesc('amount')
            ->sortBy('bank_account')
            ->sortBy('channel_user_id')
            ->sortBy('pay_merchant_company_id')
            ->values();
    }

    public function mergePayments(int $is_company)
    {
        //获取排好序的待合并数据
        $withdrawCashes = $this->findWaitMergeWithdrawCash($is_company);
        $amount = 0;
        $temp = $withdrawCashes->first();
        $channel_ids = [];
        $ids = [];
        if (count($withdrawCashes) > 0) {
            try {
                DB::beginTransaction();
                foreach ($withdrawCashes as $withdrawCash) {
                    if (
                        $temp->channel_user_id == $withdrawCash->channel_user_id
                        && $temp->bank_account == $withdrawCash->bank_account
                        && $temp->pay_merchant_company_id == $withdrawCash->pay_merchant_company_id
                        && $amount + $withdrawCash->amount < 50000
                    ) {
                        $amount += $withdrawCash->amount;
                        $ids[] = $withdrawCash->id;
                        if (!in_array($withdrawCash->distribution_channel_id, $channel_ids))
                            $channel_ids[] = $withdrawCash->distribution_channel_id;
                    } else {
                        $this->addBatchPayment(self::auto_wait_pay, $amount, $is_company, $temp, $channel_ids, $ids);
                        $amount = $withdrawCash->amount;
                        $ids = [];
                        $channel_ids = [];
                        $ids[] = $withdrawCash->id;
                        $channel_ids[] = $withdrawCash->distribution_channel_id;
                        $temp = $withdrawCash;
                    }
                }
                if ($amount > 0) {
                    $this->addBatchPayment(self::auto_wait_pay, $amount, $is_company, $temp, $channel_ids, $ids);
                }
                DB::commit();
            } catch (Exception $e) {
                myLog('add_batch_payment')->error($e->getMessage());
                DB::rollBack();
            }
        }
    }
}
