<?php

namespace common\models;

use common\helpers\Date;
use common\helpers\LoggerTool;
use common\helpers\Tool;
use common\libs\lock\RedisLock;
use Yii;
use common\libs\logging\operate\valueType\Config as ValueTypeConfig;
use yii\base\Exception;
use yii\db\Expression;

/**
 * This is the model class for table "{{%USER_PERFORMANCE}}".
 *
 * @property string $ID
 * @property string $USER_ID
 * @property string $CASH
 * @property integer $UPDATED_AT
 * @property integer $CLEAR_BY_CLOSED_AT
 * @property User $user
 */
class UserPerformance extends \common\components\ActiveRecord
{
    const NEWS = 10;
    const USING = 20;
    const FINISHED = 30;
    const NULLIFY = 40;
    const EXPIRED = 50;

    const USER_PERFORMANCE_BALANCE_LOCK_KEY = 'userPerformanceLock';

    public static function getEffective(): array
    {
        return [self::NEWS, self::USING];
    }

    public static function getInvalid(): array
    {
        return [self::FINISHED, self::NULLIFY, self::EXPIRED];
    }

    /**
     * {@inheritdoc}
     */
    public static function tableName()
    {
        return '{{%USER_PERFORMANCE}}';
    }

    /**
     * @inheritdoc
     */
    public function rules()
    {
        return [
            [['USER_ID'], 'required'],
            [['AMOUNTS', 'ORIGINAL'], 'number'],
            [['UPDATED_AT', 'STATUS_ID'], 'integer'],
            [['ID','USER_ID'], 'string', 'max' => 32],
            [['USER_ID'], 'unique'],
        ];
    }

    /**
     * @inheritdoc
     */
    public function attributeLabels()
    {
        return [
            'ID' => 'ID',
            'USER_ID' => '用户id',
            'AMOUNTS' => '当前余额',
            'ORIGINAL' => '原始金额',
            'UPDATED_AT' => '修改时间',
        ];
    }

    /**
     * 获取一名会员的余额
     * @param $userId
     * @return array|null
     */
    public static function getAmountByUserId($userId)
    {
        $data = UserPerformance::find()->select('SUM(AMOUNTS) AS AMOUNTS')->where('USER_ID=:USER_ID AND STATUS_ID<(:STATUS_ID)', [':USER_ID' => $userId, ':STATUS_ID' => self::FINISHED])->asArray()->one();
        if(!$data){
            $data = [
                'USER_ID' => $userId,
                'AMOUNTS' => 0,
            ];
        }
        return $data;
    }

    public static function getAmounts($userId)
    {
        $data = UserPerformance::find()->select('SUM(AMOUNTS) AS AMOUNTS')->where('USER_ID=:USER_ID AND STATUS_ID<(:STATUS_ID)', [':USER_ID' => $userId, ':STATUS_ID' => self::FINISHED])->asArray()->one();

        return $data['AMOUNTS'] ?? 0;
    }

    /**
     * @param $userId
     * @param $amount
     * @param string $orderId
     * @return true
     * @throws Exception
     * @throws \yii\db\Exception
     */
    public static function changeUserPerformance($userId, $amount, string $orderId = ''): bool
    {
        if ($amount == 0) {
            return true;
        }

        // 会员总绩效奖金
        $userPerformanceAmount = self::getAmountByUserId($userId);
        if ($amount > $userPerformanceAmount) {
            throw new Exception(Yii::t('app', 'applicantPrpShort'));
        }

        $period = Period::instance();
        $periodNum = $period->getNowPeriodNum();

        $calcYearMonth = $period->getYearMonth($periodNum);
        // redis加锁（防止并发余额数值不准确出错）
        $lockKey = self::USER_PERFORMANCE_BALANCE_LOCK_KEY . $userId;

        if (RedisLock::instance()->lock($lockKey)) {
            // 根据规则获取绩效奖金：过期时间(升序) -> 发放时间(升序)
            $records = self::find()
                ->where('USER_ID=:USER_ID AND AMOUNTS>0 AND STATUS_ID<:STATUS_ID', [':USER_ID' => $userId, ':STATUS_ID' => self::FINISHED])
                ->orderBy('EXPIRED_AT ASC, CREATED_AT ASC')
                ->asArray()
                ->all();

            $db = \Yii::$app->db;
            $transaction = $db->beginTransaction();
            try {
                // 循环扣除绩效奖金
                $surplus = $amount;
                foreach ($records as $record) {
                    $balance = 0;
                    if ($record['AMOUNTS'] > $surplus) {
                        $balance = $record['AMOUNTS'] - $surplus;
                        // 扣除奖金
                        UserPerformance::updateAll(['AMOUNTS' => $balance, 'STATUS_ID' => self::USING], 'ID=:ID', [':ID' => $record['ID']]);
                        // 写日志
                        UserPerformanceLogs::changeAmountLogs($record['ID'], $surplus, $periodNum, $orderId);
                        break;
                    }

                    if ($record['AMOUNTS'] == $surplus) {
                        $balance = $record['AMOUNTS'] - $surplus;
                        // 扣除奖金
                        UserPerformance::updateAll(['AMOUNTS' => $balance, 'STATUS_ID' => self::FINISHED], 'ID=:ID', [':ID' => $record['ID']]);
                        // 写日志
                        UserPerformanceLogs::changeAmountLogs($record['ID'], $surplus, $periodNum, $orderId);

                        break;
                    }

                    if ($record['AMOUNTS'] < $surplus) {
                        $balance = $surplus - $record['AMOUNTS'];
                        $surplus = $balance;
                        // 扣除奖金
                        UserPerformance::updateAll(['AMOUNTS' => 0, 'STATUS_ID' => self::FINISHED], 'ID=:ID', [':ID' => $record['ID']]);
                        // 写日志
                        UserPerformanceLogs::changeAmountLogs($record['ID'], $surplus, $periodNum, $orderId);
                    }
                }

                $transaction->commit();
            } catch (\Exception $e) {
                $transaction->rollBack();
                throw new Exception($e->getMessage());
            }

            // 解除锁
            RedisLock::instance()->unlock($lockKey);
        } else {
            throw new Exception(Yii::t('app', 'flowCreateError'));
        }

        return true;
    }

    /**
     * 绩效奖金发放
     * @param $userId
     * @param $amount
     * @param $bountyPeriodNum
     * @return bool
     * @throws Exception
     */
    public static function sentUserPerformance($userId, $amount, $bountyPeriodNum): bool
    {
        if ($amount == 0) {
            return true;
        }

        // 会员国家
        $countryCode = Countries::getById(User::getEnCodeInfo($userId)['COUNTRY_ID'])['CODE'];

        $period = Period::instance();
        $periodNum = $period->getNowPeriodNum();

        $db = \Yii::$app->db;
        $transaction = $db->beginTransaction();
        try {
            // 奖金发放
            $id = 'EP' . Tool::generateUserPerformanceNo($countryCode);
            self::insertOne([
                'ID' => $id,
                'USER_ID' => $userId,
                'AMOUNTS' => $amount,
                'ORIGINAL' => $amount,
                'STATUS_ID' => self::NEWS,
                'EXPIRED_AT' => date('Y-m-d H:i:s', strtotime('+1 year', time())),
                'CREATED_AT' => date('Y-m-d H:i:s', time()),
                'UPDATED_AT' => date('Y-m-d  H:i:s', time()),
                'REMARK' => DealType::getDealTypeTagById(DealType::USER_PERFORMANCE_SEND),
                'BOUNTY_PERIOD_NUM' => $bountyPeriodNum,
                'PAID_PERIOD_NUM' => $periodNum,
            ]);

            // 写日志
            UserPerformanceLogs::changeAmountLogs($id, $amount, $periodNum, '', DealType::getDealTypeTagById(DealType::USER_PERFORMANCE_SEND));

            $transaction->commit();
        } catch (\Exception $e) {
            $transaction->rollBack();
            throw new Exception(sprintf('PB奖金挂网异常：File: %s, Line: {%s}, message: %s', $e->getFile(), $e->getLine(), $e->getMessage()));
        }

        return true;
    }

    /**
     * 绩效奖金发放
     * @param $userId
     * @param $amount
     * @param $bountyPeriodNum
     * @return bool
     * @throws Exception
     */
    public static function sentUserPerformanceApi($userId, $amount, $bountyPeriodNum): bool
    {
        if ($amount == 0) {
            return true;
        }

        // 会员国家
        $countryCode = Countries::getById(User::getEnCodeInfo($userId)['COUNTRY_ID'])['CODE'];

        $db = \Yii::$app->db;
        $transaction = $db->beginTransaction();
        try {
            // 奖金发放
            $id = 'EP' . Tool::generateUserPerformanceNo($countryCode);
            self::insertOne([
                'ID' => $id,
                'USER_ID' => $userId,
                'AMOUNTS' => $amount,
                'ORIGINAL' => $amount,
                'STATUS_ID' => self::NEWS,
                'EXPIRED_AT' => date('Y-m-d H:i:s', strtotime('+1 year', time())),
                'CREATED_AT' => date('Y-m-d H:i:s', time()),
                'UPDATED_AT' => date('Y-m-d  H:i:s', time()),
                'REMARK' => DealType::getDealTypeTagById(DealType::USER_PERFORMANCE_SEND),
                'BOUNTY_PERIOD_NUM' => $bountyPeriodNum,
                'PAID_PERIOD_NUM' => $bountyPeriodNum+1,
            ]);

            // 写日志
            UserPerformanceLogs::changeAmountLogs($id, $amount, $bountyPeriodNum+1, '', DealType::getDealTypeTagById(DealType::USER_PERFORMANCE_SEND));

            $transaction->commit();
        } catch (\Exception $e) {
            $transaction->rollBack();
            throw new Exception(sprintf('PB奖金挂网异常：File: %s, Line: {%s}, message: %s', $e->getFile(), $e->getLine(), $e->getMessage()));
        }

        return true;
    }
}
