package com.xinelu.mobile.service.appletpersoncenter.Impl;

import com.alibaba.fastjson2.JSONObject;
import com.aliyuncs.exceptions.ClientException;
import com.xinelu.common.config.AliYunSmsConfig;
import com.xinelu.common.config.WeChatAppletChatConfig;
import com.xinelu.common.constant.Constants;
import com.xinelu.common.core.domain.AjaxResult;
import com.xinelu.common.utils.uuid.IdUtils;
import com.xinelu.manage.domain.residentinfo.ResidentInfo;
import com.xinelu.manage.mapper.residentinfo.ResidentInfoMapper;
import com.xinelu.mobile.dto.appletpersoncenter.HealthRecordDTO;
import com.xinelu.mobile.dto.appletpersoncenter.HealthRecordInfoDTO;
import com.xinelu.manage.dto.smssend.SmsInfoDTO;
import com.xinelu.mobile.dto.verifysmscode.VerifySmsCodeDTO;
import com.xinelu.mobile.mapper.appletpersoncenter.AppletPersonCenterMapper;
import com.xinelu.mobile.service.appletpersoncenter.AppletPersonCenterService;
import com.xinelu.mobile.utils.SmsSendUtils;
import com.xinelu.mobile.utils.WeChatAppletUtils;
import com.xinelu.mobile.vo.appletpersoncenter.PostDischargeAppletPhoneVO;
import com.xinelu.mobile.vo.appletpersoncenter.PostDischargeAppletVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

/**
 * @Description 院后小程序个人中心业务层实现类
 * @Author 纪寒
 * @Date 2024-04-16 10:52:09
 * @Version 1.0
 */
@Service
@Slf4j
public class AppletPersonCenterServiceImpl implements AppletPersonCenterService {

    @Resource
    private WeChatAppletUtils weChatAppletUtils;
    @Resource
    private WeChatAppletChatConfig weChatAppletChatConfig;
    @Resource
    private RedisTemplate<String, String> redisTemplate;
    @Resource
    private ResidentInfoMapper residentInfoMapper;
    @Resource
    private AppletPersonCenterMapper appletPersonCenterMapper;

    @Resource
    private SmsSendUtils smsSendUtils;
    @Resource
    private AliYunSmsConfig aliYunSmsConfig;

    /**
     * 院后微信小程序一键登录接口
     *
     * @param loginCode 登录凭证
     * @param phoneCode 获取手机号登录凭证
     * @return 微信小程序用户登录信息
     */
    @Override
    public AjaxResult appletLogin(String loginCode, String phoneCode) {
        //根据code获取用户的微信unionId以及openId等信息
        PostDischargeAppletVO appletLoginInfo = weChatAppletUtils.getPostDischargeAppletLogin(weChatAppletChatConfig.getAppletId(), weChatAppletChatConfig.getSecret(), loginCode, weChatAppletChatConfig.getGrantType());
        if (Objects.isNull(appletLoginInfo)) {
            return AjaxResult.error("获取院后微信小程序用户信息失败");
        }
        if (Objects.nonNull(appletLoginInfo.getErrcode()) && appletLoginInfo.getErrcode() != Constants.SUCCESS_CODE) {
            return AjaxResult.error("获取院后微信小程序用户信息失败，失败信息为：" + appletLoginInfo.getErrmsg());
        }
        //获取微信accessToken
        String accessToken;
        String accessTokenKey = Constants.POST_DISCHARGE_APPLET_ACCESS_TOKEN + "accessToken";
        //从Redis中取出accessToken
        Object object = redisTemplate.opsForValue().get(accessTokenKey);
        if (Objects.isNull(object)) {
            accessToken = weChatAppletUtils.getWeChatAppletAccessToken();
        } else {
            accessToken = (String) object;
        }
        //获取用户手机号
        PostDischargeAppletPhoneVO appletPhoneInfo = weChatAppletUtils.getPostDischargeAppletPhone(phoneCode, accessToken);
        if (Objects.isNull(appletPhoneInfo)) {
            return AjaxResult.error("获取用户手机号失败");
        }
        if (Objects.nonNull(appletPhoneInfo.getErrcode()) && appletPhoneInfo.getErrcode() == Constants.ERROR_ACCESS_CODE) {
            //当前Redis缓存中的access_token无效直接删除
            if (Objects.nonNull(object)) {
                redisTemplate.delete(accessTokenKey);
                //删除之后重新获取获取accessToken
                accessToken = weChatAppletUtils.getWeChatAppletAccessToken();
                appletPhoneInfo = weChatAppletUtils.getPostDischargeAppletPhone(phoneCode, accessToken);
                if (Objects.isNull(appletPhoneInfo)) {
                    return AjaxResult.error("获取用户手机号失败");
                }
                if (Objects.nonNull(appletPhoneInfo.getErrcode()) && appletPhoneInfo.getErrcode() == Constants.ERROR_ACCESS_CODE) {
                    return AjaxResult.error("登录失败！");
                }
            }
        }
        if (StringUtils.isNotBlank(appletPhoneInfo.getErrmsg()) && !Constants.OK.equals(appletPhoneInfo.getErrmsg())) {
            return AjaxResult.error("获取用户手机号失败，失败信息为：" + appletPhoneInfo.getErrmsg());
        }
        //根据手机号和微信小程序openid判断当前用户是否存在
        String phone = StringUtils.isBlank(appletPhoneInfo.getPhoneInfo().getPhoneNumber()) ? "" : appletPhoneInfo.getPhoneInfo().getPhoneNumber();
        String openId = StringUtils.isBlank(appletLoginInfo.getOpenid()) ? "" : appletLoginInfo.getOpenid();
        ResidentInfo residentInfoByPhone = residentInfoMapper.getResidentInfoByPhoneAndOpenId(null, openId);
        ResidentInfo residentInfo = new ResidentInfo();
        //居民信息为空新增个人信息
        if (Objects.isNull(residentInfoByPhone)) {
            residentInfo.setOpenId(openId);
            residentInfo.setPatientPhone(phone);
            residentInfo.setCreateTime(LocalDateTime.now());
            residentInfoMapper.insertResidentInfo(residentInfo);
            residentInfo.setId(residentInfo.getId());
            return AjaxResult.success(residentInfo);
        }
        //更新居民信息的openid等微信标识信息
        residentInfo.setId(residentInfoByPhone.getId());
        residentInfo.setOpenId(openId);
        residentInfo.setPatientPhone(StringUtils.isBlank(residentInfoByPhone.getPatientPhone()) ? "" : residentInfoByPhone.getPatientPhone());
        residentInfo.setUpdateTime(LocalDateTime.now());
        residentInfoMapper.updateResidentInfo(residentInfo);
        return AjaxResult.success(residentInfo);
    }

    /**
     * 根据居民表id查询患者个人信息
     *
     * @param residentId 居民表id
     * @return 个人信息
     */
    @Override
    public AjaxResult getResidentInfoById(Long residentId) {
        return AjaxResult.success(residentInfoMapper.selectResidentInfoById(residentId));
    }

    /**
     * 根据居民表id查询患者健康档案信息
     *
     * @param residentId 居民患者id
     * @return 健康档案列表
     */
    @Override
    public List<HealthRecordDTO> getHealthRecordListByResidentId(Long residentId) {
        return appletPersonCenterMapper.getHealthRecordListByResidentId(residentId);

    }

    /**
     * 根据id获取患者患者健康档案信息
     *
     * @param id 居民患者id
     * @return PatientInfo
     */
    @Override
    public HealthRecordInfoDTO getHealthRecordInfoById(Long id) {
        HealthRecordInfoDTO healthRecordInfoDTO = appletPersonCenterMapper.getHealthRecordInfoById(id);
        //签约状态，未签约：UN_SIGN,在签：IN_SIGN,解约：SEPARATE_SIGN
        if (StringUtils.equals(healthRecordInfoDTO.getSignStatus(), "UN_SIGN") || StringUtils.equals(healthRecordInfoDTO.getSignStatus(), "SEPARATE_SIGN")) {
            healthRecordInfoDTO.setSignStatus("否");
        } else if (StringUtils.equals(healthRecordInfoDTO.getSignStatus(), "IN_SIGN")) {
            healthRecordInfoDTO.setSignStatus("是");
        }
        return healthRecordInfoDTO;
    }

    /**
     * 院后微信小程序登录接口
     *
     * @param loginCode 登录凭证
     * @return 微信小程序用户登录信息
     */
    @Override
    public AjaxResult appletLoginV1(String loginCode) {
        //根据code获取用户的微信unionId以及openId等信息
        PostDischargeAppletVO appletLoginInfo = weChatAppletUtils.getPostDischargeAppletLogin(weChatAppletChatConfig.getAppletId(), weChatAppletChatConfig.getSecret(), loginCode, weChatAppletChatConfig.getGrantType());
        if (Objects.isNull(appletLoginInfo)) {
            return AjaxResult.error("获取院后微信小程序用户信息失败");
        }
        if (Objects.nonNull(appletLoginInfo.getErrcode()) && appletLoginInfo.getErrcode() != Constants.SUCCESS_CODE) {
            return AjaxResult.error("获取院后微信小程序用户信息失败，失败信息为：" + appletLoginInfo.getErrmsg());
        }
        String openId = StringUtils.isBlank(appletLoginInfo.getOpenid()) ? "" : appletLoginInfo.getOpenid();
        ResidentInfo residentInfo = residentInfoMapper.getResidentInfoByPhoneAndOpenId(null, openId);
        if (ObjectUtils.isEmpty(residentInfo)) {
            return AjaxResult.success("未注册", openId);
        }
        return AjaxResult.success("已注册", residentInfo);
    }

    /**
     * 小程序登录发送验证码
     *
     * @param phoneNum 手机号码
     * @return 发送短信验证码信息
     */
    @Override
    public AjaxResult sendSms(String phoneNum) {
        try {
            String coolDownKey = Constants.SMS_COOL_DOWN + phoneNum;
            String cacheKey = Constants.SMS_CODE + phoneNum;
            ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();

            // 检查是否处于冷却期
            if (valueOperations.get(coolDownKey) != null) {
                return AjaxResult.error("请等待60秒后再尝试发送验证码");
            }

            // 生成并发送新的验证码
            SmsInfoDTO smsInfoDTO = new SmsInfoDTO();
            smsInfoDTO.setPhoneNumbers(phoneNum);
            smsInfoDTO.setSignName(aliYunSmsConfig.getSignName());
            smsInfoDTO.setTemplateCode(aliYunSmsConfig.getTemplateCode());
            String code = String.valueOf(IdUtils.generateSixDigitNumber());
            JSONObject obj = new JSONObject();
            obj.put("code", code);
            smsInfoDTO.setTemplateParam(obj);
            Boolean b = smsSendUtils.sendSms(smsInfoDTO);
            if (!b) {
                log.error("短信发送失败，手机号：{}", phoneNum);
                return AjaxResult.error("短信发送失败");
            }

            // 更新验证码和冷却键
            valueOperations.set(cacheKey, code, 5, TimeUnit.MINUTES);
            valueOperations.set(coolDownKey, Constants.SMS_COOL_DOWN_VALUE, 60, TimeUnit.SECONDS);
            log.info("手机号为{}的验证码已经生成，验证码:{}", phoneNum, code);
            return AjaxResult.success("短信发送成功");
        } catch (ClientException e) {
            log.error("短信发送失败，手机号：{}，错误原因：{}", phoneNum, e.getMessage(), e);
            return AjaxResult.error("短信发送失败");
        }
    }

    /**
     * 小程序登录验证短信验证码
     *
     * @param verifySmsCodeDTO 验证短信验证码DTO
     * @return 验证短信验证码信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult verifySmsCode(VerifySmsCodeDTO verifySmsCodeDTO) {
        try {
            String openId = verifySmsCodeDTO.getOpenId();
            String smsCode = verifySmsCodeDTO.getSmsCode();
            String cacheKey = Constants.SMS_CODE + verifySmsCodeDTO.getPhoneNum();
            ValueOperations<String, String> valueOperations = redisTemplate.opsForValue();
            String storedCode = valueOperations.get(cacheKey);
            // 缓存的验证码不存在
            if (storedCode == null) {
                log.error("验证码已过期，手机号：{}", verifySmsCodeDTO.getPhoneNum());
                return AjaxResult.error("验证码已过期，请重新获取！");
            }
            // 缓存的验证码与输入的验证码不匹配
            if (!storedCode.equals(smsCode)) {
                log.error("验证码验证失败，手机号：{}，输入的验证码：{}，获取的验证码：{}", verifySmsCodeDTO.getPhoneNum(), smsCode, storedCode);
                return AjaxResult.error("验证码错误，请重新输入！");
            }
            // 根据手机号和姓名查询用户信息
            log.info("验证码验证成功，手机号：{}，输入的验证码：{}，获取的验证码：{}", verifySmsCodeDTO.getPhoneNum(), smsCode, storedCode);
            List<ResidentInfo> residentInfos = residentInfoMapper.getResidentInfoByPhoneAndName(verifySmsCodeDTO.getPhoneNum(), verifySmsCodeDTO.getPatientName());
            // 用户信息不存在
            if (residentInfos == null || residentInfos.isEmpty()) {
                return AjaxResult.error("您的用户信息尚未录入系统，无法执行注册操作！");
            } else if (residentInfos.size() > 1) {
                // 存在姓名和手机号重复的情况
                return AjaxResult.error("您的信息存在重复记录，请联系管理员进行操作！");
            }
            // 更新用户的openid
            ResidentInfo residentInfo = residentInfos.get(0);
            residentInfo.setOpenId(openId);
            residentInfo.setUpdateBy(residentInfo.getPatientName());
            residentInfo.setUpdateTime(LocalDateTime.now());
            residentInfoMapper.updateResidentInfo(residentInfo);
            log.info("用户注册成功，姓名：{}，手机号：{}，openid：{}", verifySmsCodeDTO.getPatientName(), verifySmsCodeDTO.getPhoneNum(), openId);
            return AjaxResult.success("注册成功！", residentInfo);
        } catch (Exception e) {
            log.error("验证验证码时发生异常，手机号：{}，异常信息：{}", verifySmsCodeDTO.getPhoneNum(), e.getMessage(), e);
            return AjaxResult.error("短信验证码验证失败！");
        }
    }

    /**
     * 小程序端手机号与openid解绑
     *
     * @param phoneNum 手机号
     * @param openId   openid
     * @return 解绑信息
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public AjaxResult unbindOpenId(String phoneNum, String openId) {
        try {
            // 根据phoneNum和openid查询用户信息
            ResidentInfo residentInfo = residentInfoMapper.getResidentInfoByPhoneAndOpenId(phoneNum, openId);

            // 未查询到用户信息
            if (ObjectUtils.isEmpty(residentInfo)) {
                log.info("未查询到用户信息，手机号：{}，openId：{}", phoneNum, openId);
                return AjaxResult.error("未查询到用户信息，无法解绑！");
            }

            // 清空openid字段，更新用户信息
            residentInfo.setOpenId(null);
            residentInfo.setUpdateBy(residentInfo.getPatientName());
            residentInfo.setUpdateTime(LocalDateTime.now());
            residentInfoMapper.updateResidentInfoOpenid(residentInfo);
            log.info("解绑成功，手机号：{}，openId：{}", phoneNum, openId);
            return AjaxResult.success("解绑成功！");
        } catch (Exception e) {
            log.error("解绑openId时发生异常，手机号：{}，openId：{}，异常信息：{}", phoneNum, openId, e.getMessage(), e);
            return AjaxResult.error("解绑失败！");
        }
    }
}
