package cokr.xit.ens.modules.common.ctgy.nicedici.service;

import cokr.xit.ens.core.aop.EnsResponseVO;
import cokr.xit.ens.core.exception.EnsException;
import cokr.xit.ens.core.exception.code.EnsErrCd;
import cokr.xit.ens.core.utils.CmmnUtil;
import cokr.xit.ens.modules.common.ctgy.nicedici.code.LinkType;
import cokr.xit.ens.modules.common.ctgy.nicedici.domain.NiceCiMng;
import cokr.xit.ens.modules.common.ctgy.nicedici.domain.NiceJidMng;
import cokr.xit.ens.modules.common.ctgy.nicedici.domain.repository.NiceCiMngRepository;
import cokr.xit.ens.modules.common.ctgy.nicedici.domain.repository.NiceJidMngRepository;
import cokr.xit.ens.modules.common.ctgy.nicedici.support.rest.NiceCiGenerator;
import cokr.xit.ens.modules.common.ctgy.nicedici.support.rest.utils.SymmetricKey;
import cokr.xit.ens.modules.common.ctgy.nicedici.support.socket.Interop;
import cokr.xit.ens.modules.common.domain.support.FieldError;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class NiceDiCiService {

    private final NiceJidMngRepository niceJidMngRepository;
    private final NiceCiMngRepository niceCiMngRepository;


    @Value("${contract.nice.dici.type}")
    private String type;
    @Value("${contract.nice.dici.socket.proxy.use}")
    private boolean isProxyUse;
    @Value("${contract.nice.dici.socket.proxy.origin}")
    private String proxyOrigin;
    @Value("${contract.nice.dici.socket.proxy.path}")
    private String proxyPath;

    private final NiceCiGenerator niceCiGenerator;


    /**
     * 주민번호로 CI를 취득 한다.
     * -.CI: 연계정보(Connecting Information)
     * -.국가표준 규격에 따라 사용자의 주민번호를 암호화한 개인 식별값.
     * -.서비스에 상관없이 값이 일정 함.
     * -.주민번호 -> 해쉬 -> CI 값 (88 byte)
     *
     * @param siteCode
     * @param sitePw
     * @param jids
     */
    @SuppressWarnings("deprecation")
//	@Transactional(noRollbackFor = Exception.class)
    @Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = Exception.class)
    public EnsResponseVO ci(String siteCode, String sitePw, String clientId, String clientSecret, List<String> jids) {
        LinkType type = LinkType.valueOf(this.type);
        switch (type) {
            case socket:
                return this.ci(type, siteCode, sitePw, jids);
            case rest:
                return this.ci(type, clientId, clientSecret, jids);
            default:
                return this.ci(LinkType.socket, siteCode, sitePw, jids);
        }
    }

    @Transactional(propagation = Propagation.REQUIRES_NEW, noRollbackFor = Exception.class)
    public EnsResponseVO ci(LinkType linkType, String id, String pw, List<String> jids) {

        EnsResponseVO responseVO = null;
        try {
            if (CmmnUtil.isEmpty(id))
                throw new EnsException(EnsErrCd.ERR401, "사이트코드(은)는 필수조건 입니다.");
            if (CmmnUtil.isEmpty(pw))
                throw new EnsException(EnsErrCd.ERR401, "사이트 패스워드(은)는 필수조건 입니다.");
            if (CmmnUtil.isEmpty(jids))
                throw new EnsException(EnsErrCd.ERR401, "서비스 구분값(주민번호:JID)(은)는 필수조건 입니다.");


            jids = jids.stream()
                    .map(jid -> jid.replaceAll("[^0-9]", ""))
                    .collect(Collectors.toList())
                    .stream()
                    .distinct()
                    .collect(Collectors.toList());
            AtomicInteger i = new AtomicInteger();
            List<EnsResponseVO> responseVOList = jids.stream()
                    .map(jid -> {
                        EnsResponseVO result = null;
                        String encJid = null;
                        String ci = null;
                        try {
                            try {
                                encJid = hexSha256(jid);
                            } catch (NoSuchAlgorithmException | IOException e) {
                                log.error("주민번호(%s) 단방향암호화(sha256) 실패:: %e", jid, e.getMessage(), e);
                            }

                            Optional<String> findCi = niceCiMngRepository.findCiByJid(encJid);
                            if (findCi.isPresent()) {
                                ci = findCi.get();
                                result = EnsResponseVO.okBuilder().build();

                            } else {
                                if (linkType.equals(LinkType.socket)) {
                                    if (isProxyUse) {
                                        result = Interop.getCIProxy(proxyOrigin, proxyPath, id, pw, jid);
                                    } else {
                                        result = Interop.getCI(id, pw, jid);
                                    }
                                } else {
                                    if (0 == i.getAndIncrement())
                                        if (!SymmetricKey.isValidStat(id))
                                            niceCiGenerator.initialKey(id, pw);
                                    result = niceCiGenerator.getCI(jid, null, id, pw);
                                }

                                ci = EnsErrCd.OK.equals(result.getErrCode()) ? String.valueOf(result.getResultInfo()) : null;
                                return result;
                            }

                        } catch (Exception e) {
                            result = EnsResponseVO.errBuilder()
                                    .errCode(EnsErrCd.ERR904)
                                    .errMsg(String.format("%s. %s", EnsErrCd.ERR904, e.getMessage()))
                                    .build();

                        } finally {
                            Map<String, String> resultInfo = new HashMap<>();
                            resultInfo.put("jid", jid);
                            resultInfo.put("ci", ci);
                            if (EnsErrCd.OK.equals(result.getErrCode()))
                                return EnsResponseVO.<Map<String, String>>okBuilder().resultInfo(resultInfo).build();
                            else
                                return EnsResponseVO.<Map<String, String>>errRsltBuilder()
                                        .errCode(result.getErrCode())
                                        .errMsg(result.getErrMsg())
                                        .resultInfo(resultInfo)
                                        .build();
                        }
                    })
                    .collect(Collectors.toList());



            List<NiceCiMng> niceCiMngs = responseVOList.stream()
                    .map(row -> {
                        Map<String, String> resultInfo = (Map<String, String>) row.getResultInfo();
                        String encJid = null;
                        String ci = resultInfo.get("ci");
                        try {
                            String onlyNumJid = resultInfo.get("jid").replaceAll("[^0-9]", "");
                            encJid = hexSha256(onlyNumJid);
                        } catch (NoSuchAlgorithmException | IOException e) {
                            log.error("주민번호(%s) 단방향암호화(sha256) 실패:: %e", resultInfo.get("jid"), e.getMessage(), e);
                        }
                        NiceJidMng niceJidMng = NiceJidMng.builder()
                                .jid(encJid)
                                .error(FieldError.initBuilder()
                                        .errorCode(row.getErrCode().getCode())
                                        .errorMessage(row.getErrMsg())
                                        .build())
                                .build();
                        return NiceCiMng.builder()
                                .ci(ci)
                                .niceJidMng(niceJidMng)
                                .build();
                    })
                    .collect(Collectors.toList());
            niceJidMngRepository.saveAll(niceCiMngs.stream()
                    .map(row -> row.getNiceJidMng())
                    .collect(Collectors.toList()));
            niceJidMngRepository.flush();
            niceCiMngRepository.saveAll(niceCiMngs);
            niceCiMngRepository.flush();


            responseVO = EnsResponseVO.okBuilder().resultInfo(responseVOList).build();

        } catch (EnsException e) {
            responseVO = EnsResponseVO.errBuilder()
                    .errCode(e.getErrCd())
                    .errMsg(e.getMessage())
                    .build();
        }


        return responseVO;

    }


    /**
     * sha256 암호화
     *
     * @param text
     * @return
     * @throws IOException
     * @throws NoSuchAlgorithmException
     */
    public static String hexSha256(String text) throws IOException, NoSuchAlgorithmException {
        StringBuffer sbuf = new StringBuffer();

        MessageDigest mDigest = MessageDigest.getInstance("SHA-256");
        mDigest.update(text.getBytes());

        byte[] msgStr = mDigest.digest();

        for (int i = 0; i < msgStr.length; i++) {
            byte tmpStrByte = msgStr[i];
            String tmpEncTxt = Integer.toString((tmpStrByte & 0xff) + 0x100, 16).substring(1);

            sbuf.append(tmpEncTxt);
        }

        return sbuf.toString();
    }


}
