<?php

namespace Base\Repository;

class UserRepository
{

    public function __construct()
    {

    }

    private function assembleRecords($items, $keys, $valueColumn, $keyColumn = 'day')
    {
        $records = [];
        foreach ($keys as $key) {
            $value = 0;
            foreach ($items as $item) {
                if ($item[$keyColumn] == $key) {
                    $value = $item[$valueColumn];
                }
            }
            $records[$key] = $value;
        }
        return $records;
    }

    private function setKeys($params, $alias)
    {
        $records = [];
        foreach ($params as $key => $value) {
            $records[$alias . '.' . $key] = $value;
        }
        return $records;
    }

    private function getDayGroupConditions($params)
    {
        $beginTime = $params['begin_time'] ?? 0;
        $endTime = $params['end_time'] ?? 0;
        $gameId = $params['game_id'] ?? 0;
        $ids = $params['promote_id'] ?? [];

        $conditions = [];
        $conditions['promote_id'] = ['in', $ids];
        $conditions[$params['time_column']] = ['between', [$beginTime, $endTime]];
        if (is_array($gameId)) {
            if (count($gameId)) {
                $conditions['game_id'] = ['in', $gameId];
            }
        } else {
            if ($gameId > 0) {
                $conditions['game_id'] = $gameId;
            }
        }
        $conditions['pay_way'] = $isBan ? ['neq', '-10'] : ['neq', '-1'];

        return $conditions;
    }

    private function getGameGroupConditions($params)
    {
        $beginTime = $params['begin_time'] ?? 0;
        $endTime = $params['end_time'] ?? 0;
        $ids = $params['promote_ids'] ?? [];
        $gameIds = $params['game_ids'] ?? [];

        $conditions = [];
        $conditions['promote_id'] = ['in', $ids];
        if (!empty($gameIds)) {
            $conditions['game_id'] = ['in', $gameIds];
        }
        $conditions[$params['time_column']] = ['between', [$beginTime, $endTime]];

        return $conditions;
    }

    private function getCreateRoleConditions($params)
    {
        $beginTime = $params['begin_time'] ?? 0;
        $endTime = $params['end_time'] ?? 0;
        $gameId = $params['game_id'] ?? 0;
        $serverId = $params['server_id'] ?? 0;
        $ids = $params['promote_id'] ?? [];

        $conditions = [];
        $conditions['promote_id'] = ['in', $ids];
        $conditions[$params['time_column']] = ['between', [$beginTime, $endTime]];
        if ($gameId > 0) {
            $conditions['game_id'] = $gameId;
        }
        if ($serverId > 0) {
            $conditions['server_id'] = $serverId;
        }

        return $conditions;
    }

    private function getCreateRoleByGameConditions($params)
    {
        $beginTime = $params['begin_time'] ?? 0;
        $endTime = $params['end_time'] ?? 0;
        $ids = $params['promote_ids'] ?? [];
        $gameIds = $params['game_ids'] ?? [];
        $serverId = $params['server_id'] ?? 0;

        $conditions = [];
        $conditions['promote_id'] = ['in', $ids];
        $conditions['game_id'] = ['in', $gameIds];
        if ($serverId > 0) {
            $conditions['server_id'] = $serverId;
        }
        $conditions[$params['time_column']] = ['between', [$beginTime, $endTime]];

        return $conditions;
    }

    /**
     * 按照时间分组统计登录总数
     */
    public function getLoginCountGroupByDay($params)
    {
        $dayList = $params['dayList'] ?? [];
        $params['time_column'] = 'create_time';
        $conditions = $this->getDayGroupConditions($params);
        $items = M('login_daily_record', 'tab_')->field('FROM_UNIXTIME(create_time, "%Y-%m-%d") as day, count(DISTINCT user_id) as count')
            ->where($conditions)
            ->group('day')
            ->select();

        return $this->assembleRecords($items, $dayList, 'count');
    }

    /**
     * 按照时间分组统计登录总数
     */
    public function getLoginCountGroupByDayNew($params)
    {
        $dayList = $params['dayList'] ?? [];
        $params['time_column'] = 'create_time';
        $conditions = $this->getDayGroupConditions($params);
        $subQuery = M('login_daily_record', 'tab_')->field('FROM_UNIXTIME(create_time, "%Y-%m-%d") as day,count(DISTINCT user_id) as count')
            ->where($conditions)
            ->group('day,game_id')
            ->buildSql();
        $items = M()->field('*,sum(count) as count')
            ->table($subQuery)
            ->alias('a')
            ->group('day')
            ->select();

        return $this->assembleRecords($items, $dayList, 'count');
    }

    /**
     * 按照游戏分组统计登录总数
     */
    public function getLoginCountGroupByGame($params)
    {
        $gameIds = $params['game_ids'] ?? [];
        $params['time_column'] = 'create_time';
        $conditions = $this->getGameGroupConditions($params);
        if (isset($params['all_data'])) {
            $sql = M('login_daily_record', 'tab_')->field('distinct game_id, user_id')
                ->where($conditions)
                ->fetchSql(true)
                ->select();
            $model = new \Think\Model();
            return $model->query("select count(*) as num from ($sql) as t")[0]['num'];
        } else {
            $items = M('login_daily_record', 'tab_')->field('game_id, count(DISTINCT user_id) as count')
                ->where($conditions)
                ->group('game_id')
                ->select();

            return $this->assembleRecords($items, $gameIds, 'count', 'game_id');
        }
    }

    /**
     * 按照时间分组统计注册总数
     */
    public function getRegisterCountGroupByDay($params)
    {
        $dayList = $params['dayList'] ?? [];
        $params['time_column'] = 'register_time';
        $conditions = $this->getDayGroupConditions($params);

        /* if (isset($conditions['game_id'])) {
            $conditions['fgame_id'] = $conditions['game_id'];
            unset($conditions['game_id']);
        } */

        $items = M('user', 'tab_')->field('count(*) count, FROM_UNIXTIME(register_time, "%Y-%m-%d") as day')
            ->where($conditions)
            ->group('day')
            ->select();
        return $this->assembleRecords($items, $dayList, 'count');
    }

    /**
     * 按照游戏分分组统计注册总数
     */
    public function getRegisterCountGroupByGame($params)
    {
        $gameIds = $params['game_ids'] ?? [];
        $params['time_column'] = 'register_time';
        $conditions = $this->getGameGroupConditions($params);

        $items = M('user', 'tab_')->field('count(*) count, fgame_id')
            ->where($conditions)
            ->group('fgame_id')
            ->select();
        return $this->assembleRecords($items, $gameIds, 'count', 'fgame_id');
    }

    /**
     * 按照时间分组统计注册编号序列
     * @param string $newslist 新玩家序列
     * @param integer $end 结束时间(时间戳)
     * @param integer $game_id 游戏编号
     * @param integer/string    $promote_id     渠道编号或渠道编号列表字符串(字符串逗号分隔)
     * @param integer $flag 留存类型
     * @return array            详细数据
     * @author 鹿文学
     */
    public function getRatentionRate($newslist, $game_id = 0, $promote_id = 0, $flag = 1)
    {

        $map['lock_status'] = 1;
        if ($game_id > 0) {
            $map['up.game_id'] = $game_id;
        }
        $map['tab_user.promote_id'] = is_numeric($promote_id) ? $promote_id : array('in', $promote_id);

        $group = 'up.login_time';

        $fieldname = 'retention_rate' . $flag;

        foreach ($newslist as $value) {
            $ct1 = strtotime("+$flag day", strtotime($value['time']));
            $ct2 = strtotime("+1 day", $ct1) - 1;

            $map[$group] = array(array('egt', $ct1), array('elt', $ct2));

            $map['user_id'] = array('in', $value['id']);
            $count = count(explode(',', $value['id']));


            $d = $this
                ->field('count(distinct up.user_id) as ' . $fieldname . ' ,FROM_UNIXTIME(up.login_time,"%Y-%m-%d") as play_time')
                ->join('tab_user_login_record up on tab_user.id=up.user_id', 'right')
                ->where($map)
                ->group('play_time')
                ->select();

            if ($d)
                $data[] = array(
                    "play_time" => $value['time'],
                    $fieldname => ($d[0][$fieldname] == 0) ? 0 : sprintf("%.2f", ($d[0][$fieldname] / $count) * 100)
                );
        }
        return $data;
    }

    /**
     * 按照时间统计创角数
     */
    public function getCreateRoleCountByDay($params)
    {
        $dayList = $params['dayList'] ?? [];
        $params['time_column'] = 'create_time';
        $conditions = $this->getCreateRoleConditions($params);

        $items = M('user_play_info', 'tab_')->field('count(*) count, FROM_UNIXTIME(create_time, "%Y-%m-%d") as day')
            ->where($conditions)
            ->group('day')
            ->select();
        return $this->assembleRecords($items, $dayList, 'count');
    }

    /**
     * 按照游戏统计创角数
     */
    public function getCreateRoleCountByGame($params)
    {
        $gameIds = $params['game_ids'] ?? [];
        $params['time_column'] = 'create_time';
        $conditions = $this->getCreateRoleByGameConditions($params);

        if (isset($params['all_data'])) {
            return M('user_play_info', 'tab_')
                ->where($conditions)
                ->count();
        } else {
            $items = M('user_play_info', 'tab_')->field('count(*) count, game_id')
                ->where($conditions)
                ->group('game_id')
                ->select();
            return $this->assembleRecords($items, $gameIds, 'count', 'game_id');
        }
    }

    /**
     * 按照时间统计创角用户(去重)
     */
    public function getCreateRoleUserCountByDay($params)
    {
        $dayList = $params['dayList'] ?? [];
        $params['time_column'] = 'create_time';
        $conditions = $this->getCreateRoleConditions($params);

        $subQuery = M('user_play_info', 'tab_')->field('FROM_UNIXTIME(create_time, "%Y-%m-%d") as day,count(DISTINCT user_id) as count')
            ->where($conditions)
            ->group('day,game_id')
            ->buildSql();
        $items = M()->field('*,sum(count) as count')
            ->table($subQuery)
            ->alias('a')
            ->group('day')
            ->select();
        return $this->assembleRecords($items, $dayList, 'count');
    }

    /**
     * 按照游戏统计创角用户(去重)
     */
    public function getCreateRoleUserCountByGame($params)
    {
        $gameIds = $params['game_ids'] ?? [];
        $params['time_column'] = 'create_time';
        $conditions = $this->getCreateRoleByGameConditions($params);

        if (isset($params['all_data'])) {
            $sql = M('user_play_info', 'tab_')->field('distinct game_id, user_id')
                ->where($conditions)
                ->fetchSql(true)
                ->select();
            $model = new \Think\Model();
            return $model->query("select count(*) as num from ($sql) as t")[0]['num'];
        } else {
            $items = M('user_play_info', 'tab_')->field('count(distinct user_id) count, game_id')
                ->where($conditions)
                ->group('game_id')
                ->select();
            return $this->assembleRecords($items, $gameIds, 'count', 'game_id');
        }
    }

    /**
     * 按照时间统计新创角用户(去重)
     */
    public function getNewCreateRoleUserCountByDay($params)
    {
        $dayList = $params['dayList'] ?? [];
        $params['time_column'] = 'create_time';
        $conditions = $this->getCreateRoleConditions($params);
        $conditionsSql = $conditions;
        unset($conditionsSql['begin_time']);
        unset($conditionsSql['end_time']);
        $alias = 'ti';
        $conditionsSql = $this->setKeys($conditionsSql, $alias);
        $model = M('user_play_info', 'tab_');

        $sql = $model->alias($alias)
            ->where($conditionsSql)
            ->where("ti.user_id = user_id and ti.game_id = game_id and ti.create_time < " . $params['begin_time'])
            ->fetchSql(true)
            ->count();
        $subQuery = $model->field('FROM_UNIXTIME(create_time, "%Y-%m-%d") as day,count(DISTINCT user_id) as count')
            ->where($conditions)
            ->group('day,game_id')
            ->buildSql();
        $items = M()->field("*,sum(count) as count,(" . $sql . ") as num")
            ->table($subQuery)
            ->alias('a')
            ->group('day')
            ->having('num = 0')
            ->select();
        return $this->assembleRecords($items, $dayList, 'count');
    }

    /**
     * 按照游戏统计新创角用户(去重)
     */
    public function getNewCreateRoleUserCountByGame($params)
    {
        $gameIds = $params['game_ids'] ?? [];
        $params['time_column'] = 'create_time';
        $conditions = $this->getCreateRoleByGameConditions($params);
        $conditionsSql = $conditions;
        unset($conditionsSql['begin_time']);
        unset($conditionsSql['end_time']);
        $alias = 'ti';
        $conditionsSql = $this->setKeys($conditionsSql, $alias);
        $model = M('user_play_info', 'tab_');

        $sql = $model->alias($alias)
            ->where($conditionsSql)
            ->where("ti.user_id = user_id and ti.game_id = game_id and ti.create_time < " . $params['begin_time'])
            ->fetchSql(true)
            ->count();
        if (isset($params['all_data'])) {
            return count($model->field("game_id, (" . $sql . ") as num")
                ->where($conditions)
                ->group('game_id, user_id')
                ->having('num = 0')
                ->select());
        } else {
            $items = $model->field("count(distinct user_id) count, game_id, (" . $sql . ") as num")
                ->where($conditions)
                ->group("game_id")
                ->having('num = 0')
                ->select();
            return $this->assembleRecords($items, $gameIds, 'count', 'game_id');
        }
    }

    /**
     * 按照时间统计新创角设备(去重)
     */
    public function getNewCreateRoleDeviceCountByDay($params)
    {
        $dayList = $params['dayList'] ?? [];
        $params['time_column'] = 'create_time';
        $conditions = $this->getCreateRoleConditions($params);
        $conditionsSql = $conditions;
        unset($conditionsSql['begin_time']);
        unset($conditionsSql['end_time']);
        $alias = 'ti';
        $conditionsSql = $this->setKeys($conditionsSql, $alias);
        $model = M('user_play_info', 'tab_');

        $sql = $model->alias($alias)
            ->where($conditionsSql)
            ->where("ti.create_device_number = create_device_number and ti.game_id = game_id and ti.create_time < " . $params['begin_time'])
            ->fetchSql(true)
            ->count();
        $subQuery = $model->field('FROM_UNIXTIME(create_time, "%Y-%m-%d") as day,count(DISTINCT create_device_number) as count')
            ->where($conditions)
            ->group('day,game_id')
            ->buildSql();
        $items = M()->field("*,sum(count) as count,(" . $sql . ") as num")
            ->table($subQuery)
            ->alias('a')
            ->group('day')
            ->having('num = 0')
            ->select();
        return $this->assembleRecords($items, $dayList, 'count');
    }

    /**
     * 按照游戏统计新创角设备(去重)
     */
    public function getNewCreateRoleDeviceCountByGame($params)
    {
        $gameIds = $params['game_ids'] ?? [];
        $params['time_column'] = 'create_time';
        $conditions = $this->getCreateRoleByGameConditions($params);
        $conditionsSql = $conditions;
        unset($conditionsSql['begin_time']);
        unset($conditionsSql['end_time']);
        $alias = 'ti';
        $conditionsSql = $this->setKeys($conditionsSql, $alias);
        $model = M('user_play_info', 'tab_');

        $sql = $model->alias($alias)
            ->where($conditionsSql)
            ->where("ti.create_device_number = create_device_number and ti.game_id = game_id and ti.create_time < " . $params['begin_time'])
            ->fetchSql(true)
            ->count();
        if (isset($params['all_data'])) {
            return count($model->field("game_id, (" . $sql . ") as num")
                ->where($conditions)
                ->group('game_id, create_device_number')
                ->having('num = 0')
                ->select());
        } else {
            $items = $model->field("count(distinct game_id,create_device_number) count, game_id, (" . $sql . ") as num")
                ->where($conditions)
                ->group("game_id")
                ->having('num = 0')
                ->select();
            return $this->assembleRecords($items, $gameIds, 'count', 'game_id');
        }
    }

    /**
     * 按照时间统计新创角IP(去重)
     */
    public function getNewCreateRoleIpCountByDay($params)
    {
        $dayList = $params['dayList'] ?? [];
        $params['time_column'] = 'create_time';
        $conditions = $this->getCreateRoleConditions($params);
        $conditionsSql = $conditions;
        unset($conditionsSql['begin_time']);
        unset($conditionsSql['end_time']);
        $alias = 'ti';
        $conditionsSql = $this->setKeys($conditionsSql, $alias);
        $model = M('user_play_info', 'tab_');

        $sql = $model->alias($alias)
            ->where($conditionsSql)
            ->where("ti.create_ip = create_ip and ti.game_id = game_id and ti.create_time < " . $params['begin_time'])
            ->fetchSql(true)
            ->count();
        $subQuery = $model->field('FROM_UNIXTIME(create_time, "%Y-%m-%d") as day,count(DISTINCT create_ip) as count')
            ->where($conditions)
            ->group('day,game_id')
            ->buildSql();
        $items = M()->field("*,sum(count) as count,(" . $sql . ") as num")
            ->table($subQuery)
            ->alias('a')
            ->group('day')
            ->having('num = 0')
            ->select();
        return $this->assembleRecords($items, $dayList, 'count');
    }

    /**
     * 按照游戏统计新创角IP(去重)
     */
    public function getNewCreateRoleIpCountByGame($params)
    {
        $gameIds = $params['game_ids'] ?? [];
        $params['time_column'] = 'create_time';
        $conditions = $this->getCreateRoleByGameConditions($params);
        $conditionsSql = $conditions;
        unset($conditionsSql['begin_time']);
        unset($conditionsSql['end_time']);
        $alias = 'ti';
        $conditionsSql = $this->setKeys($conditionsSql, $alias);
        $model = M('user_play_info', 'tab_');

        $sql = $model->alias($alias)
            ->where($conditionsSql)
            ->where("ti.create_ip = create_ip and ti.game_id = game_id and ti.create_time < " . $params['begin_time'])
            ->fetchSql(true)
            ->count();
        if (isset($params['all_data'])) {
            return count($model->field("game_id, (" . $sql . ") as num")
                ->where($conditions)
                ->group('game_id, create_ip')
                ->having('num = 0')
                ->select());
        } else {
            $items = $model->field("count(distinct game_id,create_ip) count, game_id, (" . $sql . ") as num")
                ->where($conditions)
                ->group("game_id")
                ->having('num = 0')
                ->select();
            return $this->assembleRecords($items, $gameIds, 'count', 'game_id');
        }
    }

    public function getActiveUserCountRecently($dayCount = 1, $params = [])
    {
        $conditions = [];
        $endTime = strtotime(date('Y-m-d 23:59:59'));
        $beginTime = $endTime + 1 - $dayCount * 24 * 3600;
        $conditions = ['login_time' => ['between', [$beginTime, $endTime]]];
        $item = M('user_login_record', 'tab_')->field('count(DISTINCT user_id) as count')
            ->where($conditions)
            ->find();
        return $item['count'];  
    }

    public function getUserRetention($baseGameId, $dateRange, $deviceType, $promote = null, $dayList = [60, 90])
    {
        $begin = $dateRange[0];
        $end = $dateRange[1];

        $repository = new GameRepository();
        $baseGame = M('base_game', 'tab_')->where(['id' => $baseGameId])->find();

        $beginDate = strtotime($begin);
        $endDate = strtotime($end);
        $dayTime = 24 * 3600;

        $records = [];
        for ($date = $beginDate; $date <= $endDate; $date = $date + $dayTime) {
            $dateStr = date('Y-m-d', $date);
            $userRegisterCount = $repository->getUserRegisterCount($baseGame, $dateStr, $deviceType, $promote);

            $retentionRates = [];
            foreach ($dayList as $day) {
                $retentionCount = $repository->getUserRetentionCount($baseGame, $dateStr, $day, $deviceType, $promote);
                $retentionRates[$day] = $userRegisterCount == 0 ? '--' : round($retentionCount / $userRegisterCount * 100, 2);
            }
            $records[$dateStr] = [
                'date' => $dateStr,
                'register_count' => $userRegisterCount,
                'retention_rate' => $retentionRates,
            ];
        }
        return $records;
    }
}