package com.css.txw.sso.service.auth.impl;


import static com.aliyun.teautil.Common.toJSONString;
import static com.css.ggzc.framework.common.exception.util.ServiceExceptionUtil.exception;
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_BAD_CREDENTIALS;
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_CAPTCHA_CODE_ERROR;
import static com.css.txw.sso.constants.ErrorCodeConstants.AUTH_LOGIN_PASSWORD_ERROR_LOCK;
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_LOGIN_SJHM_NOT_EXISTS;
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_LOGIN_SMS_NOT_EXISTS;
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_SJHM_LOCK;
import static com.css.txw.sso.constants.ErrorCodeConstants.OAUTH2_SJHM_NOT_EXISTS;
import static com.css.txw.sso.constants.SsoConstants.SMS_CLIENT_SIGNNAME;
import static com.css.txw.sso.constants.SsoConstants.SMS_TEMPLATE_CODE;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;

import javax.annotation.Resource;
import javax.validation.Valid;

import org.apache.commons.lang.RandomStringUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import com.aliyun.dysmsapi20170525.Client;
import com.aliyun.dysmsapi20170525.models.SendSmsRequest;
import com.aliyun.dysmsapi20170525.models.SendSmsResponse;
import com.css.ggzc.framework.cache.utils.CacheUtils;
import com.css.ggzc.framework.common.exception.enums.GlobalErrorCodeConstants;
import com.css.ggzc.framework.common.util.gy.GyUtils;
import com.css.ggzc.framework.common.util.json.JsonUtils;
import com.css.txw.common.pojo.dto.sms.SMSResDTO;
import com.css.txw.common.service.ISMService;
import com.css.txw.mhzc.pojo.YhxxbDTO;
import com.css.txw.sso.configuration.SMSClient;
import com.css.txw.sso.controller.thirdparty.DidController;
import com.css.txw.sso.convert.AuthConvert;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2AccessTokenDO;
import com.css.txw.sso.pojo.domain.oauth2.OAuth2ClientDO;
import com.css.txw.sso.pojo.vo.AuthLoginReqVO;
import com.css.txw.sso.pojo.vo.AuthLoginRespVO;
import com.css.txw.sso.pojo.vo.DidBindPhoneReqVO;
import com.css.txw.sso.pojo.vo.SMSLoginReqVO;
import com.css.txw.sso.pojo.vo.SendMsgReqVO;
import com.css.txw.sso.pojo.vo.oauth2.LogoutVO;
import com.css.txw.sso.pojo.vo.thirdparty.did.DidCallbackExtendInfo;
import com.css.txw.sso.pojo.vo.thirdparty.did.DidCredentialSubject;
import com.css.txw.sso.service.auth.AuthService;
import com.css.txw.sso.service.oauth2.OAuth2ClientService;
import com.css.txw.sso.service.oauth2.OAuth2TokenService;
import com.css.txw.sso.service.verify.VerifyService;
import com.css.txw.sso.service.yhxx.AccountLockService;
import com.css.txw.sso.service.yhxx.YhxxService;

import cn.hutool.core.util.IdUtil;
import cn.hutool.crypto.digest.MD5;
import lombok.extern.slf4j.Slf4j;

@Service
@Slf4j
public class AuthServiceImpl implements AuthService {

    @Resource
    private OAuth2TokenService oauth2TokenService;

    @Resource
    private VerifyService verifyService;

    @Resource
    private AccountLockService accountLockService;

    @Resource
    private OAuth2ClientService clientService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private YhxxService yhxxService;

    @Resource
    private ISMService smService;

    @Override
    public YhxxbDTO authenticate(String username, String password) {


        YhxxbDTO yhxxbDO = yhxxService.getYhxxByDlzh(username);

        if (yhxxbDO == null) {
            throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
        }

        if (accountLockService.checkLockStatus(yhxxbDO.getYhUuid())) {
            throw exception(AUTH_LOGIN_PASSWORD_ERROR_LOCK);
        }

        if (!isPasswordMatch(password, yhxxbDO.getDlmm())) {
            accountLockService.handlePasswordError(yhxxbDO.getYhUuid());
            throw exception(AUTH_LOGIN_BAD_CREDENTIALS);
        }

        accountLockService.clearCache(yhxxbDO.getYhUuid());

        return yhxxbDO;
    }

    @Override
    public AuthLoginRespVO login(AuthLoginReqVO reqVO) {
        final Boolean checked = verifyService.checkVerifyToken(reqVO.getCaptchaVerification());
        if (!checked) {
            throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR);
        }
        // 使用账号密码，进行登录
        YhxxbDTO user = authenticate(reqVO.getUsername(), reqVO.getPassword());
        return createTokenAfterLoginSuccess(user.getYhUuid());
    }


    @Override
    public List<LogoutVO> logout(String token, Integer logType) {
        List<LogoutVO> result = new ArrayList<>();

        final List<OAuth2AccessTokenDO> oAuth2AccessTokenDOS = oauth2TokenService.removeAccessToken(token);
        if (!GyUtils.isNull(oAuth2AccessTokenDOS)) {
            oAuth2AccessTokenDOS.forEach(oAuth2AccessTokenDO -> {
                final String clientid = oAuth2AccessTokenDO.getClientid();
                if (GyUtils.isNull(clientid)) {
                    log.info("logout方法clientid为空,入参token:{},loginType:{}", token, logType);
                } else {
                    final OAuth2ClientDO oAuth2ClientFromCache = clientService.getOAuth2ClientFromCache(clientid);
                    final String dcdz = oAuth2ClientFromCache.getDcdz();
                    final LogoutVO logoutVO = new LogoutVO();
                    logoutVO.setToken(token);
                    logoutVO.setDcdz(dcdz);
                    logoutVO.setClient(clientid);
                    result.add(logoutVO);
                }
            });
        }
        return result;
    }

    @Override
    public AuthLoginRespVO refreshToken(String refreshToken) {
        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.refreshAccessToken(refreshToken, "default");
        return AuthConvert.INSTANCE.convert(accessTokenDO);
    }

    @Override
    public Integer sendMsg(SendMsgReqVO reqVO) throws Exception{
        final Boolean checked = verifyService.checkVerifyToken(reqVO.getCaptchaVerification());
        if (!checked) {
            throw exception(AUTH_LOGIN_CAPTCHA_CODE_ERROR);
        }
        String sjhm = reqVO.getSjhm1();
        Boolean aBoolean = stringRedisTemplate.hasKey(formatKeyPhone(sjhm));
        if (!GyUtils.isNull(aBoolean)&&aBoolean) {
            throw exception(OAUTH2_SJHM_LOCK);
        }
        stringRedisTemplate.opsForValue().set(formatKey(sjhm), sjhm, 1, TimeUnit.MINUTES);
//        YhxxbDTO yhxx = yhxxService.getYhxxBySjhm(sjhm);
//        if (GyUtils.isNull(yhxx)) {
//            throw exception(OAUTH2_SJHM_NOT_EXISTS);
//        }
        Boolean aBoolean1 = stringRedisTemplate.hasKey(formatKeyPhone(sjhm));
        if (GyUtils.isNull(aBoolean1)||!aBoolean1) {
            throw exception(OAUTH2_LOGIN_SMS_NOT_EXISTS);
        }
        String s = CacheUtils.dm2mc("cs_ggzc_xtcs", "SSO_SMS");
        String sms;
        if (!GyUtils.isNull(s)){
            sms = s;
        }else {
            sms = RandomStringUtils.randomNumeric(6);
            Map<String,String> param = new HashMap<>();
            param.put("code",sms);
            //发送短信
            SMSResDTO smsResDTO = smService.sendCaptcha(sjhm, sms);
            //String resJson = send(sjhm, param);
            stringRedisTemplate.opsForValue().set(formatKey(sjhm+sms), sjhm, 3, TimeUnit.MINUTES);
            //Map<String, Object> map = JsonUtils.toMap(resJson);
            Integer res = StringUtils.isBlank(smsResDTO.getCode()) ? 0 : Integer.parseInt(smsResDTO.getCode());
            return res;
        }
        stringRedisTemplate.opsForValue().set(formatKey(sjhm+sms), sjhm, 3, TimeUnit.MINUTES);
        return 1;
    }

    @Override
    public AuthLoginRespVO loginBySMS(SMSLoginReqVO reqVO) {
        String sms = reqVO.getSms();
        String sjhm = reqVO.getSjhm();
        Boolean aBoolean = stringRedisTemplate.hasKey(formatKey(sjhm+sms));
        if (GyUtils.isNull(aBoolean)||!aBoolean) {
            throw exception(OAUTH2_LOGIN_SJHM_NOT_EXISTS);
        }
        YhxxbDTO yhxx = yhxxService.getYhxxBySjhm(sjhm);
        if (GyUtils.isNull(yhxx)) {
            throw exception(OAUTH2_SJHM_NOT_EXISTS);
        }
        return createTokenAfterLoginSuccess(yhxx.getYhUuid());
    }

    @Override
	public AuthLoginRespVO didBindPhone(@Valid DidBindPhoneReqVO reqVO) {
        String sms = reqVO.getSms();
        String sjhm = reqVO.getSjhm();
        Boolean aBoolean = stringRedisTemplate.hasKey(formatKey(sjhm+sms));
        if (GyUtils.isNull(aBoolean)||!aBoolean) {
            throw exception(OAUTH2_LOGIN_SJHM_NOT_EXISTS);
        }
        String reqId =reqVO.getReqId() ; 
		// did用户注册
      //缓存did 。一次请求对应一个did 
		String tmp = String.format("%s%s", DidController.REQ_USER_DID_PREFIX,reqId);
		String holderDid =CacheUtils.getCacheData(tmp);
		if (GyUtils.isNull(holderDid)) {
            throw exception(GlobalErrorCodeConstants.ERROR.getCode(),"缓存did为空！");
        }
		//缓存did数据，为创建账号做准备
//		DidCredentialSubject cs = JsonUtils.toBean(csjson, DidCredentialSubject.class);
		tmp = String.format("%s%s", DidController.REQ_USER_DID_DATA_PREFIX,reqId);
//		CacheUtils.cacheData(tmp, JsonUtils.toJson(cs),timeout);
		String csJson =CacheUtils.getCacheData(tmp);
		if (GyUtils.isNull(csJson)) {
            throw exception(GlobalErrorCodeConstants.ERROR.getCode(),"缓存did数据为空！");
        }
		YhxxbDTO existYhxx = yhxxService.getYhxxBySjhm(sjhm);
		YhxxbDTO yhxx = null;
		DidCredentialSubject cs =JsonUtils.toBean(csJson,DidCredentialSubject.class );
		if (GyUtils.isNull(existYhxx)) {
			yhxx = new YhxxbDTO();
			yhxx.setYhUuid(IdUtil.fastSimpleUUID());
			yhxx.setDid(holderDid); 
			yhxx.setSjhm1(sjhm); 
			yhxx.setDlzh(sjhm);
			if (GyUtils.isNotNull(cs.getLegalName())) {
				yhxx.setZsxm1(cs.getLegalName());
			} 
			else if (GyUtils.isNotNull(cs.getEntname())) {
				yhxx.setZsxm1(cs.getEntname());
			} 
			else {
				yhxx.setZsxm1("--");
			}
			DidCallbackExtendInfo ext = cs.getExtInfo();
			if (GyUtils.isNotNull(ext)) {
				yhxx.setSfzjhm(ext.getAuthoriserIdCard());
			}
			yhxx = yhxxService.saveYhxxByDid(yhxx);
		} 
		else {
//			yhxx = existYhxx;
//			yhxx.setDid(holderDid); 
//			yhxxService.updateDid(yhxx);
			throw exception(GlobalErrorCodeConstants.ERROR.getCode(),"已经存在该手机号的账号信息！");
		}
		// 企业认证成功
		YhxxbDTO qyxx = new YhxxbDTO();
		qyxx.setQymc(cs.getEntname());
		qyxx.setNsrsbh(cs.getUniscid());
		qyxx.setYhUuid(yhxx.getYhUuid());
		log.info("qyxx:{}",qyxx);
		qyxx = yhxxService.intQyxxByDid(qyxx);
//        YhxxbDTO yhxx = yhxxService.getYhxxBySjhm(sjhm);
//        if (GyUtils.isNull(yhxx)) {
//            throw exception(OAUTH2_SJHM_NOT_EXISTS);
//        }
        return createTokenAfterLoginSuccess(yhxx.getYhUuid());
    
	}

	private AuthLoginRespVO createTokenAfterLoginSuccess(String userId) {
        // 创建访问令牌
        OAuth2AccessTokenDO accessTokenDO = oauth2TokenService.createAccessToken(userId, "default", null);
        // 构建返回结果
        final AuthLoginRespVO respVO = new AuthLoginRespVO();
        respVO.setAccessToken(accessTokenDO.getAccessToken());
        respVO.setRefreshToken(accessTokenDO.getRefreshToken());
        respVO.setExpiresTime(accessTokenDO.getGqsj());
        return respVO;
    }

    private boolean isPasswordMatch(String rawPassword, String encodedPassword) {
        final String rawPasswordHex = MD5.create().digestHex(rawPassword);
        return rawPasswordHex.equals(encodedPassword);
    }

    public String send(String sjhm,Map<String,String> param) throws Exception {
        // 初始化请求客户端
        Client client = SMSClient.createClient();

        // 构造请求对象，请填入请求参数值
        SendSmsRequest sendSmsRequest = new SendSmsRequest()
                .setPhoneNumbers(sjhm)
                .setSignName(SMS_CLIENT_SIGNNAME)
                .setTemplateCode(SMS_TEMPLATE_CODE)
                .setTemplateParam(JsonUtils.toJson(param));

        // 获取响应对象
        SendSmsResponse sendSmsResponse = client.sendSms(sendSmsRequest);

        // 响应包含服务端响应的 body 和 headers
        return toJSONString(sendSmsResponse);
    }

    private static String formatKey(String sms) {
        String VERIFY_TOKEN = "sms_token:%s";
        return String.format(VERIFY_TOKEN, sms);
    }

    private static String formatKeyPhone(String phone) {
        String PHONE_LOCK = "sms_token:%s";
        return String.format(PHONE_LOCK, phone);
    }

}
