<?php
// +----------------------------------------------------------------------
// | CRMEB [ CRMEB赋能开发者,助力企业发展 ]
// +----------------------------------------------------------------------
// | Copyright (c) 2016~2020 https://www.crmeb.com All rights reserved.
// +----------------------------------------------------------------------
// | Licensed CRMEB并不是自由软件,未经许可不能去掉CRMEB相关版权
// +----------------------------------------------------------------------
// | Author: CRMEB Team <admin@crmeb.com>
// +----------------------------------------------------------------------
declare (strict_types=1);

namespace app\services\user;

use app\jobs\user\UserLevelJob;
use app\services\BaseServices;
use app\services\system\SystemSignRewardServices;
use app\services\user\member\MemberCardServices;
use app\dao\user\UserSignDao;
use crmeb\exceptions\ApiException;
use think\exception\ValidateException;

/**
 *
 * Class UserSignServices
 * @package app\services\user
 * @mixin UserSignDao
 */
class UserSignServices extends BaseServices
{

    /**
     * UserSignServices constructor.
     * @param UserSignDao $dao
     */
    public function __construct(UserSignDao $dao)
    {
        $this->dao = $dao;
    }

    /**
     * 返回签到列表数据
     * @param $uid
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function signConfig($uid)
    {
        //获取周签到还是月签到
        $signMode = (int)sys_config('sign_mode', 1);

        //获取签到列表
        $startDate = $signMode == 1 ? strtotime('this week Monday') : strtotime('first day of this month midnight');
        $endDate = $signMode == 1 ? strtotime('this week Sunday') : strtotime('last day of this month midnight');
        $dateList = range($startDate, $endDate, 86400);

        $checkSign = $this->getIsSign($uid, 'today');


        $sign_give_point = (int)sys_config('sign_give_point', 0);
        $sign_exp = sys_config('member_func_status', 1) ? sys_config('sign_give_exp', 0) : 0;
        [$signList, $continuousSignDays, $cumulativeSignDays, $nextContinuousDays, $nextCumulativeDays] = $this->signDataFilling($signMode, $uid, $sign_give_point, $sign_exp, $dateList, '');

        //格式化签到数据
//        $signList = array_chunk($signList, 7);

        //获取用户签到提醒状态
        $signRemindStatus = app()->make(UserServices::class)->value(['uid' => $uid], 'sign_remind');

        //是否显示签到提醒开关
        $signRemindSwitch = (int)sys_config('sign_remind', 0);

        //签到功能是否关闭
        $signStatus = (int)sys_config('sign_status', 0);

        $signData['sign_point'] = $sign_give_point;
        $signData['sign_exp'] = $sign_exp;


        return compact('signList', 'continuousSignDays', 'cumulativeSignDays', 'nextContinuousDays', 'nextCumulativeDays', 'signMode', 'checkSign', 'signRemindStatus', 'signRemindSwitch', 'signStatus', 'signData');
    }

    /**
     * 日历数据
     * @param $uid
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getCalendarDate($uid, $time)
    {
        $startDate = strtotime(date('Y-m-01', strtotime($time)));
        $endDate = strtotime(date('Y-m-d', strtotime(date('Y-m-01', strtotime($time)) . ' +1 month -1 day')));
        $dateList = range($startDate, $endDate, 86400);
        $checkSign = $this->getIsSign($uid, 'today');
        $sign_give_point = (int)sys_config('sign_give_point', 0);
        $sign_exp = sys_config('member_func_status', 1) ? sys_config('sign_give_exp', 0) : 0;
        $today = date("Y-m-d"); // 今日日期
        $w = date('w', $startDate); //月初周几
        [$signList, $continuousSignDays, $cumulativeSignDays, $nextContinuousDays, $nextCumulativeDays] = $this->signDataFilling(0, $uid, $sign_give_point, $sign_exp, $dateList, $time);
        return compact('today', 'w', 'signList', 'continuousSignDays', 'cumulativeSignDays', 'nextContinuousDays', 'nextCumulativeDays', 'checkSign');
    }

    /**
     * 整理签到列表数据
     * @param $signMode
     * @param $uid
     * @param $sign_give_point
     * @param $sign_exp
     * @param $dateList
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function signDataFilling($signMode, $uid, $sign_give_point, $sign_exp, $dateList, $time)
    {
        if (!$time) $time = date('Y-m', time());

        //获取已经签到的列表
        $list = $this->dao->getUserSignList($signMode, $uid, $time);

        //获取累积签到和连续签到
        $cumulativeSignDays = $this->dao->getCumulativeDays($uid); //获取累积签到天数
        $continuousSignDays = app()->make(UserServices::class)->value(['uid' => $uid], 'sign_num');//获取连续签到天数

        //获取累积签到和连续签到奖励
        $nextCumulativeSignRewardList = app()->make(SystemSignRewardServices::class)->selectList(['type' => 1], '*', 1, 200, 'days asc')->toArray(); //累计签到奖励
        $nextContinuousSignRewardList = app()->make(SystemSignRewardServices::class)->selectList(['type' => 0], '*', 1, 200, 'days asc')->toArray(); //连续签到奖励

        //下一次连续签到奖励还需签到几天
        $nextContinuousDays = 0;
        foreach ($nextContinuousSignRewardList as $continuousNext) {
            if ($continuousSignDays < $continuousNext['days']) {
                $nextContinuousDays = $continuousNext['days'] - $continuousSignDays > 0 ?: 1;
                break;
            }
        }
        $nextCumulativeDays = 0;
        foreach ($nextCumulativeSignRewardList as $cumulativeNext) {
            if ($cumulativeSignDays < $cumulativeNext['days']) {
                $nextCumulativeDays = $cumulativeNext['days'] - $cumulativeSignDays > 0 ?: 1;
                break;
            }
        }
        //整理签到列表数据
        $signList = [];
        $i = 0;

        foreach ($dateList as $key => $time) {
            $day = date('m/d', $time);
            if ($day[0] == '0') $day = substr($day, 1);
            $signList[$key]['day'] = $day;
            $signList[$key]['is_sign'] = false;

            //判断当前签到日期
            $signList[$key]['sign_day'] = date('Y-m-d', $time) == date('Y-m-d', time());

            //判断今日是否签到
            foreach ($list as $value) {
                if (date('Y-m-d', $time) == date('Y-m-d', $value['add_time'])) {
                    $signList[$key]['is_sign'] = true;
                    break;
                }
            }

            //处理处理签到类型展示,type 1积分,2经验
            $signList[$key]['type'] = $sign_give_point == 0 && sys_config('member_func_status', 1) == 1 && $sign_exp > 0 ? 2 : 1;
            $signList[$key]['point'] = $sign_give_point;
            $signList[$key]['days'] = 0; //连续、累计 奖励天数
            $signList[$key]['sign_type'] = 0; // 3连续签到奖励,4累计签到奖励
            if (date('Y-m-d', $time) >= date('Y-m-d', time())) {
                // 连续签到奖励
                foreach ($nextContinuousSignRewardList as $continuous) {
                    if (($continuous['days'] - $continuousSignDays) == $i) {
                        $signList[$key]['sign_type'] = 3;
                        $signList[$key]['days'] = $continuous['days'];
                        $signList[$key]['point'] = $continuous['point'];
                    }
                }
                // 累计签到奖励
                foreach ($nextCumulativeSignRewardList as $cumulative) {
                    if (($cumulative['days'] - $cumulativeSignDays) == $i) {
                        $signList[$key]['sign_type'] = 4;
                        $signList[$key]['days'] = $cumulative['days'];
                        $signList[$key]['point'] = $cumulative['point'];
                    }
                }
                $i++;
            }
        }
        return [$signList, $continuousSignDays, $cumulativeSignDays, $nextContinuousDays, $nextCumulativeDays];
    }

    /**
     * 获取用户是否签到
     * @param $uid
     * @return bool
     */
    public function getIsSign(int $uid, string $type = 'today')
    {
        return (bool)$this->dao->count(['uid' => $uid, 'time' => $type]);
    }

    /**
     * 获取用户累计签到次数
     * @Parma int $uid 用户id
     * @return int
     * */
    public function getSignSumDay(int $uid)
    {
        return $this->dao->count(['uid' => $uid]);
    }

    /**
     * 设置签到数据
     * @param int $uid 用户uid
     * @param string $title 签到说明
     * @param int $number 签到获得积分
     * @param int $integral_balance
     * @param int $exp_banlance
     * @param int $exp_num
     * @return bool
     * @throws \think\Exception
     */
    public function setSignData($uid, $title = '', $number = 0, $integral_balance = 0, $exp_banlance = 0, $exp_num = 0)
    {
        $data = [];
        $data['uid'] = $uid;
        $data['title'] = $title;
        $data['number'] = $number;
        $data['balance'] = $integral_balance;
        $data['add_time'] = time();
        if (!$this->dao->save($data)) {
            throw new ValidateException('添加签到数据失败');
        }
        /** @var UserBillServices $userBill */
        $userBill = app()->make(UserBillServices::class);
        $data['mark'] = $title;
        $userBill->incomeIntegral($uid, 'sign', $data);

        if ($exp_num) {
            $data['number'] = $exp_num;
            $data['category'] = 'exp';
            $data['type'] = 'sign';
            $data['title'] = $data['mark'] = '签到奖励';
            $data['balance'] = $exp_banlance;
            $data['pm'] = 1;
            $data['status'] = 1;
            if (!$userBill->save($data)) {
                throw new ValidateException('赠送经验失败');
            }
            //检测会员等级
            UserLevelJob::dispatch([$uid]);
        }

        return true;
    }

    /**
     * 获取用户签到列表
     * @param int $uid
     * @param string $field
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function getUserSignList(int $uid, string $field = '*')
    {
        [$page, $limit] = $this->getPageValue();
        $list = $this->dao->getList(['uid' => $uid], $field, $page, $limit);
        foreach ($list as &$item) {
            $item['add_time'] = $item['add_time'] ? date('Y-m-d', $item['add_time']) : '';
        }
        return $list;
    }

    /**
     * 用户签到
     * @param $uid
     * @return bool|int|mixed
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function sign(int $uid)
    {
        /** @var UserServices $userServices */
        $userServices = app()->make(UserServices::class);

        //检测用户是否存在
        $user = $userServices->getUserInfo($uid);
        if (!$user) {
            throw new ApiException('用户不存在');
        }

        //检测今天是否已经签到
        if ($this->getIsSign($uid, 'today')) {
            throw new ApiException('今日已签到');
        }
        $title = '签到奖励';
        //检测昨天是否签到,如果没有签到,连续签到清0
        if (!$this->getIsSign($uid, 'yesterday')) $user->sign_num = 0;

        //获取签到周期配置,如果周签到,每周一清空连续签到记录,如果月签到,每月一日清空连续签到记录
        $sign_mode = sys_config('sign_mode', -1);
        if ($sign_mode == 1 && date('w') == 1) $user->sign_num = 0;
        if ($sign_mode == 0 && date('d') == 1) $user->sign_num = 0;

        //连续签到天数
        $user->sign_num += 1;
        $continuousDays = $user->sign_num;
        //累积签到天数
        $cumulativeDays = $this->dao->getCumulativeDays($uid);

        //基础签到奖励
        $sign_point = sys_config('sign_give_point', 0);
        $sign_exp = sys_config('member_func_status', 1) ? sys_config('sign_give_exp', 0) : 0;

        //连续签到和累积签到奖励
        $signRewardsServices = app()->make(SystemSignRewardServices::class);
        [$continuousStatus, $continuousRewardPoint, $continuousRewardExp] = $signRewardsServices->getSignRewards(0, $continuousDays);
        [$cumulativeStatus, $cumulativeRewardPoint, $cumulativeRewardExp] = $signRewardsServices->getSignRewards(1, $cumulativeDays);
        if ($continuousStatus && $cumulativeStatus) {
            $sign_point = $continuousRewardPoint + $cumulativeRewardPoint;
            $sign_exp = $continuousRewardExp + $cumulativeRewardExp;
        } elseif ($continuousStatus) {
            $sign_point = $continuousRewardPoint;
            $sign_exp = $continuousRewardExp;
        } elseif ($cumulativeStatus) {
            $sign_point = $cumulativeRewardPoint;
            $sign_exp = $cumulativeRewardExp;
        }

        //会员签到积分会员奖励
        if ($user->is_money_level > 0) {
            //看是否开启签到积分翻倍奖励
            /** @var MemberCardServices $memberCardService */
            $memberCardService = app()->make(MemberCardServices::class);
            $sign_rule_number = $memberCardService->isOpenMemberCard('sign');
            if ($sign_rule_number) {
                $up_num = (int)$sign_rule_number * $sign_point - $sign_point;
                $sign_point = (int)$sign_rule_number * $sign_point;
                if (!$this->getIsSign($uid, 'yesterday')) $title = '签到奖励(SVIP+' . $up_num . ')';
            }
        }

        //增加签到数据
        $this->transaction(function () use ($uid, $title, $sign_point, $user, $sign_exp) {
            $this->setSignData($uid, $title, $sign_point, $user['integral'], (int)$user['exp'], $sign_exp);
            if ($sign_point)  $user->integral = (int)$user->integral + (int)$sign_point;
            if ($sign_exp && $user['level_status']) {//有经验 && 用户等级已经激活
                $user->exp = (int)$user->exp + (int)$sign_exp;
            }
            if (!$user->save()) {
                throw new ApiException('修改用户数据失败');
            }
        });
        return compact('sign_point','sign_exp');
    }

    /**
     * 签到用户信息
     * @param int $uid
     * @param $sign
     * @param $integral
     * @param $all
     * @return mixed
     */
    public function signUser(int $uid, $sign, $integral, $all)
    {
        /** @var UserServices $userServices */
        $userServices = app()->make(UserServices::class);
        $user = $userServices->getUserCacheInfo($uid);
        if (!$user) {
            throw new ValidateException('数据不存在');
        }
        $data = ['uid' => $user['uid'], 'nickname' => $user['nickname'], 'phone' => $user['phone'], 'now_money' => $user['now_money'], 'integral' => $user['integral'], 'is_promoter' => $user['is_promoter'] ?? 0];
        //是否统计签到
        if ($sign || $all) {
            $data['sum_sgin_day'] = $this->getSignSumDay($uid);
            $data['is_day_sgin'] = $this->getIsSign($uid);
            $data['is_YesterDay_sgin'] = $this->getIsSign($uid, 'yesterday');
            if (!$data['is_day_sgin'] && !$data['is_YesterDay_sgin']) {
                $data['sign_num'] = 0;
            }
        }
        /** @var UserIntegralServices $userIntegralServices */
        $userIntegralServices = app()->make(UserIntegralServices::class);
        [$clear_integral, $clear_time] = $userIntegralServices->getUserClearIntegral($uid, $user);
        $data['clear_integral'] = $clear_integral;
        $data['clear_time'] = $clear_time;
        //是否统计积分使用情况
        if ($integral || $all) {
            /** @var UserBillServices $userBill */
            $userBill = app()->make(UserBillServices::class);
            $data['sum_integral'] = intval($userBill->getRecordCount($uid, 'integral', [], '', true));
            $data['deduction_integral'] = intval($userBill->getRecordCount($uid, 'integral') ?? 0);
            $data['today_integral'] = intval($userBill->getRecordCount($uid, 'integral', [], 'today', true));

            $data['frozen_integral'] = $userBill->getBillSum(['uid' => $uid, 'is_frozen' => 1]);
            $data['integral_effective_status'] = (int)sys_config('integral_effective_status');
            $data['clear_end'] = '';
            if ($data['integral_effective_status']) {
                [$clear_end, $start, $end] = $userIntegralServices->getTime();
                $data['clear_end'] = date('Y-m-d', $clear_end);
            }
        }
        if (!$user['is_promoter']) {
            $data['is_promoter'] = (int)sys_config('store_brokerage_statu') == 2;
        }
        return $data;
    }


    /**
     * 获取签到
     * @param $uid
     * @return array
     */
    public function getSignMonthList($uid)
    {
        [$page, $limit] = $this->getPageValue();
        $data = $this->dao->getListGroup(['uid' => $uid], 'FROM_UNIXTIME(add_time,"%Y-%m") as time,group_concat(id SEPARATOR ",") ids', $page, $limit, 'time');
        $list = [];
        if ($data) {
            $ids = array_unique(array_column($data, 'ids'));
            $dataIdsList = $this->dao->getList(['id' => $ids], 'FROM_UNIXTIME(add_time,"%Y-%m-%d") as add_time,title,number,exp_num,id,uid', 0, 0);
            foreach ($data as $item) {
                $value['month'] = $item['time'];
                $value['list'] = array_merge(array_filter($dataIdsList, function ($val) use ($item) {
                    if (in_array($val['id'], explode(',', $item['ids']))) {
                        return $val;
                    }
                }));
                array_push($list, $value);
            }
        }
        return $list;
    }

    /**
     * 签到提醒设置
     * @param $uid
     * @param $status
     * @return bool
     */
    public function setSignRemind($uid, $status)
    {
        app()->make(UserServices::class)->update($uid, ['sign_remind' => $status]);
        return true;
    }

    /**
     * 用户签到提醒
     * @return bool
     */
    public function userSignRemind()
    {
        /** @var  UserServices $userService */
        $userService = app()->make(UserServices::class);
        $where = ['is_del' => 0, 'status' => 1, 'sign_remind' => 1];
        $userList = $userService->getColumn($where, 'uid,phone,sign_remind', 'uid');
        foreach ($userList as $key => $item) {
            if (!$this->getIsSign($item['uid'], 'today')) {
                event('notice.notice', [$item, 'sign_remind_time']);
            }
        }
        return true;
    }

    /**
     * 首页diy签到数据
     * @param int $uid
     * @return array
     * @throws \think\db\exception\DataNotFoundException
     * @throws \think\db\exception\DbException
     * @throws \think\db\exception\ModelNotFoundException
     */
    public function homeDiysignData(int $uid = 0)
    {
        //获取签到列表
        $startDate = strtotime('this week Monday');
        $endDate = strtotime('this week Sunday');
        $dateList = range($startDate, $endDate, 86400);

        $checkSign = $this->getIsSign($uid, 'today');

        $sign_give_point = (int)sys_config('sign_give_point', 0);
        $sign_exp = sys_config('member_func_status', 1) ? sys_config('sign_give_exp', 0) : 0;

        //获取连续签到奖励
        $nextContinuousSignRewardList = app()->make(SystemSignRewardServices::class)->selectList(['type' => 0], '*', 1, 1, 'days asc')->toArray(); //连续签到奖励
        $list = [];

        if ($uid) {
            //获取已经签到的列表
            $list = $this->dao->getUserSignList(1, $uid, '');
        }
        //整理签到列表数据
        $signList = [];

        foreach ($dateList as $key => $time) {
            $day = date('m/d', $time);
            if ($day[0] == '0') $day = substr($day, 1);
            $signList[$key]['day'] = $day;
            $signList[$key]['is_sign'] = false;

            //判断当前签到日期
            $signList[$key]['sign_day'] = date('Y-m-d', $time) == date('Y-m-d', time());
            if ($list) {
                //判断今日是否签到
                foreach ($list as $value) {
                    if (date('Y-m-d', $time) == date('Y-m-d', $value['add_time'])) {
                        $signList[$key]['is_sign'] = true;
                        break;
                    }
                }
            }
            //处理处理签到类型展示,type 1积分,2经验
            $signList[$key]['type'] = $sign_give_point == 0 && sys_config('member_func_status', 1) == 1 && $sign_exp > 0 ? 2 : 1;
            $signList[$key]['point'] = $sign_give_point;
        }

        //格式化签到数据
        $signList = array_chunk($signList, 7);

        //签到功能是否关闭
        $signStatus = (int)sys_config('sign_status', 0);

        return compact('signList', 'nextContinuousSignRewardList', 'checkSign', 'signStatus', 'sign_give_point');
    }
}