<?php


namespace App\Servers;




use App\Jobs\BusinessFileJob;
use App\Jobs\MsgFileJob;
use App\Models\BlockItems;
use App\Models\Contract;
use App\Models\ContractDistribution;
use App\Models\ContractLog;
use App\Models\ErrorRecord;
use App\Models\Invest;
use App\Models\Level;
use App\Models\Member;
use App\Models\MemberBoth;
use App\Models\MemberClan;
use App\Servers\Icon\BanRPC;
use App\Servers\Icon\TronRPC;
use Illuminate\Support\Facades\DB;

/**
 * Redis数据缓存类
 */
class ContractServer
{


    static private $server = '';
    private $distributionArr=[
      1=>'平台奖金',
      2=>'直推奖金',
      3=>'间推奖金',
      4=>'团队奖金',
      5=>'公排池',
      6=>'沉淀奖',
      7=>'代币奖',
      8=>'公排出局',
    ];

    /**
     * @return string[]
     */
    public function getDistributionArr(): array
    {
        return $this->distributionArr;
    }

    /**
     * 获取清分类型
     * @param $type
     * @return string
     */
    public function getDistributionArrStr($type)
    {
        return empty($this->distributionArr[$type])?'无':$this->distributionArr[$type];
    }


    private function __construct()
    {

    }

    /**
     * 创建对象
     * @return ContractServer
     */
    static function creatServer()
    {
        if (empty(self::$server)) {
            self::$server = new  ContractServer();
        }
        return self::$server;
    }

    /**
     * 报单信息检测
     */
    function broadcastDetection(){
        //检测报单信息
        $contractList=Contract::where('status',1)->limit(50)->get();
        $sysAddress=CommonServer::creatServer()->getConfigValue('sys_address');
        $sysAddress=strtolower($sysAddress);
        foreach ($contractList as $contract){
            $contractNum=Contract::where('hash',$contract->{'hash'})->count();
            if($contractNum>0){
                $broadcastData=BanRPC::creatServer()->getTransactionReceipt($contract->{'hash'});
                $memberAddress=Member::where('id',$contract->{'m_id'})->value('address');
                $memberAddress=strtolower($memberAddress);
                if (empty($broadcastData)) {
                    //交易正在广播中,下次继续查询
                    continue;
                } else {
                    //交易已完成 返回数据格式数据 ,将数据json保存
                    $updateInfo['broadcast_data'] = json_encode($broadcastData);
                    if ($broadcastData['status'] == '0x1') {
                        if($sysAddress!=strtolower($broadcastData['to'])){
                            $updateInfo['status'] = 3;
                            $updateInfo['error_msg'] = '收款地址不匹配';
                        }elseif ($memberAddress!=strtolower($broadcastData['from'])){
                            $updateInfo['status'] = 3;
                            $updateInfo['error_msg'] = '转出地址不匹配';
                        }else{
                            $updateInfo['status'] = 2;
                        }
                    } else {
                        $updateInfo['status'] = 3;
                        $updateInfo['error_msg'] = '广播失败';
                    }

                }
            }else{
                $updateInfo['status'] = 3;
                $updateInfo['error_msg'] = '当前哈希被重复使用';
            }
            //检测是否有更新细腻
            if($updateInfo){
                DB::beginTransaction();
               $ret= $contract->update($updateInfo);
               if(empty($ret)){
                   CommonServer::creatServer()->addErrorRecord('保单信息更新失败',$contract,0);
                   DB::rollBack();
               }
               if($updateInfo['status']==2){
                   //报单成功,就进行清分
                   $memberTeam=MemberClan::where('m_id',$contract->{'m_id'})->select(['id','m_id','p_ids','one_m_id','two_m_id'])->first();
                   $pIds=array_reverse(array_filter(explode(',',$memberTeam->{'p_ids'})));
                   //平台清分
                   $this->platformDistribution($contract,$pIds);
                   //直推及间推发放
                   $this->directProportion($contract,$memberTeam);
                   //团队清分
                   $this->teamProportion($contract,$pIds);
                   //公排分账
                   $this->commonProportion($contract);
                   //沉淀账户分账
                   $this->sedimentProportion($contract);
                   //代币清分
                   $this->agencyProportion($contract);
                   //触发会员升级
                   JobServer::creatServer()->verificationLevelJob($pIds);
               }
               DB::commit();
            }
        }
        return $contractList->count();
    }

    /**
     * 系统分账功能
     * @param Contract $contract
     * @param $pIds
     */
    private function platformDistribution(Contract $contract,$pIds){
        $platformMoney=$contract->{'platform_money'};
        if(empty($platformMoney)){
            $this->addContractLog($contract,'平台清分金额为0,停止平台清分');
        }else{
            //验证平台分红
            $receiveLevelId=CommonServer::creatServer()->getConfigValue('receive_level_id');
            //用户分红金额
            if(empty($receiveLevelId)){
                $this->addContractLog($contract,'未设置平台分红等级,跳出分红');
            }else{
                $pMember=Member::whereIn('id',$pIds)->where('level_id','>=',$receiveLevelId)->orderBy('id','desc')->select(['id','address','is_auto'])->first();
                if($pMember){
                    $receiveUserMax=CommonServer::creatServer()->getConfigValue('receive_user_max');
                    $teamContractNum=$this->getTeamContractNum($pMember->{'id'});
                    if($teamContractNum>=$receiveUserMax){
                        //最大比例
                        $receiveProportion=CommonServer::creatServer()->getConfigValue('receive_max');
                        $this->addContractLog($contract,$teamContractNum.'团队数量满足条件,执行最大分红比例');
                    }else{
                        //最小比例
                        $receiveProportion=CommonServer::creatServer()->getConfigValue('receive_mix');
                        $this->addContractLog($contract,$teamContractNum.'团队数量满不足条件,执行最小分红比例');
                    }
                    if(empty($receiveProportion)){
                        $this->addContractLog($contract,$teamContractNum.'平台分红比例不存在,跳出分红');
                    }else{
                        $receiveMoney=round($platformMoney*$receiveProportion/100,6);
                        if($receiveMoney<=0){
                            $this->addContractLog($contract,$receiveProportion.'分红比例金额小于0,跳出分红');
                        }else{
                            //计算推荐分红数量及平台收益数量
                            if($pMember->{'is_auto'}==0){
                                $this->addContractLog($contract,'后台升级用户,跳出分红');
                            }else{
                                $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$receiveMoney,1,$pMember->{'id'},$pMember->{'address'},$receiveProportion,1,'平台用户分红');
                                $platformMoney-=$receiveMoney;
                            }

                        }
                    }
                }else{
                    $this->addContractLog($contract,$receiveLevelId.'及以上的父级不存在,跳出分红');
                }
            }
            $openSys=CommonServer::creatServer()->getConfigValue('open_sys');
            if($openSys){
                $receiveAddress=CommonServer::creatServer()->getConfigValue('receive_address');
                if(empty($receiveAddress)){
                    $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$platformMoney,1,0,'',$contract->{'platform_money'},0,'平台分红至系统账户');
                }else{
                    $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$platformMoney,1,0,$receiveAddress,$contract->{'platform_money'},1,'平台分红至系统账户');
                }
            }else{
                $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$platformMoney,1,0,'',$contract->{'platform_money'},0,'平台关闭转账至系统账户,不进行划拨分账');
            }
        }
    }

    /**
     * 直推及间推
     * @param Contract $contract
     * @param MemberClan $memberTeam
     */
    private function directProportion(Contract $contract,MemberClan $memberTeam){
        $directMember=Member::where('id',$memberTeam->{'one_m_id'})->select(['id','address','is_boss'])->first();
        if(empty($directMember)){
            $this->addContractLog($contract,'直推人信息异常,停止清分');
        }else{
            if($directMember->{'is_boss'}){
                $this->addContractLog($contract,'直推人是boss,直与间都划分为直推人');
                $directProportion=($contract->{'direct_proportion'}+$contract->{'indirect_proportion'});
                $directMoney=round(($contract->{'money'}-$contract->{'platform_money'})*$directProportion/100,6);
                $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$directMoney,2,$directMember->{'id'},$directMember->{'address'},$directProportion,1,'直推BOSS分红');
            }else{
                $directMoney=round(($contract->{'money'}-$contract->{'platform_money'})*$contract->{'direct_proportion'}/100,6);
                $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$directMoney,2,$directMember->{'id'},$directMember->{'address'},$contract->{'direct_proportion'},1,'直推分红');
                $indirectMember=Member::where('id',$memberTeam->{'two_m_id'})->select(['id','address','is_boss'])->first();
                $indirectMoney=round(($contract->{'money'}-$contract->{'platform_money'})*$contract->{'indirect_proportion'}/100,6);
                if(empty($indirectMember)){
                    $this->addContractLog($contract,'间推人信息不存在,停止清分');
                    $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$indirectMoney,3,$directMember->{'id'},$directMember->{'address'},$contract->{'indirect_proportion'},0,'间推推分红,间推人不存在,未划分');
                }else{
                    $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$indirectMoney,3,$directMember->{'id'},$directMember->{'address'},$contract->{'indirect_proportion'},1,'间推推分红');
                }
            }
        }
    }

    /**
     * 团队清分
     * @param Contract $contract
     * @param $pIds
     */
    private function teamProportion(Contract $contract,$pIds){
        $levels=Level::select(['direct_num','id','lateral_num'])->get()->toArray();
        $levelArr=[];
        foreach ($levels as $level){
            $levelArr[$level['id']]=$level;
        }
        $totalMoney=$contract->{'money'}-$contract->{'platform_money'};
        $teamTotalMoney=round($totalMoney*$contract->{'team_proportion'}/100,6);
        $startId=1;
        $isLateral=false;
        //已经使用过的比例
        $useProportion=0;
        //已经使用金额
        $userMoney=0;
        //使用金额
        $grantMoney=0;
        foreach ($pIds as $pId){
            $pMember=Member::where('id',$pId)->select(['id','address','level_id'])->first();
            if(empty($pMember)){
                $this->addContractLog($contract,$pId.',无当前父及信息,停止清分');
            }else{
                $levelData=empty($levelArr[$pMember->{'level_id'}])?0:$levelArr[$pMember->{'level_id'}];
                if(empty($levelData)){
                    $this->addContractLog($contract,$pMember->{'id'}.'ID,'.$pMember->{'level_id'}.',无当前等级配置信息,停止清分');
                }else{
                    if($pMember->{'level_id'}>$startId || ($pMember->{'level_id'}==$startId &&!$isLateral)){
                        //等级大于等于当前等级
                        $isLateral=true;
                        //等级比例
                        $levelProportion=$levelData['direct_num'];
                        //等级比例-减去已经使用比例,获取到可用比列
                        $grantProportion=$levelProportion-$useProportion;
                        //累计已经使用的比例
                        $useProportion+=$grantProportion;
                        $grantMoney=round($grantProportion*$totalMoney/100,6);
                        if($userMoney+$grantMoney>$teamTotalMoney){
                            $grantMoney=$teamTotalMoney-$userMoney;
                            if($grantMoney<=0){
                                $this->addContractLog($contract,$pMember->{'level_id'}.'层级,资金已不足,停止团队奖金清分');
                            }
                        }
                        $userMoney+=$grantMoney;
                        $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$grantMoney,4,$pMember->{'id'},$pMember->{'address'},$grantProportion,1,$pMember->{'level_id'}.'级别团队分红');
                    }elseif($pMember->{'level_id'}==$startId && $isLateral){
                        //平级奖金发放
                        $isLateral=false;
                        $startId=$pMember->{'level_id'}+1;
                        $lateralMoney=round($grantMoney*$levelData['lateral_num']/100,6);
                        if($lateralMoney>0){
                            if($userMoney+$lateralMoney>$teamTotalMoney){
                                $lateralMoney=$teamTotalMoney-$userMoney;
                                if($lateralMoney<=0){
                                    $this->addContractLog($contract,$pMember->{'level_id'}.'层级,资金已不足,停止团队平级奖金清分');
                                }
                            }
                            $userMoney+=$lateralMoney;
                            $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$lateralMoney,4,$pMember->{'id'},$pMember->{'address'},$levelData['lateral_num'],1,$pMember->{'level_id'}.'级别团队平级分红');
                        }else{
                            $this->addContractLog($contract,$pMember->{'level_id'}.'层级,平级奖金小于等于0,停止团队平级奖金清分');
                        }
                        if($startId>count($levels)){
                            //清分完成,跳出循环
                            break;
                        }
                    }
                }
            }
        }
    }

    /**
     * 公排分账
     * @param Contract $contract
     */
    private function commonProportion(Contract $contract){
        $commonMoney=round(($contract->{'money'}-$contract->{'platform_money'})*$contract->{'common_proportion'}/100,6);
        $openSys=CommonServer::creatServer()->getConfigValue('open_sys');
        if($openSys && $commonMoney>0){
            $commonAddress=CommonServer::creatServer()->getConfigValue('common_address');
            if(empty($commonAddress)){
                $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$commonMoney,5,0,'',$contract->{'common_proportion'},0,'公排分账,无收款地址信息。系统关闭');
            }else{
                $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$commonMoney,5,0,$commonAddress,$contract->{'common_proportion'},1,'公排分账');
            }
        }else{
            $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$commonMoney,5,0,'',$contract->{'common_proportion'},0,'平台关闭转账至系统账户,不进行划拨分账');
        }
        //公排触发清算队列
        JobServer::creatServer()->contractCommonJob();
    }

    /**
     * 沉淀账户收款地址
     * @param Contract $contract
     */
    private function sedimentProportion(Contract $contract){
        $sedimentMoney=round(($contract->{'money'}-$contract->{'platform_money'})*$contract->{'sediment_proportion'}/100,6);
        $openSys=CommonServer::creatServer()->getConfigValue('open_sys');
        if($openSys && $sedimentMoney>0){
            $sedimentAddress=CommonServer::creatServer()->getConfigValue('sediment_address');
            if(empty($sedimentAddress)){
                $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$sedimentMoney,6,0,'',$contract->{'sediment_proportion'},0,'沉淀分账,无收款地址信息。系统关闭');
            }else{
                $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$sedimentMoney,6,0,$sedimentAddress,$contract->{'sediment_proportion'},1,'沉淀分账');
            }
        }else{
            $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$sedimentMoney,6,0,'',$contract->{'sediment_proportion'},0,'平台关闭转账至系统账户,不进行划拨分账');
        }
    }

    /**
     * 代币比例
     * @param Contract $contract
     */
    private function agencyProportion(Contract $contract){
        $agencyMoney=round(($contract->{'money'}-$contract->{'platform_money'})*$contract->{'agency_proportion'}/100,6);
        $exchangeMoney=CommonServer::creatServer()->getConfigValue('exchange_money');
        $dbMoney=$agencyMoney*$exchangeMoney;
        if($exchangeMoney >0){
            $member=Member::where('id',$contract->{'m_id'})->select(['id','address','is_boss'])->first();
            $this->addDistribution($contract->{'id'},$contract->{'m_id'},$dbMoney,0,7,$member->{'id'},$member->{'address'},$contract->{'agency_proportion'},1,'代币分账');
            MoneyDetailServer::creatServer()->write(1,1,$dbMoney,1,$member->{'id'},'报单代币清分',$contract->{'id'});
        }else{
            $sedimentAddress=CommonServer::creatServer()->getConfigValue('sediment_address');
            $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$agencyMoney,7,0,$sedimentAddress,$contract->{'agency_proportion'},1,'代币价格为0,转入沉淀分账');
            $this->addContractLog($contract,'代币价格为0,不分发');
        }

    }

    /**
     * 添加清分记录
     * @param $contractId
     * @param $mId
     * @param $nbnMoney
     * @param $dbMoney
     * @param $type
     * @param $toMid
     * @param $toAddress
     * @param $proportion
     * @param $status
     * @param string $remark
     */
    function addDistribution($contractId,$mId,$dbMoney,$nbnMoney,$type,$toMid,$toAddress,$proportion,$status,$remark='奖金发放'){
        $serviceMoney=0;
        if($toMid>0 && $nbnMoney>0){
            $serviceMoney=CommonServer::creatServer()->getConfigValue('service_money')?:0;
        }
        ContractDistribution::create([
            'contract_id' => $contractId,
            'broadcast_id' => 0,
            'm_id' => $mId,
            'db_money' => $dbMoney,
            'nbn_money' => $nbnMoney,
            'type' => $type,
            'to_m_id' => $toMid,
            'to_address' => $toAddress,
            'status' => $status,
            'proportion' => $proportion,
            'service_money' => $serviceMoney,
            'remark' => $remark
        ]);
    }

    /**
     * 获取团队合约数量
     * @param $mId
     * @return int
     */
    function getTeamContractNum($mId){
        $contractNum=0;
        $teamIds=MemberClan::where('id',$mId)->value('m_ids');
        $teamIds=array_filter(explode(',',$teamIds));
        $teamIds[]=$mId;
        if($teamIds){
            $contractNum=Contract::whereIn('m_id',$teamIds)->whereIn('status',[2,4])->count();
        }
        return $contractNum;
    }

    /**
     * 公排出局检测
     * @throws \Exception
     */
    function verifyContractCommon(){
        $commonNum=CommonServer::creatServer()->getConfigValue('common_num');
        $contractIds=Contract::where('status',2)->where('is_out',0)->orderBy('id','asc')->limit($commonNum)->pluck('id')->toArray();
        if(count($contractIds)>=$commonNum){
            //选择第一个单子排队出公排
            $contract=Contract::where('status',2)->orderBy('id','asc')->first();
            DB::beginTransaction();
            Contract::whereIn('id',$contractIds)->update(['is_out'=>1]);
            $commonBnb=CommonServer::creatServer()->getConfigValue('common_bnb');
            $commonDb=CommonServer::creatServer()->getConfigValue('common_db');
            $exchangeMoney=CommonServer::creatServer()->getConfigValue('exchange_money');
            $member=Member::where('id',$contract->{'m_id'})->select(['id','address','is_boss'])->first();
            $this->addDistribution($contract->{'id'},$contract->{'m_id'},0,$commonBnb,9,$member->{'id'},$member->{'address'},$commonBnb,1,'bsn公排出局');
            $dbMoney=1*$exchangeMoney*$commonDb;
            $this->addDistribution($contract->{'id'},$contract->{'m_id'},$dbMoney,0,9,$member->{'id'},$member->{'address'},$commonBnb,1,'代币公排出局');
            MoneyDetailServer::creatServer()->write(1,2,$dbMoney,1,$member->{'id'},'公排池代币出局',$contract->{'id'});
            $contract->update(['status' => 4]);
            DB::commit();
        }
    }



    /**
     * 合约日志记录
     * @param Contract $contract
     * @param $msg
     * @param array $data
     */
    function addContractLog(Contract $contract,$msg,$data=[]){
        ContractLog::create([
           'contract_id'=> $contract->{'id'},
           'm_id'=> $contract->{'m_id'},
           'msg'=> $msg,
           'contract_data'=> json_encode($contract),
           'data'=> json_encode($data),
        ]);
    }



}