package ink.wgink.module.sms.service.sms.impl;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.ParamsException;
import ink.wgink.exceptions.base.SystemException;
import ink.wgink.interfaces.user.IUserBaseService;
import ink.wgink.module.sms.dao.sms.ISmsDao;
import ink.wgink.module.sms.factory.sms.ICustomSmsSend;
import ink.wgink.module.sms.factory.sms.SmsSendFactory;
import ink.wgink.module.sms.manager.VerifyCodeManager;
import ink.wgink.module.sms.pojo.dtos.sms.SmsDTO;
import ink.wgink.module.sms.pojo.pos.sms.SmsPO;
import ink.wgink.module.sms.pojo.vos.sms.SmsPhoneSendVO;
import ink.wgink.module.sms.pojo.vos.sms.SmsSendBaseVO;
import ink.wgink.module.sms.pojo.vos.sms.SmsSendVO;
import ink.wgink.module.sms.pojo.vos.sms.SmsVO;
import ink.wgink.module.sms.service.sms.ISmsService;
import ink.wgink.pojo.ListPage;
import ink.wgink.pojo.dtos.user.UserDTO;
import ink.wgink.pojo.result.SuccessResultList;
import ink.wgink.properties.sms.SmsProperties;
import ink.wgink.util.RegexUtil;
import ink.wgink.util.UUIDUtil;
import ink.wgink.util.map.HashMapUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: SmsServiceImpl
 * @Description: 短信
 * @Author: wanggeng
 * @Date: 2021/5/1 3:07 下午
 * @Version: 1.0
 */
@Service
public class SmsServiceImpl extends DefaultBaseService implements ISmsService {

    @Autowired
    private SmsProperties smsProperties;
    @Autowired
    private ISmsDao smsDao;
    @Autowired
    private IUserBaseService userBaseService;
    @Autowired(required = false)
    private ICustomSmsSend smsCustomSend;

    private ScheduledExecutorService scheduledExecutorService = new ScheduledThreadPoolExecutor(2);

    @Override
    public void save(SmsVO smsVO) {
        Map<String, Object> params = HashMapUtil.beanToMap(smsVO);
        params.put("smsId", UUIDUtil.getUUID());
        setSaveInfoByUserId(params, "1");
        smsDao.save(params);
    }

    @Override
    public void remove(List<String> ids) {
        Map<String, Object> params = getHashMap(6);
        params.put("smsIds", ids);
        setUpdateInfo(params);
        smsDao.remove(params);
    }

    @Override
    public SmsDTO get(Map<String, Object> params) {
        return smsDao.get(params);
    }

    @Override
    public SmsDTO get(String smsId) {
        Map<String, Object> params = getHashMap(2);
        params.put("smsId", smsId);
        return get(params);
    }

    @Override
    public SmsPO getPO(Map<String, Object> params) {
        return smsDao.getPO(params);
    }

    @Override
    public SmsPO getPO(String smsId) {
        Map<String, Object> params = getHashMap(2);
        params.put("smsId", smsId);
        return getPO(params);
    }

    @Override
    public List<SmsDTO> list(Map<String, Object> params) {
        return smsDao.list(params);
    }

    @Override
    public SuccessResultList<List<SmsDTO>> listPage(ListPage page) {
        PageHelper.startPage(page.getPage(), page.getRows());
        List<SmsDTO> smsDTOs = list(page.getParams());
        PageInfo<SmsDTO> pageInfo = new PageInfo<>(smsDTOs);
        return new SuccessResultList<>(smsDTOs, pageInfo.getPageNum(), pageInfo.getTotal());
    }

    @Override
    public void sendContentByUserIdAndPhoneAndUserName(String userId, String phone, String userName, String content) {
        SmsVO smsVO = new SmsVO();
        smsVO.setUserId(userId);
        smsVO.setUserName(userName);
        smsVO.setPhone(phone);
        smsVO.setContent(content);
        try {
            LOG.info(">>>>> 向手机号：{}，发送内容：{}", phone, content);
            SmsSendFactory.getSendSms(smsProperties, smsCustomSend).content(phone, content);
            smsVO.setSendStatus(1);
        } catch (Exception e) {
            String errorMessage = e.getMessage();
            smsVO.setSendStatus(0);
            smsVO.setErrorMessage(errorMessage);
            throw new SystemException("短信发送失败");
        } finally {
            save(smsVO);
        }
    }

    @Override
    public void sendContentDirectlyByPhone(String phone, String content) {
        SmsVO smsVO = new SmsVO();
        smsVO.setUserId(phone);
        smsVO.setUserName(phone);
        smsVO.setPhone(phone);
        smsVO.setContent(content);
        try {
            LOG.info(">>>>> 向手机号：{}，发送内容：{}", phone, content);
            SmsSendFactory.getSendSms(smsProperties, smsCustomSend).content(phone, content);
            smsVO.setSendStatus(1);
        } catch (Exception e) {
            String errorMessage = e.getMessage();
            smsVO.setSendStatus(0);
            smsVO.setErrorMessage(errorMessage);
            throw new SystemException("短信发送失败");
        } finally {
            save(smsVO);
        }
    }

    @Override
    public void sendVerifyCode(String phone) {
        VerifyCodeManager verifyCodeManager = VerifyCodeManager.getInstance();
        long timeRemaining = verifyCodeManager.effectiveVerificationCodeTimeRemaining(phone);
        if (timeRemaining > 0) {
            throw new ParamsException(String.format("验证码已发送，若没有收到，请在%d秒后重试", (timeRemaining / 1000)));
        }

        String currentTimeStr = String.valueOf(System.currentTimeMillis());
        String code = currentTimeStr.substring(currentTimeStr.length() - 6);
        LOG.info(">>>>> 向手机号：{}，发送验证码：{}", phone, code);

        SmsVO smsVO = new SmsVO();
        smsVO.setPhone(phone);
        smsVO.setContent(code);
        try {
            SmsSendFactory.getSendSms(smsProperties, smsCustomSend).code(phone, code);
            verifyCodeManager.setVerificationCode(phone, code);
            smsVO.setSendStatus(1);
        } catch (Exception e) {
            LOG.error(e.getMessage());
            String errorMessage = e.getMessage();
            smsVO.setSendStatus(0);
            smsVO.setErrorMessage(errorMessage);
            throw new SystemException("短信发送失败");
        } finally {
            save(smsVO);
        }
    }

    @Override
    public void send(SmsSendVO smsSendVO) {
        List<String> userIds = Arrays.asList(smsSendVO.getUserIds().split("\\_"));
        List<UserDTO> userDTOs = userBaseService.listByUserIds(userIds);
        scheduledExecutorService.schedule(() -> {
            sendSms(userDTOs, smsSendVO);
        }, 3, TimeUnit.SECONDS);
    }

    @Override
    public void sendByPhones(SmsPhoneSendVO smsPhoneSendVO) {
        for (String phone : smsPhoneSendVO.getPhones()) {
            if (!RegexUtil.isPhone(phone)) {
                throw new ParamsException(phone + " 不是手机号");
            }
        }
        List<UserDTO> userDTOs = userBaseService.listByUserPhones(smsPhoneSendVO.getPhones());
        scheduledExecutorService.schedule(() -> {
            sendSms(userDTOs, smsPhoneSendVO);
        }, 3, TimeUnit.SECONDS);
    }

    @Override
    public void sendDirectlyByPhones(SmsPhoneSendVO smsPhoneSendVO) {
        List<String> phones = new ArrayList<>(smsPhoneSendVO.getPhones());
        for (int i = 0; i < phones.size(); i++) {
            if (!RegexUtil.isPhone(phones.get(i))) {
                phones.remove(i);
                i--;
                continue;
            }
        }
        if (phones.isEmpty()) {
            return;
        }
        scheduledExecutorService.schedule(() -> {
            phones.forEach(phone -> {
                sendContentDirectlyByPhone(phone, smsPhoneSendVO.getContent());
            });
        }, 3, TimeUnit.SECONDS);
    }

    /**
     * 发送短信
     * 如果用户设置手机号，并且手机号格式正确，用手机号发送，如果格式错误，判断用户名
     * 如果用户名格式为手机号，用用户名发送，否则不发送短信
     *
     * @param userDTOs
     */
    private void sendSms(List<UserDTO> userDTOs, SmsSendBaseVO smsSendBaseVO) {
        userDTOs.forEach(userDTO -> {
            if (!StringUtils.isBlank(userDTO.getUserPhone())) {
                if (RegexUtil.isPhone(userDTO.getUserPhone())) {
                    LOG.info("用户：{}:{} 使用手机号发送短信", userDTO.getUserId(), userDTO.getUserUsername());
                    sendContentByUserIdAndPhoneAndUserName(userDTO.getUserId(), userDTO.getUserPhone(), userDTO.getUserName(), smsSendBaseVO.getContent());
                    return;
                }
            }
            if (RegexUtil.isPhone(userDTO.getUserUsername())) {
                LOG.info("用户：{}:{} 使用用户名发送短信", userDTO.getUserId(), userDTO.getUserUsername());
                sendContentByUserIdAndPhoneAndUserName(userDTO.getUserId(), userDTO.getUserUsername(), userDTO.getUserName(), smsSendBaseVO.getContent());
                return;
            }
            LOG.info("用户：{}:{} 手机号、用户名均不是手机号", userDTO.getUserId(), userDTO.getUserUsername());
        });
    }

    @Override
    public List<SmsPO> listPO(Map<String, Object> params) {
        return smsDao.listPO(params);
    }

    @Override
    public List<SmsPO> listPOByDate(String date) {
        Map<String, Object> params = getHashMap(2);
        params.put("date", date);
        return listPO(params);
    }

    @Override
    public List<SmsPO> listPOByStartTimeAndEndTime(String startTime, String endTime) {
        Map<String, Object> params = getHashMap(4);
        params.put("startTime", startTime);
        params.put("endTime", endTime);
        return listPO(params);
    }

    @Override
    public String getVerifyCode(String phone) {
        return VerifyCodeManager.getInstance().getVerifyCode(phone);
    }

    /**
     * 设置用户
     *
     * @param smsDTOs
     */
    private void setUser(List<SmsDTO> smsDTOs) {
        if (smsDTOs.isEmpty()) {
            return;
        }
        Set<String> usernameSet = new HashSet<>();
        for (SmsDTO smsDTO : smsDTOs) {
            usernameSet.add(smsDTO.getPhone());
        }
        List<UserDTO> userDTOs = userBaseService.listByUsernames(new ArrayList<>(usernameSet));
        if (userDTOs.isEmpty()) {
            return;
        }
        for (SmsDTO smsDTO : smsDTOs) {
            for (UserDTO userDTO : userDTOs) {
                if (StringUtils.equals(smsDTO.getPhone(), userDTO.getUserUsername())) {
                    smsDTO.setUserName(userDTO.getUserName());
                    break;
                }
            }
        }
    }

}
