<?php

namespace common\helpers\bonus\Calc;


use common\helpers\bonus\BonusCalc;
use common\helpers\Cache;
use common\helpers\Form;
use common\models\CalcOperation;
use common\models\CalcRecord;
use common\models\forms\PeriodForm;
use common\models\Period;
use Yii;

/**
 * 业务端计算服务监听脚本
 */
class CalcConsole extends BaseBusiness
{
    const  CALC_DB_NAME = 'dbCalcServer';

    /**监听计算服务
     * @return array|bool
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\db\Exception
     */
    public static function listenCalcPeriod()
    {
        // 先检查redis的开关
        $calcingSwitch = Yii::$app->cache->get('isCalcing') ? 1 : 0;
        if(!$calcingSwitch){
            return true;
        }
        $db        = self::CALC_DB_NAME;
        $allPeriod = \Yii::$app->$db->createCommand("SELECT * FROM AR_PERIOD order by PERIOD_NUM desc")->queryAll();
        $period    = [];
        foreach ($allPeriod as $v) {
            if ($v['IS_PREPARE'] > 0) {
                $period = $v;
                break;
            }
        }
        if (empty($period)) {
            return true;
        }

        $businessPeriod = Period::find()->where(['PERIOD_NUM' => $period['PERIOD_NUM']])
            ->asArray()->one();

        if(!$period['CALC_ID'] || $period['CALC_ID'] != $businessPeriod['CALC_ID']) { // 如果计算ID不符，则退出
            return true;
        }

        if (empty($businessPeriod)) {
            return true;
        }

        if ($businessPeriod['SEND_STARTED_AT'] > 0 || $businessPeriod['IS_SENT'] > 0) {
            //开始挂网 或者 已挂网 则直接返回
            return true;
        }
//        if ($businessPeriod['IS_CLOSED']==0) {
//            // 如果没有封期 则直接返回
//            return true;
//        }

        //用户选择是否自动执行
        $autoExec             = $businessPeriod['AUTO_EXEC'] > Period::MANUAL_EXEC_CALC;
        $businessIsCalculated = $businessPeriod['IS_CALCULATED'] == Period::CALCULATE_FINISH;
        $startExecTime        = $businessPeriod['START_EXEC_TIME'] ?? 0;
        $businessIsProcessing = $businessPeriod['IS_PROCESSING'] ?? 0;
        $isPreparing = $businessPeriod['IS_PREPARING'];

        echo $businessPeriod['IS_CALCING'];
        //同步计算进度日志到业务系统
        if (4 == $period['IS_PREPARE'] && 1 == $period['IS_CALCULATED'] && 1 == $businessPeriod['IS_CALCING']) {
            self::syncLogRecord($period['PERIOD_NUM'], $db, $startExecTime);
            \Yii::$app->db->createCommand()->update('AR_PERIOD', ['IS_CALCING' => 0], 'PERIOD_NUM=' . $period['PERIOD_NUM'])->execute();
        }

        if (2 == $period['IS_PREPARE'] && 1 == $period['IS_PERFED'] && ($autoExec || $isPreparing) && !$businessIsProcessing) {
            //拉取期业绩
            CalcRecord::record($period['PERIOD_NUM'], '【期业绩】第' . $period['PERIOD_NUM'] . '期的业绩数据已生成');
            CalcRecord::record($period['PERIOD_NUM'], '【期业绩】开始获取第' . $period['PERIOD_NUM'] . '期的期业绩数据');

            Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::IS_PROCESSING);
            $res = (new PullPerfDataFromCalc($period['PERIOD_NUM']))->start();
            if (200 == $res['code']) {
                Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::NOT_PROCESSING);
                CalcRecord::record($period['PERIOD_NUM'], '【期业绩】第' . $period['PERIOD_NUM'] . '期的期业绩数据已获取');
            } else {
                //结束计算状态
                Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::NOT_PROCESSING);
                CalcRecord::record($period['PERIOD_NUM'], '【期业绩】第' . $period['PERIOD_NUM'] . '期的期业绩数据获取失败，原因：' . $res['msg']);
                return $res;
            }

            //自动执行且IS_PREPARE为3 直接开始结算流程
            CalcRecord::record($period['PERIOD_NUM'], '【奖金计算】开始计算第' . $period['PERIOD_NUM'] . '期的奖金');
            //同步周期表的值到业务系统
            self::pullPeriodForUpdate($period['PERIOD_NUM']);
            return \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['IS_PREPARE' => 3], 'PERIOD_NUM=:PERIOD_NUM', ['PERIOD_NUM' => $period['PERIOD_NUM']])->execute();
        }

        if (4 == $period['IS_PREPARE'] && 1 == $period['IS_CALCULATED'] && ($autoExec || $isPreparing) && !$businessIsProcessing && !$businessIsCalculated) {
            CalcRecord::record($period['PERIOD_NUM'], '【奖金计算】第' . $period['PERIOD_NUM'] . '期的奖金已计算完成');
            CalcRecord::record($period['PERIOD_NUM'], '【奖金计算】开始拉取第' . $period['PERIOD_NUM'] . '期的奖金数据');

            Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::IS_PROCESSING);
            //自动执行且IS_CALCULATED为1 直接开始拉取结算数据
            $res = (new PullCalcBonusData($period['PERIOD_NUM']))->start();
            if (200 == $res['code']) {
                CalcRecord::record($period['PERIOD_NUM'], '【奖金计算】第' . $period['PERIOD_NUM'] . '期的奖金数据已全部拉取');
                //待拉取完成后再将计算完成标识改为已完成
                \Yii::$app->db->createCommand()->update('AR_PERIOD', ['IS_CALCULATED' => 1], 'PERIOD_NUM=' . $period['PERIOD_NUM'])->execute();

                //自动执行完成 更新对应字段
                //计算结束
                Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::NOT_PROCESSING);
                //流程结束
                //同步周期表的值到业务系统
                self::pullPeriodForUpdate($period['PERIOD_NUM']);
                \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['CALC_ID' => ''], 'PERIOD_NUM=:PERIOD_NUM', ['PERIOD_NUM' => $period['PERIOD_NUM']])->execute();
                CalcOperation::closeOperation($businessPeriod['CALC_ID']);
                //拉取计算系统的进度日志
                self::syncLogRecord($period['PERIOD_NUM'], $db, $startExecTime);
                //设置计算进行中标识
                Period::updatePeriodIsPreparing($period['PERIOD_NUM'], Period::NOT_PREPARING);
                //设置redis开关
                Yii::$app->cache->set('isCalcing', 0);
            } else {
                //结束计算状态
                Period::updatePeriodIsProcessing($period['PERIOD_NUM'], Period::NOT_PROCESSING);
                \Yii::$app->$db->createCommand()->update('AR_PERIOD', ['CALC_ID' => ''], 'PERIOD_NUM=:PERIOD_NUM', ['PERIOD_NUM' => $period['PERIOD_NUM']])->execute();
                CalcOperation::closeOperation($businessPeriod['CALC_ID']);
//                CalcRecord::record($period['PERIOD_NUM'], '【奖金计算】第' . $period['PERIOD_NUM'] . '期的奖金数据获取失败，原因：' . $res['msg']);
                CalcRecord::record($period['PERIOD_NUM'], '【奖金计算】第' . $period['PERIOD_NUM'] . '期的奖金数据获取失败');
                //设置计算进行中标识
                Period::updatePeriodIsPreparing($period['PERIOD_NUM'], Period::NOT_PREPARING);
                //设置redis开关
                Yii::$app->cache->set('isCalcing', 0);
                return $res;
            }
            return true;
        }

        return true;
    }

    // 定时计算
    public static function autoPrePerf()
    {
        $nowTs = time();
        $nowDate = date("d", $nowTs);
        $nowHour = date("H", $nowTs);
        $nowMinuter = date('i', $nowTs);

        // 如果不是整点,则跳过计算
        if ($nowMinuter != 0) {
            echo('自动计算未开始. 【非整点时间】' . date('Y-m-d H:i:s') . PHP_EOL);
            return;
        }

        $currentPeriod = Period::find()->where('START_TIME< :NOW_TIME',['NOW_TIME'=>$nowTs])->where('END_TIME>= :NOW_TIME',['NOW_TIME'=>$nowTs-6])->asArray()->one();
        $periodNum = $currentPeriod['PERIOD_NUM'];
        $prevPeriodNum = $periodNum - 1;

        if ($currentPeriod['IS_SENT'] != 0){
            echo(date('Y-m-d H:i:s') . ' 期数：' . $periodNum . ',当前期数已挂网,异常,中止计算' . PHP_EOL);
            return;
        }

//        if ($nowDate >= 1 && $nowDate <= 4){
//            //判断上一期是否手动计算
//            $prevPeriod = Period::find()
//                ->where('PERIOD_NUM = :PERIOD_NUM AND HAND_CALC_TIME < :HAND_CALC_TIME', [
//                    ':PERIOD_NUM'     => $prevPeriodNum,
//                    ':HAND_CALC_TIME' => strtotime(date('Y-m-01'))
//                ])
//                ->asArray()
//                ->one();
//            if (!empty($prevPeriod)){
//                echo('上一期上次手动计算时间'.($prevPeriod['hand_calc_time']??'----').'，期数切换为' . $prevPeriodNum . PHP_EOL);
//                $periodNum = $prevPeriodNum;
//            }
//        }

        // 自动计算：次月1号凌晨计算一次上一期
        if ($nowDate == 1 && $nowHour == 0) {
            echo('自动计算开始(1). ' . date('Y-m-d H:i:s') . ' 期数：' . $prevPeriodNum . PHP_EOL);

            BonusCalc::instance()->calculateBonus($prevPeriodNum);

            return;
        }
        // 自动计算：每2小时自动计算一次
//        if ($nowHour % 2 == 0) {
            echo('自动计算开始(2). ' . date('Y-m-d H:i:s') . ' 期数：' . $periodNum . PHP_EOL);
            BonusCalc::instance()->calculateBonus($periodNum);
//        }
    }

    private static function processAutoExec($periodNum)
    {
        //设置计算进行中标识
        Period::updatePeriodIsPreparing($periodNum, Period::IS_PREPARING);
        //设置自动计算标识
        Period::updatePeriodIsAutoExec($periodNum, Period::AUTO_EXEC_CALC);
        //记录开始计算的时间
        $calc_id = uniqid('calc_');
        CalcOperation::record($calc_id, $periodNum);

        $formModel = new PeriodForm();
        $formModel->scenario = 'autoPerf';
        if($formModel->load(['periodNum'=>$periodNum], '') && $formModel->autoPrePerf()) {
            echo('【定时计算】第' . $periodNum . '期，定时计算开始' . PHP_EOL);
        } else {
            echo('【定时计算】第' . $periodNum . '期，定时计算未开始' . PHP_EOL);
        }
    }

    /**
     * 同步计算系统的计算进度日志到业务系统计算步骤表
     * @param $periodNum
     * @param $db
     * @param $startTime
     * @return bool|int
     * @throws \yii\base\InvalidConfigException
     * @throws \yii\db\Exception
     */
    public static function syncCalcRecord($periodNum, $db, $startTime = 0)
    {
        $type         = 'bonus';
        $calcTaskDesc = \Yii::$app->$db->createCommand("SELECT
        ID,PERIOD_NUM,TASK_DESCRIBE,START_AT FROM AR_SERVE_PROCESS 
        where TASK_TYPE='$type' AND PERIOD_NUM=$periodNum  AND START_AT>=$startTime
        order by START_AT desc")->queryAll();

        $calcTaskDescKeys = array_column($calcTaskDesc, null, 'ID');

        $currentRecordList    = CalcRecord::find()->where(['PERIOD_NUM' => $periodNum])->select('ID')->asArray()->column();
        $currentRecordListKey = array_flip($currentRecordList);

        $tableName = (new CalcRecord())->getTableSchema()->name;
        $result    = array_diff_key($calcTaskDescKeys, $currentRecordListKey);
        if (!empty($result)) {
            return \Yii::$app->db->createCommand()->batchInsert($tableName, ['ID', 'PERIOD_NUM', 'TEXT', 'CREATED_AT'], $result)->execute();
        }
        return true;
    }

    public static function syncLogRecord($periodNum, $db, $startTime = 0)
    {
        $calcTaskDesc = \Yii::$app->$db->createCommand("SELECT
        ID,PERIOD_NUM,LOG_DESCRIBE,CREATE_AT FROM AR_SERVE_LOG 
        where  PERIOD_NUM=$periodNum  AND CREATE_AT>=$startTime
        order by RECORD_SORT")->queryAll();

        $calcTaskDescKeys = array_column($calcTaskDesc, null, 'ID');

        $currentRecordList    = CalcRecord::find()->where(['PERIOD_NUM' => $periodNum])->select('ID')->asArray()->column();
        $currentRecordListKey = array_flip($currentRecordList);

        $tableName = (new CalcRecord())->getTableSchema()->name;
        $result    = array_diff_key($calcTaskDescKeys, $currentRecordListKey);
        if (!empty($result)) {
            return \Yii::$app->db->createCommand()->batchInsert($tableName, ['ID', 'PERIOD_NUM', 'TEXT', 'CREATED_AT'], $result)->execute();
        }
        return true;
    }
}
