<?php
// +----------------------------------------------------------------------
// | Niucloud-admin 企业快速开发的saas管理平台
// +----------------------------------------------------------------------
// | 官方网址:https://www.niucloud.com
// +----------------------------------------------------------------------
// | niucloud团队 版权所有 开源版本可自由商用
// +----------------------------------------------------------------------
// | Author: Niucloud Team
// +----------------------------------------------------------------------

namespace app\service\api\member;

use app\job\member\MemberGiftGrantJob;
use app\model\member\MemberSign;
use app\service\core\member\CoreMemberService;
use app\service\core\sys\CoreConfigService;
use core\base\BaseApiService;
use core\exception\CommonException;
use think\db\exception\DbException;
use think\facade\Db;
use DateInterval;
use DateTime;
use DatePeriod;

/**
 * 会员签到服务层
 * Class BaseService
 * @package app\service
 */
class MemberSignService extends BaseApiService
{

    public function __construct()
    {
        parent::__construct();
        $this->model = new MemberSign();
    }

    /**
     * 会员签到记录
     * @param array $where
     * @return array
     */
    public function getPage(array $where = [])
    {
        $where['member_id'] = $this->member_id;
        $where['site_id'] = $this->site_id;
        $field = 'sign_id, site_id, member_id, days, day_award, continue_award, continue_tag, create_time, is_sign';
        $search_model = $this->model->where($where)->field($field)->append(['is_sign_name'])->order('create_time desc');
        return $this->pageQuery($search_model);
    }

    /**
     * 会员签到详情
     * @param int $sign_id
     * @return array
     */
    public function getInfo(int $sign_id)
    {
        $field = 'sign_id, site_id, member_id, days, day_award, continue_award, continue_tag, create_time, is_sign';
        return $this->model->where([['sign_id', '=', $sign_id], ['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->field($field)->append(['is_sign_name'])->findOrEmpty()->toArray();
    }

    /**
     * 签到
     * @return array
     */
    public function sign()
    {

        $sign_config = $this->getSign();
        if (!$sign_config['is_use']) throw new CommonException('SIGN_NOT_USE');
        if (empty($sign_config['sign_period']) || empty($sign_config['day_award'])) throw new CommonException('SIGN_NOT_SET');
        $sign_period = $sign_config['sign_period'];//签到周期
        $today = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->whereDay('create_time')->findOrEmpty()->toArray();
        if (!empty($today)) throw new CommonException('SIGNED_TODAY');
        Db::startTrans();

        try {
            $yesterday = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->whereDay('create_time', 'yesterday')->findOrEmpty()->toArray();
            if ($yesterday) {
                $days = $yesterday['days'];
                $days++;
                if ($days > $sign_period) { //连签天数大于签到周期,连签天数重置为1
                    $days = 1;
                    $data['start_time'] = time();
                }
                if (!empty($sign_config['continue_award'])) {
                    $continue_signs = array_column($sign_config['continue_award'], 'continue_sign');
                    //获取连签奖励最大天数
                    $max_continue_sign = max($continue_signs);
                    if ($max_continue_sign < $sign_period && $days > $max_continue_sign) { //连签奖励最大天数 小于 签到周期 并且 连签天数 大于 连签奖励最大天数 连签天数重置为1
                        $days = 1;
                    }
                }
            } else { //断签,连签天数重置为1
                $days = 1;
                $data['start_time'] = time();
            }

            $awards = [];        //奖励数组
            $continue_text = ''; //连签提示

            //添加签到记录
            $data['site_id'] = $this->site_id;
            $data['member_id'] = $this->member_id;
            $data['days'] = $days;
            $data['day_award'] = $sign_config['day_award'];
            $data['is_sign'] = 1;
            $data['create_time'] = time();
            $res = $this->model->create($data);
            if ($res) {
                //日签奖励发放
                MemberGiftGrantJob::dispatch([
                    'site_id' => $this->site_id,
                    'member_id' => $this->member_id,
                    'gift' => $sign_config['day_award'],
                    'param' => [
                        'from_type' => 'day_sign_award',
                        'memo' => '日签奖励'
                    ]
                ]);
                $awards['day_award'] = $sign_config['day_award'];

                //签到成功后判断连签天数是否满足连签奖励发放条件
                if (!empty($sign_config['continue_award'])) {
                    foreach ($sign_config['continue_award'] as $key => $value) {
                        $continue_sign = intval($value['continue_sign']);//连续签到天数要求
                        //如果连签天数满足配置条件,发放连签奖励
                        if ($res->days == $continue_sign) {
                            $gifts = $value;
                            unset($gifts['continue_sign'], $gifts['continue_tag'], $gifts['receive_limit'], $gifts['receive_num']);

                            $continue_data['continue_award'] = $value;
                            $continue_data['continue_tag'] = $value['continue_tag'];//连签奖励标识
                            if ($value['receive_limit'] == 2) {//receive_limit (1.不限制 2.每人限领 receive_num 次)
                                //周期开始时间
                                $period_start_time = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id], ['days', '=', 1], ['start_time', '>', 0]])->order('sign_id desc')->field('start_time')->limit(1)->value('start_time');
                                //周期结束时间
                                $period_end_time = strtotime("+$sign_period day", $period_start_time);
                                //查询领取次数
                                $receive_count = $this->model
                                    ->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id], ['continue_tag', '=', $value['continue_tag']]])
                                    ->whereBetweenTime('create_time', $period_start_time, $period_end_time)->count('sign_id');
                                if ($receive_count < $value['receive_num']) {
                                    //连签奖励发放
                                    MemberGiftGrantJob::dispatch([
                                        'site_id' => $this->site_id,
                                        'member_id' => $this->member_id,
                                        'gift' => $gifts,
                                        'param' => [
                                            'from_type' => 'continue_sign_award',
                                            'memo' => '连签奖励'
                                        ]
                                    ]);
                                    $awards['continue_award'] = $gifts;
                                    $continue_text = get_lang('CONTINUE_SIGN').$res->days.get_lang('DAYS');
                                    //更新连签发放记录
                                    $this->model->where([['sign_id', '=', $res->sign_id]])->update($continue_data);
                                }
                            } else { //不限制
                                //连签奖励发放
                                MemberGiftGrantJob::dispatch([
                                    'site_id' => $this->site_id,
                                    'member_id' => $this->member_id,
                                    'gift' => $gifts,
                                    'param' => [
                                        'from_type' => 'continue_sign_award',
                                        'memo' => '连签奖励'
                                    ]
                                ]);
                                $awards['continue_award'] = $gifts;
                                $continue_text = get_lang('CONTINUE_SIGN').$res->days.get_lang('DAYS');
                                //更新连签发放记录
                                $this->model->where([['sign_id', '=', $res->sign_id]])->update($continue_data);
                            }
                        }
                    }
                }
            }
            Db::commit();
            $awards_total = $this->getTotalAward($awards);
            $result['title'] = get_lang('SIGN_SUCCESS');
            $result['info'] = $continue_text.get_lang('GET_AWARD');
            $result['awards'] = $awards_total;
            if ($awards_total) {
                return $result;
            } else {
                return [
                    'title' => '',
                    'info' => '',
                    'awards' => [],
                ];
            }

        } catch (DbException $e) {
            Db::rollback();
            throw new CommonException($e->getMessage());
        }

    }

    /**
     * 获取月签到数据
     * @param int $year
     * @param int $month
     * @return array
     */
    public function getSignInfo(int $year, int $month)
    {
        $data = [];
        $info = $this->getSign();
        if ($info['is_use'] == 1) {//判断签到是否开启
            $model_result = $this->model->field('create_time')->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->whereMonth('create_time', $year . '-' . sprintf("%02d", $month))->select();
            $days = [];
            foreach ($model_result as $key => $value) {
                $day = date('d', strtotime($value['create_time']));
                array_push($days, $day);
            }
            $data['days'] = $days;
            if (!empty($info['sign_period']) && !empty($info['continue_award'])) {//判断签到周期和连签奖励是否设置
                $sign_period = $info['sign_period'];//签到周期

                $continue_signs = array_column($info['continue_award'], 'continue_sign');
                //获取连签奖励最大天数
                $max_continue_sign = max($continue_signs);
                //周期开始时间
                $period_start_time = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id], ['days', '=', 1], ['start_time', '>', 0]])->order('sign_id desc')->field('start_time')->limit(1)->value('start_time');
                if (!empty($period_start_time)) {
                    //周期结束时间
                    $period_end_time = strtotime("+$sign_period day", $period_start_time);
                    //获取两个时间戳之间的天数组
                    $days_array = $this->getDaysArray($period_start_time, $period_end_time);
                    foreach ($days_array as $key => $value) {
                        $day = $key + 1;
                        foreach ($info['continue_award'] as $k => $v) {
                            if ($v['receive_limit'] == 1) {//不限制次数奖励添加
                                $period_num = intdiv($sign_period, $max_continue_sign);//周期内可循环轮次
                                for ($i = 0; $i < $period_num; $i++) {
                                    if ($max_continue_sign * $i + $v['continue_sign'] == $day) {
                                        $data['period'][$key]['award'] = true;
                                    }
                                }
                            } else {//限制次数奖励添加
                                for ($i = 0; $i < $v['receive_num']; $i++) {
                                    if ($max_continue_sign * $i + $v['continue_sign'] == $day) {
                                        $data['period'][$key]['award'] = true;
                                    }
                                }
                            }
                        }
                        $data['period'][$key]['day'] = $value;
                    }
                } else {
                    $data['period'] = [];
                }
            } else {
                $data['period'] = [];
            }
        }
        return $data;
    }

    /**
     * 获取日签到奖励
     * @param int $year
     * @param int $month
     * @param int $day
     * @return array
     */
    public function getDayAward(int $year, int $month, int $day)
    {
        $max_continue_sign = 1;//连签奖励最大天数
        $continue_sign_day = 0;//连签奖励天数

        $time = $year.'-'.sprintf("%02d", $month).'-'.sprintf("%02d", $day);
        $info = $this->getSign();
        if (!$info['is_use']) throw new CommonException('SIGN_NOT_USE');
        if (empty($info['sign_period']) || empty($info['day_award'])) throw new CommonException('SIGN_NOT_SET');
        $sign_period = $info['sign_period'];//签到周期
        if (!empty($info['continue_award'])) {
            $continue_signs = array_column($info['continue_award'], 'continue_sign');
            //获取连签奖励最大天数
            $max_continue_sign = max($continue_signs);
        }
        //周期开始时间
        $period_start_time = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id], ['days', '=', 1], ['start_time', '>', 0]])->order('sign_id desc')->field('start_time')->limit(1)->value('start_time');
        //周期结束时间
        $period_end_time = strtotime("+$sign_period day", $period_start_time);
        //获取两个时间戳之间的天数组
        $days_array = $this->getDaysArray($period_start_time, $period_end_time);
        $award = [];//当日奖励
        //判断查询日期是否在签到周期内
        if (in_array($time, $days_array)) {
            $counter = 0;//计数器
            foreach ($days_array as $key => $value) {
                $counter++;
                if ($value == $time) {

                    $continue_sign_day = $counter;
                    $award['day_award'] = $info['day_award'];

                    if (!empty($info['continue_award'])) {
                        $days = $key + 1;
                        foreach ($info['continue_award'] as $k => $v) {
                            $gift = $v;
                            unset($gift['continue_sign'], $gift['continue_tag'], $gift['receive_limit'], $gift['receive_num']);
                            if ($v['receive_limit'] == 1) {//不限制次数奖励添加
                                $period_num = intdiv($sign_period, $max_continue_sign);//周期内可循环轮次
                                for ($i = 0; $i < $period_num; $i++) {
                                    if ($max_continue_sign * $i + $v['continue_sign'] == $days) {
                                        $award['continue_award'] = $gift;
                                    }
                                }
                            } else {//限制次数奖励添加
                                for ($i = 0; $i < $v['receive_num']; $i++) {
                                    if ($max_continue_sign * $i + $v['continue_sign'] == $days) {
                                        $award['continue_award'] = $gift;
                                    }
                                }
                            }
                        }
                    }

                }
                if (!empty($info['continue_award'])) {
                    if ($counter % $max_continue_sign == 0) {
                        $counter = 0;
                    }
                } else {
                    if ($counter % $sign_period == 0) {
                        $counter = 0;
                    }
                }
            }
        } else {
            $day_result = $this->model->field('create_time')->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->whereDay('create_time', $time)->findOrEmpty()->toArray();
            if (!empty($day_result)) {
                $award['day_award'] = $day_result['day_award'];
                $continue_award = $day_result['continue_award'];
                if (!empty($continue_award)) {
                    unset($continue_award['continue_sign'], $continue_award['continue_tag'], $continue_award['receive_limit'], $continue_award['receive_num']);
                    $award['continue_award'] = $continue_award;
                }
            }
        }
        $awards_total = $this->getTotalAward($award);
        $continue_text = $continue_sign_day > 0 ? get_lang('CONTINUE_SIGN').$continue_sign_day.get_lang('DAYS') : '';
        $result['title'] = get_lang('SIGN_AWARD');
        $result['info'] = $continue_text.get_lang('WILL_GET_AWARD');
        $result['awards'] = $awards_total;
        if ($awards_total) {
            return $result;
        } else {
            return [
                'title' => '',
                'info' => '',
                'awards' => [],
            ];
        }
    }

    /**
     * 获取合并奖励数据
     * @param $awards
     * @return array|null
     */
    private function getTotalAward($awards)
    {
        $total_point = 0;
        $total_balance = 0;
        $coupon_id = [];
        $coupon_list = [];

        $is_use_point_day = false;
        $is_use_point_continue = false;

        $is_use_balance_day = false;
        $is_use_balance_continue = false;

        $is_use_coupon_day = false;
        $is_use_coupon_continue = false;

        if (!empty($awards['day_award']['point'])) {
            if ($awards['day_award']['point']['is_use'] == 1) {
                $is_use_point_day = true;
                $total_point += intval($awards['day_award']['point']['num']);
            }
        }
        if (!empty($awards['day_award']['balance'])) {
            if ($awards['day_award']['balance']['is_use'] == 1) {
                $is_use_balance_day = true;
                $total_balance += floatval($awards['day_award']['balance']['money']);
            }
        }
        if (!empty($awards['day_award']['shop_coupon'])) {
            if ($awards['day_award']['shop_coupon']['is_use'] == 1) {
                $is_use_coupon_day = true;
                $coupon_id = array_merge($coupon_id, $awards['day_award']['shop_coupon']['coupon_id']);
                $coupon_list = $this->getArrayMerge($coupon_list, $awards['day_award']['shop_coupon']['coupon_list']);
            }
        }
        if (!empty($awards['continue_award'])) {
            if (!empty($awards['continue_award']['point'])) {
                if ($awards['continue_award']['point']['is_use'] == 1) {
                    $is_use_point_continue = true;
                    $total_point += intval($awards['continue_award']['point']['num']);
                }
            }
            if (!empty($awards['continue_award']['balance'])) {
                if ($awards['continue_award']['balance']['is_use'] == 1) {
                    $is_use_balance_continue = true;
                    $total_balance += floatval($awards['continue_award']['balance']['money']);
                }
            }
            if (!empty($awards['continue_award']['shop_coupon'])) {
                if ($awards['continue_award']['shop_coupon']['is_use'] == 1) {
                    $is_use_coupon_continue = true;
                    $coupon_id = array_merge($coupon_id, $awards['continue_award']['shop_coupon']['coupon_id']);
                    $coupon_list = $this->getArrayMerge($coupon_list, $awards['continue_award']['shop_coupon']['coupon_list']);
                }
            }
        }
        $coupon_id = array_unique($coupon_id);
        $is_use_point = ($is_use_point_day || $is_use_point_continue) ? 1 : 0;
        $is_use_balance = ($is_use_balance_day || $is_use_balance_continue) ? 1 : 0;
        $is_use_coupon = ($is_use_coupon_day || $is_use_coupon_continue) ? 1 : 0;
        //相同奖励合并
        $awards_total = [
            'point' => [
                'is_use' => $is_use_point,
                'num' => $total_point,
            ],
            'balance' => [
                'is_use' => $is_use_balance,
                'money' => $total_balance,
            ],
            'shop_coupon' => [
                'is_use' => $is_use_coupon,
                'coupon_id' => $coupon_id,
                'coupon_list' => $coupon_list,
            ]
        ];
        return (new CoreMemberService())->getGiftContent($this->site_id, $awards_total, 'member_sign');
    }

    /**
     * 获取用户签到设置
     * @return array
     */
    public function getSignConfig()
    {
        $info = $this->getSign();
        $today = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->whereDay('create_time')->findOrEmpty()->toArray();
        $yesterday = $this->model->where([['site_id', '=', $this->site_id], ['member_id', '=', $this->member_id]])->whereDay('create_time', 'yesterday')->findOrEmpty()->toArray();
        if (!empty($info['day_award'])) {
            $day_award = (new CoreMemberService())->getGiftContent($this->site_id, $info['day_award'],'member_sign');
            $info['day_award'] = $day_award;
        }
        if (!empty($info['continue_award'])) {
            foreach ($info['continue_award'] as $key => $value) {
                $gift = $value;
                unset($gift['continue_sign'], $gift['continue_tag'], $gift['receive_limit'], $gift['receive_num']);
                $gift_content = (new CoreMemberService())->getGiftContent($this->site_id, $gift, 'member_sign_continue');
                $gift_count = 0;
                $content_text = '';
                $content_icon = '';
                foreach ($gift_content as $vv) {
                    if ($vv['is_use'] == 1) {
                        foreach ($vv['content'] as $v) {
                            $content_text = $content_text . ($gift_count == 0 ? '' : '+') . $v['text'];
                            $content_icon = $v['icon'];
                            $gift_count++;
                        }
                    }
                }
                if ($gift_count > 1) {
                    $continue_award['gift'] = ['total' => ['text' => $content_text, 'icon' => '/static/resource/images/member/sign/pack01.png']];
                } else if($gift_count == 1) {
                    $continue_award['gift'] = ['total' => ['text' => $content_text, 'icon' => $content_icon]];
                } else {
                    $continue_award['gift'] = [];
                }

                $continue_award['continue_sign'] = $value['continue_sign'];
                $info['continue_award'][$key] = $continue_award;
            }
        }
        $info['is_sign'] = empty($today) ? false : true;//是否签到
        if (empty($today)) {
            $info['days'] = empty($yesterday) ? 0 : $yesterday['days'];//连签天数
        } else {
            $info['days'] = $today['days'];//连签天数
        }
        return $info;
    }

    /**
     * 获取站点签到设置
     */
    public function getSign()
    {
        $info = ( new CoreConfigService() )->getConfig($this->request->defaultSiteId(), 'SIGN_CONFIG');
        if (empty($info)) {
            $info = [];
            $info[ 'value' ] = [
                'is_use' => 0,
                'sign_period' => '',
                'day_award' => '',
                'continue_award' => [],
                'rule_explain' => ''
            ];
        }
        return $info[ 'value' ];
    }

    /**
     * 获取两个时间戳之间的天数组
     * @param $start_timestamp
     * @param $end_timestamp
     * @return array
     */
    private function getDaysArray($start_timestamp, $end_timestamp) {
        $start = new DateTime("@$start_timestamp"); // 使用时间戳创建DateTime对象
        $end = new DateTime("@$end_timestamp"); // 同上
        $interval = new DateInterval('P1D'); // 每天的周期
        $period = new DatePeriod($start, $interval, $end); // 创建周期范围

        $days_array = [];
        foreach ($period as $day) {
            $days_array[] = $day->format('Y-m-d'); // 格式化日期并添加到数组
        }
        return $days_array;
    }

    /**
     * 合并数据,如果键值相等其值相加
     * @param $desc
     * @param $json_wares
     * @return array|false
     */
    private static function getArrayMerge($desc, $json_wares)
    {
        if (is_array($desc) && is_array($json_wares)) {
            $arrayMerge = array();
            foreach ($json_wares as $key=>$value) {
                if (array_key_exists($key, $desc)) {
                    $arrayMerge[$key] = $value + $desc[$key];
                    unset($desc[$key]);
                } else {
                    $arrayMerge[$key] = $value;
                }
            }
            return $arrayMerge+$desc;
        } else {
            return false;
        }
    }

}