package com.jkcredit.identity.service.Impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jkcredit.identity.constant.AuthConstant;
import com.jkcredit.identity.constant.ResponseConstants;
import com.jkcredit.identity.model.dto.AuthDataDTO;
import com.jkcredit.identity.model.dto.IdentityInfoDTO;
import com.jkcredit.identity.model.dto.IdentityPhotoInfoDTO;
import com.jkcredit.identity.model.to.AuthDataTO;
import com.jkcredit.identity.model.vo.*;
import com.jkcredit.identity.service.IdentityVerifyService;
import com.jkcredit.identity.service.TokenService;
import com.jkcredit.identity.util.DESUtil;
import com.zdab.ukey.service.UKeyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import javax.annotation.PostConstruct;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Base64;

/**
 * @author xusonglin
 * @version V1.0
 **/
@Service
@Slf4j
public class IdentityVerifyServiceImpl implements IdentityVerifyService {
    @Value("${rz.info.username}")
    String username;
    @Value("${rz.info.password}")
    String password;
    @Value("${rz.info.url}")
    String url;
    @Value("${rz.info.authMode-0x40}")
    String authMode40;
    @Value("${rz.info.authMode-0x42}")
    String authMode42;
    @Value("${aes.secret}")
    String secret;

    private static String rzUrl;
    private static byte[] serverPubKey;
    @Autowired
    private UKeyService uKeyService;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    private ObjectMapper objectMapper;

    @Autowired
    TokenService tokenService;

    /**
     * 初始化方法
     */
    @PostConstruct
    public void init() {
        serverPubKey = uKeyService.getCertPubKey(Base64.getDecoder().decode(AuthConstant.SERVER_CERT)); // 证书中提取公钥
        rzUrl = url + "/ab/rzfw";
    }

    @Override
    public CommonResponseVO validateIdentityInfo(String params) {
        JSONObject requestParam = JSON.parseObject(params);
        String decryptParams;
        if (requestParam.get("params") != null) {
            decryptParams = DESUtil.decrypt(requestParam.getString("params"), secret);
        } else {
            return new CommonResponseVO().failed(ResponseConstants.RESPONSE_RESULT_FAILED,
                    ResponseConstants.RESPONSE_MESSAGE_DECRYPT_FAILED);
        }
        IdentityInfoDTO identityInfoDTO = JSON.toJavaObject(JSON.parseObject(decryptParams), IdentityInfoDTO.class);
        TokenVo tokenVo = tokenService.getToken();
        String token = tokenVo.getToken();
        String encKey = tokenVo.getEncKey();
        AuthDataDTO authDataDto = new AuthDataDTO();
        authDataDto.setName(identityInfoDTO.getName());
        authDataDto.setIdCard(identityInfoDTO.getIdCode());
        authDataDto.setAuthMode(authMode40);

        CommonResponseVO responseVO = rzAuth(authDataDto, token, encKey);
        log.info("name:{}, idCode:{}, requestId:{} mode:{}, result:{}", identityInfoDTO.getName(),
                identityInfoDTO.getIdCode(), identityInfoDTO.getRequestId(),
                authMode40, JSON.toJSONString(responseVO));
        return responseVO;
    }

    @Override
    public CommonResponseVO validatePhotoIdentityInfo(String params) {
        JSONObject requestParam = JSON.parseObject(params);
        String decryptParams;
        if (requestParam.get("params") != null) {
            decryptParams = DESUtil.decrypt(requestParam.getString("params"), secret);
        } else {
            return new CommonResponseVO().failed(ResponseConstants.RESPONSE_RESULT_FAILED,
                    ResponseConstants.RESPONSE_MESSAGE_DECRYPT_FAILED);
        }
        IdentityPhotoInfoDTO identityInfoDTO = JSON.toJavaObject(JSON.parseObject(decryptParams), IdentityPhotoInfoDTO.class);
        TokenVo tokenVo = tokenService.getToken();
        String token = tokenVo.getToken();
        String encKey = tokenVo.getEncKey();
        AuthDataDTO authDataDto = new AuthDataDTO();
        authDataDto.setName(identityInfoDTO.getName());
        authDataDto.setIdCard(identityInfoDTO.getIdCode());
        authDataDto.setAuthMode(authMode42);
        authDataDto.setPhotoData(identityInfoDTO.getPhoto());

        CommonResponseVO responseVO = rzAuth(authDataDto, token, encKey);
        log.info("name:{}, idCode:{}, requestId:{} mode:{}, result:{}", identityInfoDTO.getName(),
                identityInfoDTO.getIdCode(), identityInfoDTO.getRequestId(),
                authMode42, JSON.toJSONString(responseVO));
        return responseVO;
    }

    public CommonResponseVO rzAuth(AuthDataDTO authDataDto, String token, String encKey) {
        log.info("**人证服务**");
        String name = authDataDto.getName();
        String idCard = authDataDto.getIdCard();
        String authMode = authDataDto.getAuthMode();
        String photoData = authDataDto.getPhotoData();

        byte[] keyBytes = Base64.getDecoder().decode(encKey);
        if (keyBytes.length != 16) {
            return new CommonResponseVO().failed(ResponseConstants.RESPONSE_RESULT_FAILED,
                    ResponseConstants.RESPONSE_MESSAGE_ERROR_EX_KEY);
        }

        AuthDataTO.BizData.AuthData authData = new AuthDataTO.BizData.AuthData();
        authData.setName(name);
        authData.setIdCard(idCard);
        if (authMode42.equals(authMode)) {
            authData.setPhotoData(photoData); //0x42模式
        }

        // 加密认证数据
        String s;
        try {
            s = objectMapper.writeValueAsString(authData);
        } catch (JsonProcessingException jpe) {
            log.error("{}:{}", ResponseConstants.RESPONSE_MESSAGE_JSON_PARSE_FAILED, jpe);
            return new CommonResponseVO().failed(ResponseConstants.RESPONSE_RESULT_FAILED,
                    ResponseConstants.RESPONSE_MESSAGE_JSON_PARSE_FAILED);
        }

        String authDataEnc = uKeyService.sm4Encrypt(encKey, s);

        AuthDataTO.BizData bizData = new AuthDataTO.BizData();
        bizData.setAuthData(authDataEnc);
        bizData.setAuthMode(authMode);
        String timeStamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        bizData.setTimeStamp(timeStamp);

        AuthDataTO authDataTo = new AuthDataTO();
        authDataTo.setBizData(bizData);
        // 业务数据签名
        try {
            authDataTo.setSign(uKeyService.digestECCSign(objectMapper.writeValueAsString(bizData)));
        } catch (JsonProcessingException jpe) {
            log.error("{}:{}", ResponseConstants.RESPONSE_MESSAGE_JSON_PARSE_FAILED, jpe);
            return new CommonResponseVO().failed(ResponseConstants.RESPONSE_RESULT_FAILED,
                    ResponseConstants.RESPONSE_MESSAGE_JSON_PARSE_FAILED);
        }

        HttpHeaders headers = new HttpHeaders();
        headers.setBearerAuth(token);
        headers.setContentType(MediaType.APPLICATION_JSON);

//        log.info("authDataTo: {}", JSONUtil.toJsonPrettyStr(authDataTo));
        HttpEntity<AuthDataTO> entity = new HttpEntity<>(authDataTo, headers);

        ResponseEntity<ApiResponse<AuthRepData>> exchange = restTemplate.exchange(rzUrl, HttpMethod.POST, entity, new ParameterizedTypeReference<ApiResponse<AuthRepData>>() {
        });

        ApiResponse<AuthRepData> response = exchange.getBody();
        log.info("ApiResponse<AuthRepData> response:{}", JSON.toJSONString(response));
        if (response != null) {
            if (response.getCode() != 0) {
                return new CommonResponseVO().failed(ResponseConstants.RESPONSE_RESULT_FAILED,
                        response.getMsg());
            }

            AuthRepData data = response.getData();


            String sign = data.getSign();
            AuthRepData.BizData retBizData = data.getBizData();

            AuthSignData authSignData = new AuthSignData();
            authSignData.setAuthResult(retBizData.isAuthResult());
            authSignData.setTimeStamp(timeStamp);
            authSignData.setUsername(username);
            authSignData.setName(name);
            authSignData.setIdCard(idCard);

            boolean verify;
            try {
                verify = uKeyService.digestECCVerify(serverPubKey, objectMapper.writeValueAsString(authSignData), sign);
            } catch (JsonProcessingException jpe) {
                log.error("{}:{}", ResponseConstants.RESPONSE_MESSAGE_JSON_PARSE_FAILED, jpe);
                return new CommonResponseVO().failed(ResponseConstants.RESPONSE_RESULT_FAILED,
                        ResponseConstants.RESPONSE_MESSAGE_JSON_PARSE_FAILED);
            }

            if (!verify) {
                return new CommonResponseVO().failed(ResponseConstants.RESPONSE_RESULT_FAILED,
                        ResponseConstants.RESPONSE_MESSAGE_ERROR_SIGN);
            }

            return new CommonResponseVO().success(String.valueOf(retBizData.isAuthResult()),
                    ResponseConstants.RESPONSE_MESSAGE_SUCCESS);
        } else {
            return new CommonResponseVO().failed(ResponseConstants.RESPONSE_RESULT_FAILED,
                    ResponseConstants.RESPONSE_MESSAGE_REQUEST_FAILED);
        }

    }
}
