package com.ywt.outpatient.service.web;

import com.ywt.biz.common.enums.TerminalEnum;
import com.ywt.biz.common.util.Checker;
import com.ywt.biz.common.util.PhoneNumChecker;
import com.ywt.biz.common.util.RandomUtil;
import com.ywt.biz.common.util.StringHelper;
import com.ywt.biz.common.util.serializers.JsonSerializer;
import com.ywt.gapi.Result;
import com.ywt.gapi.ResultCode;
import com.ywt.gapi.message.MessageService;
import com.ywt.gapi.message.SendSmsCodeRequest;
import com.ywt.gapi.message.SendSmsCodeResponse;
import com.ywt.gapi.message.SmsTrackingData;
import com.ywt.outpatient.core.BaseResponse;
import com.ywt.outpatient.domain.models.RedisCacheKey;
import com.ywt.outpatient.domain.models.enums.SmsCodeTypeEnum;
import com.ywt.outpatient.core.WebAppContext;
import org.apache.dubbo.config.annotation.DubboReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

/**
 * 短信验证码逻辑类
 *
 * @author johnson lin
 */
@Service
public class MessageSrv {
    private static Logger logger = LoggerFactory.getLogger(MessageSrv.class);

    /**
     * 默认验证码，当用户提交的验证码为默认时，则绕过验证
     */
    private final String SMS_CODE = "253029";

    /**
     * 验证码有效时间，单位：秒
     */
    private final int EXPIRED = 15 * 60;

    /**
     * 发送验证码频次限制，每天10次
     */
    private final int SMS_MOBILE_LIMIT_DAY = 5;

    @DubboReference
    private MessageService messageServiceBlockingStub;

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    public BaseResponse sendSmsCode(String mobile, int type, String invokingMethodName) {
        BaseResponse response = verify(mobile, type);
        if (response.getCode() != BaseResponse.SUCCEED) {
            return response;
        }

        String code = RandomUtil.getRandomCode(6, 1);
        String key = String.format(RedisCacheKey.SMS_CODE_KEY_FORMAT, mobile, type);

        try {
            String smsSign;
            //根据终端指定短信抬头
            switch (TerminalEnum.valueOf(WebAppContext.current().getTerminal())) {
                case NfHealth:
                    smsSign = "南方大健康";
                    break;
                case TAI_HE_INTERNET_HOSPITAL_DOCTOR_WX_OFFICIAL:
                case TAI_HE_INTERNET_HOSPITAL_PATIENT_MINI_PROGRAM:
                case TAI_HE_INTERNET_HOSPITAL_PATIENT_WX_OFFICIAL:
                case PRESCRIPTION_SYSTEM_PC:
                    smsSign = "互联网医院";
                    break;
                default:
                    smsSign = "南方医务通";
                    break;
            }
            // 限制次数，每天最多5次
            String limitKey = String.format(RedisCacheKey.SMS_MOBILE_KEY_FORMAT, mobile, type);
            if (!checkSendStatus(limitKey)) {
                return response.failedWithParameterError("超出次数");
            }

            Map<String, Object> authDataMap = new HashMap<>();
            authDataMap.put("unionId", WebAppContext.current().getUnionId());
            authDataMap.put("openId", WebAppContext.current().getOpenId());
            authDataMap.put("userId", WebAppContext.current().getUserId());
            SmsTrackingData data = SmsTrackingData.newBuilder()
                    .setIp(WebAppContext.current().getIP())
                    .setServiceName("wapapi")
                    .setMethodName(invokingMethodName)
                    .setTerminal(WebAppContext.current().getTerminal())
                    .setAuthData(JsonSerializer.toJson(authDataMap))
                    .build();

            SendSmsCodeRequest req = SendSmsCodeRequest.newBuilder()
                    .setType(type)
                    .setCode(code)
                    .setMobile(mobile)
                    .setSmsSign(smsSign)
                    .setSmsTrackingData(data)
                    .build();
            SendSmsCodeResponse res = messageServiceBlockingStub.sendSmsCode(req);
            Result result = res.getResult();

            if (result.getCode() == ResultCode.SUCCEED) {
                redisTemplate.opsForValue().set(key, code, EXPIRED, TimeUnit.SECONDS);
                return response.succeed(result.getInfo());
            }
            return response.failedWithAppError(result.getInfo());
        } catch (Exception e) {
            logger.error("sendSmsCode({}, {}): {}", mobile, type, e.getMessage(), e);
            return response.error();
        }
    }


    public BaseResponse checkSmsCode(String mobile, String code, int type) {
        BaseResponse response = verify(mobile, type);

        if (response.getCode() != BaseResponse.SUCCEED) {
            return response;
        }
        if (StringHelper.isNullOrEmpty(code)) {
            return response.failedWithParameterError("请输入验证码");
        }
        if (SMS_CODE.equals(code)) {
            //绕过验证码
            return response.succeed();
        }

        String key = String.format(RedisCacheKey.SMS_CODE_KEY_FORMAT, mobile, type);
        Object val = redisTemplate.opsForValue().get(key);

        if (val != null && val.equals(code)) {
            redisTemplate.delete(key);
            return response.succeed();
        }
        return response.failedWithParameterError("验证码不正确");
    }

    /**
     * 处方修改密码专用
     *
     * @param mobile
     * @param code
     * @param type
     * @param count
     * @return
     */
    public BaseResponse checkSmsCodeTwice(String mobile, String code, int type, int count) {
        BaseResponse response = verify(mobile, type);

        if (response.getCode() != BaseResponse.SUCCEED) {
            return response;
        }
        if (SMS_CODE.equals(code)) {
            //绕过验证码
            return response.succeed();
        }

        String key = String.format(RedisCacheKey.SMS_CODE_KEY_FORMAT, mobile, type);
        String val = redisTemplate.opsForValue().get(key)+"";

        if (val != null && val.equals(code)) {
            response.setCode(BaseResponse.SUCCEED);
            return response;
        }
        return response.failedWithParameterError("验证码不正确");
    }

    private BaseResponse verify(String mobile, int type) {
        BaseResponse response = new BaseResponse();
        if (!PhoneNumChecker.isPhoneNum(mobile)) {
            return response.failedWithParameterError("手机号码不正确");
        }
        SmsCodeTypeEnum typeEnum = SmsCodeTypeEnum.valueOf(type);
        if (typeEnum == null) {
            return response.failedWithParameterError("未定义业务类型");
        }
        return response.succeed();
    }

    /**
     * 判断是否可以发送验证码
     *
     * @param key redis 的ky
     * @return true: 可以发送，false:不能发送
     */
    public boolean checkSendStatus(final String key) throws ParseException {
        // 限制次数
        final int limitTime = SMS_MOBILE_LIMIT_DAY;
        int expireTime = getExpireSecond();
        if (Checker.isNone(key.trim())) {
            return false;
        }
        String valueStr = redisTemplate.opsForValue().get(key)+"";
        if (!Checker.isNone(valueStr)) {
            int value = Integer.parseInt(valueStr);
            logger.error("checkSendStatus:{},{},{},{}", key, valueStr, value, limitTime);
            if (value > limitTime) {
                return false;
            }
        }
        redisTemplate.opsForValue().increment(key);
        redisTemplate.opsForValue().getAndExpire(key, expireTime, TimeUnit.SECONDS);
        return true;
    }

    /**
     * 得到当前时间距离第二天凌晨有多少秒
     *
     * @return 秒
     */
    private int getExpireSecond() throws ParseException {
        Date currentDate = new Date();
        // 当前时间
        long time = currentDate.getTime();
        // 第二天凌晨的时间
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(currentDate);
        calendar.add(Calendar.DATE, 1);
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        String endDate = sdf.format(calendar.getTime());
        Date expireDate = sdf.parse(endDate);
        long expireDateTime = expireDate.getTime();
        // 得到相差多少秒
        long diffTime = (expireDateTime - time) / 1000;
        // long to int
        Long diffTimeLong = new Long(diffTime);
        return diffTimeLong.intValue();
    }

}
