package kr.xit.ens.kakao.pay.service;

import kr.xit.biz.common.*;
import kr.xit.biz.ens.model.kakao.pay.*;
import kr.xit.core.exception.BizRuntimeException;
import kr.xit.core.spring.annotation.TraceLogging;
import kr.xit.core.spring.util.*;
import kr.xit.core.support.utils.Checks;
import kr.xit.core.support.utils.JsonUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import org.apache.commons.lang3.StringUtils;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;

import java.util.*;
import java.util.stream.Collectors;

/**
 * <pre>
 * description : 카카오 페이 전자 문서 API dummy 테스트
 * packageName : kr.xit.ens.kakao.pay.service
 * fileName    : KkopayApiDummyTestService
 * author      : julim
 * date        : 2023-04-28
 * ======================================================================
 * 변경일         변경자        변경 내용
 * ----------------------------------------------------------------------
 * 2023-04-28    julim       최초 생성
 *
 * </pre>
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class KkopayApiDummyTestService extends EgovAbstractServiceImpl {

    // FIXME:: API 호출 모듈 선택 - ApiWebClientUtil | ApiRestTemplateUtil | ApiHttpClientUtil
    private final ApiHttpClientUtil webClient;

    public KkopayDocAttrDTO.DocumentBinderUuid requestSend(final KkopayDocDTO.SendRequest reqDTO) {
        List<String> errors = new ArrayList<>();

        final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        // 실제 데이타만 검증
        Set<ConstraintViolation<KkopayDocDTO.RequestSend>> list = validator.validate(reqDTO.getDocument());
        if (list.size() > 0) {

            errors = list.stream()
                .map(row -> String.format("%s=%s", row.getPropertyPath(), row.getMessageTemplate()))
                .collect(Collectors.toList());
        }

        KkopayDocDTO.RequestSend reqSendDTO = reqDTO.getDocument();
        if(reqSendDTO.getRead_expired_at() != null && reqSendDTO.getRead_expired_sec() != null){
            errors.add("처리마감시간(절대시간 또는 상대시간)은 하나만 지정해야 합니다.");
        }
        if(reqSendDTO.getRead_expired_at() == null && reqSendDTO.getRead_expired_sec() == null){
            errors.add("처리마감시간(절대시간 또는 상대시간)을 지정해야 합니다.");
        }

        KkopayDocAttrDTO.Receiver receiver = reqSendDTO.getReceiver();
        if(Checks.isEmpty(receiver.getCi())){
            if(Checks.isEmpty(receiver.getName()))          errors.add("받는이 이름은 필수입니다.");
            if(Checks.isEmpty(receiver.getPhone_number()))  errors.add("받는이 전화번호는 필수입니다.");
            if(Checks.isEmpty(receiver.getBirthday()))      errors.add("받는이 생년월일은 필수입니다.");
        }
        if(errors.size() > 0){
            throw BizRuntimeException.create(errors.toString());
        }

        return KkopayDocAttrDTO.DocumentBinderUuid
            .builder()
            .document_binder_uuid("BIN-eue7e73uw737377eeeee")
            .build();
    }

    /**
     * <pre>
     * 토큰 유효성 검증(Redirect URL  접속 허용/불허)
     * </pre>
     * @param reqDTO KkoPayEltrDocDTO.RequestSendReq
     * @return ResponseEntity<KkopayDocDTO.ValidTokenRes></KkopayDocDTO.ValidTokenRes>
     */
    public KkopayDocDTO.ValidTokenResponse validToken(final KkopayDocDTO.ValidTokenRequest reqDTO) {
        final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
        // 실제 데이타만 검증
        Set<ConstraintViolation<KkopayDocDTO.ValidTokenRequest>> list = validator.validate(reqDTO);
        if (list.size() > 0) {

            List<String> errors = list.stream()
                .map(row -> String.format("%s=%s", row.getPropertyPath(), row.getMessageTemplate()))
                .collect(Collectors.toList());

            throw BizRuntimeException.create(errors.toString());
        }

        return KkopayDocDTO.ValidTokenResponse
            .builder()
            .token_status("USED")
            .token_expires_at(1624344762L)
            .payload("payload 파라미터 입니다.")
            .build();
    }


    /**
     * <pre>
     * 모바일웹 연계 문서발송 요청
     * -.이용기관 서버에서 전자문서 서버로 문서발송 처리를 요청합니다.
     * </pre>
     * @param reqDTO KkoPayEltrDocDTO.RequestSendReq
     * @return
     */
    public KkopayDocBulkDTO.BulkSendResponses requestSendBulk(final KkopayDocBulkDTO.BulkSendRequests reqDTO) {
        List<String> errors = new ArrayList<>();
        final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();

        //TODO :: Collection validation
        List<KkopayDocBulkDTO.BulkSendReq> dtos = reqDTO.getDocuments();
        for(int idx = 0; idx < dtos.size(); idx++) {
            Set<ConstraintViolation<KkopayDocBulkDTO.BulkSendReq>> list = validator.validate(dtos.get(idx));
            if (list.size() > 0) {

                int finalIdx = idx;
                errors.addAll(list.stream()
                    .map(row -> String.format("%s[%d]=%s", row.getPropertyPath(), finalIdx +1, row.getMessageTemplate()))
                    .collect(Collectors.toList())
                );
            }
        }

        for(int idx = 0; idx < dtos.size(); idx++) {

            if(dtos.get(idx).getRead_expired_at() != null && dtos.get(idx).getRead_expired_sec() != null){
                errors.add("처리마감시간(절대시간 또는 상대시간)은 하나만 지정해야 합니다.");
            }
            if(dtos.get(idx).getRead_expired_at() == null && dtos.get(idx).getRead_expired_sec() == null){
                errors.add("처리마감시간(절대시간 또는 상대시간)을 지정해야 합니다.");
            }

            KkopayDocAttrDTO.Receiver receiver = dtos.get(idx).getReceiver();
            if (Checks.isEmpty(receiver.getCi())) {
                if (Checks.isEmpty(receiver.getName()))         errors.add(String.format("받는이 이름은 필수입니다([%d] 번째 오류)", idx+1));
                if (Checks.isEmpty(receiver.getPhone_number())) errors.add(String.format("받는이 전화번호는 필수입니다([%d] 번째 오류)", idx+1));
                if (Checks.isEmpty(receiver.getBirthday()))     errors.add(String.format("받는이 생년월일은 필수입니다([%d] 번째 오류)", idx+1));
            } else {
                StringBuilder sb = new StringBuilder()
                    .append(StringUtils.defaultString(receiver.getName(), StringUtils.EMPTY))
                    .append(StringUtils.defaultString(receiver.getPhone_number(), StringUtils.EMPTY))
                    .append(StringUtils.defaultString(receiver.getBirthday(), StringUtils.EMPTY));

                if(Checks.isNotEmpty(sb.toString())){
                    errors.add(String.format("CI가 지정 되었습니다(받는이 정보 불필요:[%d] 번째 오류) .", idx+1));
                }
            }
        }
        if(errors.size() > 0){
            throw BizRuntimeException.create(errors.toString());
        }

        List<KkopayDocBulkDTO.BulkSendRes> resDTO = new ArrayList<>();
        resDTO.add(KkopayDocBulkDTO.BulkSendRes.builder()
            .external_document_uuid(reqDTO.getDocuments().get(0).getProperty().getExternal_document_uuid())
            .document_binder_uuid("BIN-127de7hdcyeuudfeuuewe8e8e")
            .build()
        );
        resDTO.add(KkopayDocBulkDTO.BulkSendRes.builder()
                .external_document_uuid(reqDTO.getDocuments().get(0).getProperty().getExternal_document_uuid())
                .error_code(ApiConstants.Error.NOT_FOUND.getCode())
                .error_message("요청 정보를 찾을 수 없습니다. documentBinder를 찾을수 없습니다.")
                .build()
        );

        KkopayDocBulkDTO.BulkSendResponses res = KkopayDocBulkDTO.BulkSendResponses.builder()
            .documents(resDTO)
            .build();

        KkopayDocBulkDTO.BulkSendResponses object = JsonUtils.toObject(JsonUtils.toJson(res),
            KkopayDocBulkDTO.BulkSendResponses.class);
        return res;
    }

    /**
     * <pre>
     * 대량(bulk) 문서 상태 조회 API
     * -.이용기관 서버에서 카카오페이 전자문서 서버로 문서 상태에 대한 조회를 요청 합니다.
     * : 발송된 문서의 진행상태를 알고 싶은 경우, flow와 상관없이 요청 가능
     * : polling 방식으로 호출할 경우, 호출 간격은 5초를 권장.
     * -.doc_box_status 상태변경순서
     * : SENT(송신) > RECEIVED(수신) > READ(열람)/EXPIRED(미열람자료의 기한만료)
     * </pre>
     * @param reqDTO
     * @return KkopayDocBulkDTO.BulkStatusResponse
     */
    public KkopayDocBulkDTO.BulkStatusResponses findBulkStatus(final KkopayDocBulkDTO.BulkStatusRequests reqDTO) {
        List<String> errors = new ArrayList<>();

        List<String> dtos = reqDTO.getDocument_binder_uuids();
        for(int idx = 0; idx < dtos.size(); idx++) {
            String binderUuid = dtos.get(idx);
            if (Checks.isEmpty(binderUuid) || binderUuid.length() > 40) {
                errors.add(String.format("문서 식별 번호는 40자를 넘을 수 없습니다[%d번째]", idx+1));
            }
        }
        if(errors.size() > 0) {
            throw BizRuntimeException.create(errors.toString());
        }

        List<KkopayDocBulkDTO.BulkStatus> resDTO = new ArrayList<>();

        resDTO.add(KkopayDocBulkDTO.BulkStatus.builder()
            .document_binder_uuid(reqDTO.getDocument_binder_uuids().get(0))
            .error_code(ApiConstants.Error.NOT_FOUND.getCode())
            .error_message("요청 정보를 찾을 수 없습니다. documentBinder를 찾을수 없습니다.")
            .build()
        );

        resDTO.add(KkopayDocBulkDTO.BulkStatus.builder()
            .document_binder_uuid(reqDTO.getDocument_binder_uuids().get(1))
            .status_data(KkopayDocAttrDTO.DocStatus.builder()
                .doc_box_status(ApiConstants.KkopayDocStatus.RECEIVED)
                .doc_box_sent_at(1443456743L)
                .doc_box_received_at(1443456743L)
                .user_notified_at(1443456743L)
                .build())
            .build()
        );

        resDTO.add(KkopayDocBulkDTO.BulkStatus.builder()
            .document_binder_uuid(reqDTO.getDocument_binder_uuids().get(2))
            .status_data(KkopayDocAttrDTO.DocStatus.builder()
                .doc_box_status(ApiConstants.KkopayDocStatus.READ)
                .doc_box_sent_at(1443456743L)
                .doc_box_received_at(1443456743L)
                .doc_box_read_at(1443456743L)
                .authenticated_at(1443456743L)
                .token_used_at(1443456743L)
                .user_notified_at(1443456743L)
                .build())
            .build()
        );

        KkopayDocBulkDTO.BulkStatusResponses res = KkopayDocBulkDTO.BulkStatusResponses.builder()
            .documents(resDTO)
            .build();

        KkopayDocBulkDTO.BulkStatusResponses object = JsonUtils.toObject(JsonUtils.toJson(res),
            KkopayDocBulkDTO.BulkStatusResponses.class);
        return res;
    }
}

