package com.jkcredit.identity.service.Impl;

import cn.hutool.json.JSONUtil;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jkcredit.identity.constant.CommonConstants;
import com.jkcredit.identity.model.to.HandTO;
import com.jkcredit.identity.model.to.LoginTO;
import com.jkcredit.identity.model.vo.ApiResponse;
import com.jkcredit.identity.model.vo.HandVo;
import com.jkcredit.identity.model.vo.TokenVo;
import com.jkcredit.identity.service.TokenService;
import com.jkcredit.identity.util.TokenConCurrentHashMapUtil;
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.data.redis.core.StringRedisTemplate;
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.Map;

/**
 * @author xusonglin
 * @version V1.0
 **/
@Service
@Slf4j
public class TokenServiceImpl implements TokenService {
    @Value("${rz.info.username}")
    String username;
    @Value("${rz.info.password}")
    String password;
    @Value("${rz.info.url}")
    String url;

    private static String handUrl;
    private static String tokenUrl;
    @Autowired
    private UKeyService uKeyService;
    @Autowired
    private RestTemplate restTemplate;
    @Autowired
    StringRedisTemplate stringRedisTemplate;
    @Autowired
    ObjectMapper objectMapper;

    @PostConstruct
    public void init() {
        handUrl = url + "/ab/auth/hand";
        tokenUrl = url + "/ab/auth/token";
    }

    @Override
    public TokenVo getToken() {
        Map<String, TokenVo> tokenVoMap = TokenConCurrentHashMapUtil.getInstance();
        if (tokenVoMap.size() == 0) {
            return queryToken();
        } else {
            return tokenVoMap.get(CommonConstants.TOKEN_KEY);
        }
    }

    @Override
    public TokenVo queryToken() {
        log.info("**认证握手**");
        HandTO handTo = new HandTO();
        String timeStamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        handTo.setTimeStamp(timeStamp);

        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        // 握手阶段请求头需携带用户名
        headers.set("X-Main-Id", username);
        log.info("username:{}", username);
        log.info("password:{}", password);
        log.info("handUrl:{}", handUrl);
        log.info("handTo: {}", JSONUtil.toJsonPrettyStr(handTo));
        HttpEntity<HandTO> entity = new HttpEntity<>(handTo, headers);
        ResponseEntity<ApiResponse<HandVo>> exchange = restTemplate.exchange(handUrl, HttpMethod.POST, entity, new ParameterizedTypeReference<ApiResponse<HandVo>>() {
        });

        ApiResponse<HandVo> response = exchange.getBody();

        String svrRandom = "";
        if (response != null) {
            if (response.getCode() != 0) {
                log.info("err code: {}, err msg: {}", response.getCode(), response.getMsg());
                return null;
            }
            log.info("handVo: {}", JSONUtil.toJsonPrettyStr(response.getData()));
            svrRandom = response.getData().getSvrRandom();

        }
        log.info("**获取令牌**");
        LoginTO.BizData bizData = new LoginTO.BizData();
        bizData.setUsername(username);
        bizData.setCredentials(password);
        timeStamp = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));
        bizData.setTimeStamp(timeStamp);
        // 业务数据签名需设置上服务端随机数
        bizData.setR1(svrRandom);
        bizData.setR2(uKeyService.genRandomBytes(16));

        LoginTO loginTo = new LoginTO();
        loginTo.setBizData(bizData);
        String sign = "";
        try {
            sign = uKeyService.digestECCSign(objectMapper.writeValueAsString(bizData));
            log.info(sign);
        } catch (Exception e) {
            log.error("digestECCSign.Exception:", e);
            return null;
        }

        loginTo.setSign(sign);
        // 请求时不要携带随机数
        bizData.setR1(null);

        log.info("LoginTo: {}", JSONUtil.toJsonPrettyStr(loginTo));
        headers.clear();
        headers.setContentType(MediaType.APPLICATION_JSON);
        HttpEntity<LoginTO> entity2 = new HttpEntity<>(loginTo, headers);

        ResponseEntity<ApiResponse<TokenVo>> exchange2 = restTemplate.exchange(tokenUrl, HttpMethod.POST, entity2,
                new ParameterizedTypeReference<ApiResponse<TokenVo>>() {
                });

        ApiResponse<TokenVo> response2 = exchange2.getBody();

        if (response2 != null) {
            if (response2.getCode() != 0) {
                log.info("err code: {}, err msg: {}", response2.getCode(), response2.getMsg());
                return null;
            }

            TokenVo tokenVo = response2.getData();
            log.info("tokenVo: {}", JSONUtil.toJsonPrettyStr(tokenVo));
            tokenVo.setEncKey(uKeyService.envelopedDecrypt(tokenVo.getEncKey()));
            tokenVo.setUsername(username);
            TokenConCurrentHashMapUtil.updateToken(tokenVo);
            return tokenVo;
        } else {
            return null;
        }
    }
}
