<?php


namespace app\models\tree;


use crmeb\basic\BaseModel;
use crmeb\traits\ModelTrait;
use think\db\exception\DataNotFoundException;
use think\db\exception\DbException;
use think\db\exception\ModelNotFoundException;

class Tree extends BaseModel
{
    /**
     * 数据表主键
     * @var string
     */
    protected $pk = 'id';

    /**
     * 模型名称
     * @var string
     */
    protected $name = 'tree';

    use ModelTrait;

    /**
     * 排点
     * @param int $uid
     * @param int $spread_uid
     * @return array|bool|int[]
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function getTreePoint(int $uid, int $spread_uid = 0)
    {
        if (!self::count()) {
            return [1, 1];
        }
        if (self::where('uid', $uid)->order('add_time', 'asc')->where('status', 0)->find()) {
            return self::setErrorInfo('尚未出局,不可复投');
        }
        $tree_leader = 1;
        $spread_last_point = self::where('uid', $spread_uid)->where('status', 0)->find();
        while ($spread_last_point) {
            $tree_leader = $spread_last_point['tree_num'];
            $spread_last_point = self::where('tree_num', floor($spread_last_point['tree_num'] / 2))->where('status', 0)->find();
        }
        $gp_point = 1;
        $gp_point_list = [$tree_leader * 2, $tree_leader * 2 + 1];
        while (1) {
            $member_list = self::where('tree_num', 'in', $gp_point_list)->order('tree_num', 'asc')->select();
            if (count($member_list) < count($gp_point_list)) {
                foreach ($gp_point_list as $k => $v) {
                    if (!isset($member_list[$k]) || $v != $member_list[$k]['tree_num']) {
                        $gp_point = $v;
                        break;
                    }
                }
                break;
            }
            $gp_point_list = [];
            foreach ($member_list as $v) {
                $gp_point_list[] = $v['tree_num'] * 2;
                $gp_point_list[] = $v['tree_num'] * 2 + 1;
            }
        }
        return [$gp_point, $tree_leader];
    }

    /**
     * 进场
     * @param int $uid
     * @param int $spread_uid
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function insertTree(int $uid, int $spread_uid = 0): bool
    {
        $res = self::getTreePoint($uid, $spread_uid);
        if (!$res) return self::setErrorInfo(self::getErrorInfo());
        list($point, $tree_leader) = $res;
        self::beginTrans();
        try {
            $res = self::create([
                'uid' => $uid,
                'tree_num' => $point,
                'add_time' => time(),
            ]);

            $res = $res && self::sendToUper($point);

            $gp_point_list = [$tree_leader * 2, $tree_leader * 2 + 1];
            $gp_point_all = $gp_point_list;
            $i = 9;
            while (--$i) {
                $set = [];
                foreach ($gp_point_list as $v) {
                    $gp_point_all[] = $v * 2;
                    $gp_point_all[] = $v * 2 + 1;
                    $set[] = $v * 2;
                    $set[] = $v * 2 + 1;
                }
                $gp_point_list = $set;
            }
            if (count($gp_point_all) == self::where('tree_num', 'in', $gp_point_all)->count()) {
                //出局
                $info = self::where('tree_num', $tree_leader)->find();
                $info->status = 1;
                $res = $res && $info->save();
            }
            self::checkTrans($res);
            if ($res) {
                return true;
            } else {
                return self::setErrorInfo('加入失败');
            }
        } catch (\Exception $e) {
            self::rollbackTrans();
            return self::setErrorInfo($e->getMessage());
        }
    }

    /**
     * 发奖
     * @param int $point
     * @return bool
     * @throws DataNotFoundException
     * @throws DbException
     * @throws ModelNotFoundException
     */
    public static function sendToUper(int $point): bool
    {
        $up_point = floor($point / 2);
        $info = self::where('tree_num', $up_point)->find();
        $res = true;
        while ($info) {
            $info->get += 140;
            $res = $res && $info->save();
            $up_point = floor($up_point / 2);
            $info = self::where('tree_num', $up_point)->where('status', 0)->find();
        }
        return $res;
    }
}