<?php
namespace app\api\controller\coin;

use app\models\coin\UserCoinTransfer;
use app\models\redis\UserHash;
use app\models\store\StoreOrderCartInfo;
use app\models\system\DictCoin;
use app\models\user\UserCoin;
use app\Request;
use crmeb\services\{UtilService, JsonService};
use think\facade\Config;
use think\facade\Cache;
use think\facade\Log;

class UserCoinController {
    public function status(Request $request) {
        // 是否开启挖矿
        $symbol = Config::get('app.mining_symbo');
        //
        $icon = Cache::get($symbol);
        if (!$icon) {
            $icon = DictCoin::where('symbol', $symbol)->value('icon');
            Cache::set($symbol, $icon);
        }
        //
        $defStatus = [
            'boot' => 0,
            'stop' => 0,
            'progress'=> 0,
            'symbol'=>$symbol,
            'icon'=>$icon,
            'price'=> 0.00,
            'total'=> 0,
            'step' => 0,
            'ts' => 0,
        ];

        if (!$symbol) {
            Log::warning('mining disabled: $symbol');
            return app('json')->successful($defStatus);
        }

        $uid = $request->uid();
        $mymining = UserHash::mining_get($uid);
        if (!$mymining) {
            $mymining = $defStatus;
        }
        if ($mymining['progress'] > 0.0) {
            $mymining = $this->calcMining($uid, $mymining);
            UserHash::mining_set($uid, $mymining);
        } else {
            $mymining['total'] = UserCoin::where('uid', $uid)->where('symbol', $symbol)->value('balance') ?? 0.0;
        }
        return app('json')->successful([
            'symbol' => $mymining['symbol'],
            'icon' => $mymining['icon'],
            'price' => $mymining['price'],
            'step' => $mymining['step'],
            'progress' => $mymining['progress'],
            'total' => $mymining['total'],
        ]);
    }

    /**
     * 根据 $step 返回一个新 $step
     * 新的 $step 值为围绕 旧 $step 周围的随机值，上下波动幅度为 10%
     */
    protected function floatStep($step)
    {
        $amp = bcdiv($step, 10, 8);
		$min = bcsub($step, $amp, 8);
		$max = bcadd($step, $amp, 8);
		$distance = bcsub($max, $min, 8);
		$section = bcdiv($distance, 10, 8);
		return bcmul($section, mt_rand(0, 10), 8) + $min;
    }

    /**
     * 根据上次挖矿状态, 和过去的时长, 计算当前的状态
     * @param $p
     * @return mixed
     */
    protected function calcMining($uid, $p) {
        if (!isset($p['ts']) || 
            !isset($p['boot']) || 
            !isset($p['stop']) || 
            !isset($p['step']) || 
            !isset($p['progress'])) {
            Log::warning('error format.');
            return $p;
        }

        if ($p['progress'] <= 0) {
            return $p;
        }
        $step = $this->floatStep($p['step']);
        $now = time();
        $secs_passed = $now - $p['ts'];     // 从上次到现在
        //
        if ($now >= $p['stop']) { // 挖矿结束
            $secs_remain = $p['stop'] - $p['ts'];
            if ($secs_remain < 0) {
                $secs_remain = 0;
            }
            // 本次个数
            $count = floatval(bcmul($step, $secs_remain, 8));
            $p['progress'] += floatval(bcadd($p['progress'], $count, 8));
            // save to db
            UserCoinTransfer::beginTrans();
            $r1 = UserCoinTransfer::addMining($uid, $p['order_id'], $p['symbol'], $p['progress']);
            $r2 = UserCoin::upsertCoin($uid, $p['symbol'], $p['progress']);
            UserCoinTransfer::checkTrans($r1 && $r2);
            if (!$r1 || !$r2) {
                $amount = $p['progress'];
                Log::error("user<$uid> save transfer failed, amount<$amount>");
                return $p;
            }
            // -- save to db
            $p['ts'] = $now;
            $p['total'] = floatval(bcadd($p['total'], $p['progress'], 8));
            $p['progress'] = 0;
            return $p;
        }
        // 进行中
        $count = floatval(bcmul($step, $secs_passed, 8));
        $p['progress'] = floatval(bcadd($p['progress'], $count, 8));
        $p['ts'] = $now;

        return $p;
    }

    /**
     * 启动
     */
    public function boot(Request $request) {
        $uid = $request->uid();
        $now = time();
        // 是否开启活动
        $symbol = Config::get('app.mining_symbo');
        if (!$symbol) {
            return app('json')->fail('本活动未开启');
        }
        // 是否已经开启
        $mining = UserHash::mining_get($uid);
        if ($mining) {
            if (isset($mining['progress']) && $mining['progress'] > 0.0
                && isset($mining['boot']) && isset($mining['stop']) && isset($mining['ts'])) {
                return app('json')->fail('已启动');
            }
        }

        // 是否有订单
        $orderId = StoreOrderCartInfo::getMiningOrderId($uid);
        if (!$orderId) {
            return app('json')->fail('参加活动后可启动，请参看新手教程');
        }
        // 标记订单
        if (!StoreOrderCartInfo::setMining($orderId)) {
            return app('json')->fail('启动失败');
        }
        // 开启
        $secs_unit = Config::get('app.mining_sec_unit');
        $reward_unit = Config::get('app.mining_num_per_unit');
        $step = floatval(bcdiv($reward_unit, $secs_unit, 8));
        $progress = $step;
        $icon = DictCoin::getIcon($symbol);
        $balance = UserCoin::where('uid', $uid)->where('symbol', $symbol)->value('balance') ?? 0.0;
        $hours = Config::get('app.mining_time');
        if (count($hours) != 2 || $hours[0] > $hours[1]) {
            Log::warning('app.mining_time config error.');
            return app('json')->fail('启动失败，请联系客服');;
        }
        $hour = random_int($hours[0], $hours[1]);
        $stop = $now + $hour * 60 * 60;

        $suc = UserHash::mining_set($uid, [
            'boot' => $now,
            'step' => $step,
            'stop' => $stop,
            'symbol'=> $symbol,
            'icon' => $icon,
            'price' => 0,
            'progress' => $progress,
            'total' => $balance,
            'ts' => $now + 1,
            'order_id' => $orderId,
        ]);

        if ($suc != 0 && $suc != 1) {
            StoreOrderCartInfo::setMining($orderId, 0);
            return app('json')->fail('未成功执行');
        }

        return app('json')->successful([
            'symbol' => $symbol,
            'icon' => $icon,
            'price' => 0,
            'step' => $progress,
            'progress' => $progress,
            'total' => $balance,
        ]);
    }

    /**
     * 请求转账记录
     */
    public function history(Request $request) {
        [$page, $limit] = UtilService::getMore([
            ['page', 1],
            ['limit', 20],
        ], $request, true);
        $uid = $request->uid();
        $rows = UserCoinTransfer::getUserTransferred($uid, $page, $limit);
        return app('json')->successful($rows);
    }

    /*
     * 更新钱包地址
     */
    public function updateAddr(Request $request) {
        list($symbol, $addr) = UtilService::postMore([
            ['symbol', ''],
            ['addr', ''],
        ], $request, true);
        if (!$symbol || !$addr) {
            return app('json')->fail('参数不可为空');
        }
        if (!DictCoin::where('symbol', $symbol)->select()->toArray()) {
            return app('json')->fail('未找到目标');
        }

        $uid = $request->uid();
        $suc = UserCoin::upsertAddr($uid, $symbol, $addr);
        if (!$suc) {
            return app('json')->fail('执行失败');
        }
        return app('json')->successful();
    }

    /*
     * 提现
     */
    public function transfer(Request $request) {
        list($symbol, $amount) = UtilService::postMore([
            ['symbol', ''],
            ['amount', 0.0]
        ], $request, true);

        if(!$symbol) {
            return app('json')->fail('参数不可为空');
        }
        $meta = DictCoin::where('symbol', $symbol)->find();
        if (!$meta) {
            return app('json')->fail('未找到目标');
        }
        $meta = $meta->toArray();
        if ($amount < $meta['min_withdrawal']){
            return app('json')->fail('未达到最低限额');
        }
        $uid = $request->uid();
        $userCoin = UserCoin::where(['uid'=>$uid, 'symbol'=>$symbol])->find();
        if (!$userCoin) {
            return app('json')->fail('不适用的用户');
        }
        $userCoin = $userCoin->toArray();
        if(!$userCoin['addr']) {
            return app('json')->fail('地址未设置');
        }
        if ($userCoin['balance'] < $amount) {
            return app('json')->fail('余额不足');
        }
        $transferred = UserCoinTransfer::hasTransferred($uid, $symbol, $userCoin['addr']);
        if (!$transferred) {
            return app('json')->fail('首次操作需联系客服进行');
        }
        // transfer
        UserCoin::beginTrans();
        $left = bcsub($userCoin['balance'], $amount, 8);
        $res1 = UserCoin::where(['uid'=>$uid, 'symbol'=>$symbol])->update(['balance'=>$left]);
        $res2 = UserCoinTransfer::withdrawal($uid, $symbol, $userCoin['addr'], $amount);

        $ok = $res1 && $res2;
        UserCoin::checkTrans($ok);
        if (!$ok) {
            return app('json')->fail('执行失败');
        }
        return app('json')->successful('成功,已开始审核');
    }
}