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

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

import javax.validation.*;

import org.apache.commons.lang3.*;
import org.egovframe.rte.fdl.cmmn.*;
import org.springframework.stereotype.*;

import kr.xit.biz.common.*;
import kr.xit.biz.ens.model.kakao.pay.*;
import kr.xit.biz.ens.model.kakao.talk.*;
import kr.xit.core.exception.*;
import kr.xit.core.model.*;
import kr.xit.core.spring.annotation.*;
import kr.xit.core.spring.util.*;
import kr.xit.core.support.utils.*;
import lombok.*;
import lombok.extern.slf4j.*;

/**
 * <pre>
 * description : 카카오톡 전자 문서 API dummy 테스트
 * packageName : kr.xit.ens.kakao.talk.service
 * fileName    : KkotalkApiDummyTestService
 * author      : julim
 * date        : 2024-12-17
 * ======================================================================
 * 변경일         변경자        변경 내용
 * ----------------------------------------------------------------------
 * 2024-12-17    julim       최초 생성
 *
 * </pre>
 */
@Slf4j
@RequiredArgsConstructor
@Component
public class KkotalkApiDummyTestService extends EgovAbstractServiceImpl {

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

    private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator();
    private static final CharSequence ENVELOPE_ID = "{ENVELOPE_ID}";


    public KkotalkApiDTO.SendResponse requestSend(final KkotalkDTO.SendRequest reqDTO) {
        if(Checks.isEmpty(reqDTO.getProductCode())){
            throw BizRuntimeException.create("상품 코드는 필수 입니다.");
        }
        List<String> errors = new ArrayList<>();
        errors = validate(reqDTO.getEnvelope(), errors);

        final KkotalkApiDTO.Envelope envelope = reqDTO.getEnvelope();
        if(envelope.getReviewExpiresAt() != null){
            if(envelope.getReviewExpiresAt().compareTo(envelope.getReadExpiresAt()) < 0){
                errors.add("reviewExpiresAt=재열람 만료일시를 최조 열람 만료일시 보다 큰 날짜로 입력해주세요.");
            }
        }

        if(Checks.isEmpty(envelope.getCi())){
            if(Checks.isEmpty(envelope.getName()))          Objects.requireNonNull(errors).add("name=받는이 이름은 필수입니다.");
            if(Checks.isEmpty(envelope.getPhoneNumber()))   Objects.requireNonNull(errors).add("phoneNumber=받는이 전화번호는 필수입니다.");
            if(Checks.isEmpty(envelope.getBirthday()))      Objects.requireNonNull(errors).add("birthday=받는이 생년월일은 필수입니다.");
        }
        if(!Objects.requireNonNull(errors).isEmpty()) throw BizRuntimeException.create(errors.toString());


        return KkotalkApiDTO.SendResponse
            .builder()
            .envelopeId("EVLP-01JAF27KSMDYSZ6JP1R66SV2ZC-06")
            .externalId("41220202410170021")
            .build();
    }

    /**
     * <pre>
     * 토큰 유효성 검증(Redirect URL  접속 허용/불허)
     * </pre>
     * @param reqDTO KkoPayEltrDocDTO.RequestSendReq
     * @return KkotalkApiDTO.ValidTokenResponse
     */
    public KkotalkApiDTO.ValidTokenResponse validToken(final KkotalkApiDTO.ValidTokenRequest reqDTO) {
        validate(reqDTO, null);

        return KkotalkApiDTO.ValidTokenResponse
            .builder()
            .status(ApiConstants.KkotalkDocStatus.READ) //"USED"
            //.token_expires_at(1624344762L)
            .payload("payload 파라미터 입니다.")
            .build();
    }


    /**
     * <pre>
     * 모바일웹 연계 문서발송 요청
     * -.이용기관 서버에서 전자문서 서버로 문서발송 처리를 요청합니다.
     * </pre>
     * @param reqDTO KkotalkDTO.BulkSendRequest
     * @return KkotalkDTO.BulkSendResponse
     */
    public KkotalkDTO.BulkSendResponse requestSendBulk(final KkotalkDTO.BulkSendRequest reqDTO) {
        if(Checks.isEmpty(reqDTO.getProductCode())){
            throw BizRuntimeException.create("상품 코드는 필수 입니다.");
        }

        List<String> errors = new ArrayList<>();

        List<KkotalkApiDTO.Envelope> envelopes = reqDTO.getEnvelopes();
        for(int idx = 0; idx < envelopes.size(); idx++) {
            final Set<ConstraintViolation<KkotalkApiDTO.Envelope>> list = validator.validate(envelopes.get(idx));
            if (!list.isEmpty()) {

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

        for(int idx = 0; idx < envelopes.size(); idx++) {
            final KkotalkApiDTO.Envelope envelope = envelopes.get(idx);

            if(envelope.getReviewExpiresAt() != null){
                if(envelope.getReviewExpiresAt().compareTo(envelope.getReadExpiresAt()) < 0){
                    errors.add("reviewExpiresAt=재열람 만료일시를 최조 열람 만료일시 보다 큰 날짜로 입력해주세요.");
                }
            }

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

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


        List<KkotalkApiDTO.EnvelopeRes> resDTO = new ArrayList<>();
        resDTO.add(KkotalkApiDTO.EnvelopeRes.builder()
            .externalId(envelopes.get(0).getExternalId())
            .errorCode(ApiConstants.Error.NOT_FOUND.getCode())
            .errorMessage("요청 정보를 찾을 수 없습니다. documentBinder를 찾을수 없습니다.")
            .build()
        );
        
        resDTO.add(KkotalkApiDTO.EnvelopeRes.builder()
            .externalId(envelopes.get(1).getExternalId())
            .envelopeId("EVLP-01JAF27KSMDYSZ6JP1R66SV2ZC-06")
            .edocGtid(null)
            .build()
        );

        resDTO.add(KkotalkApiDTO.EnvelopeRes.builder()
            .externalId(envelopes.get(2).getExternalId())
            .envelopeId("EVLP-01J5YX9QNMS3Y8D3QZTB51NMMN-00")
            .edocGtid(null)
            .build()
        );
        
        KkotalkDTO.BulkSendResponse res = KkotalkDTO.BulkSendResponse.builder()
            .envelopes(resDTO)
            .build();

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


    public ApiResponseDTO<?> requestSendBulkError(final KkotalkDTO.BulkSendRequest reqDTO) {
        if(Checks.isEmpty(reqDTO.getProductCode())){
            throw BizRuntimeException.create("상품 코드는 필수 입니다.");
        }

        List<String> errors = new ArrayList<>();

        List<KkotalkApiDTO.Envelope> envelopes = reqDTO.getEnvelopes();
        for(int idx = 0; idx < envelopes.size(); idx++) {
            final Set<ConstraintViolation<KkotalkApiDTO.Envelope>> list = validator.validate(envelopes.get(idx));
            if (!list.isEmpty()) {

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

        for(int idx = 0; idx < envelopes.size(); idx++) {
            final KkotalkApiDTO.Envelope envelope = envelopes.get(idx);

            if(envelope.getReviewExpiresAt() != null){
                if(envelope.getReviewExpiresAt().compareTo(envelope.getReadExpiresAt()) < 0){
                    errors.add("reviewExpiresAt=재열람 만료일시를 최조 열람 만료일시 보다 큰 날짜로 입력해주세요.");
                }
            }

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

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


        String errorRtn = """
            {"timestamp":"2024-12-16T01:19:57.040+00:00","path":"/pxy/kkoNew/v1/bulk/envelopes/D10_2","status":500,"error":"Internal Server Error","requestId":"541faefe-45126"}
            """;
        
        return ApiResponseDTO.success(errorRtn);
    }
    
    /**
     * <pre>
     * 대량(bulk) 문서 상태 조회 API
     * -.이용기관 서버에서 카카오페이 전자문서 서버로 문서 상태에 대한 조회를 요청 합니다.
     * : 발송된 문서의 진행상태를 알고 싶은 경우, flow와 상관없이 요청 가능
     * : polling 방식으로 호출할 경우, 호출 간격은 5초를 권장.
     * -.doc_box_status 상태변경순서
     * : SENT(송신) > RECEIVED(수신) > READ(열람)/EXPIRED(미열람자료의 기한만료)
     * </pre>
     * @param reqDTO KkotalkDTO.BulkStatusRequest
     * @return KkotalkDTO.BulkStatusResponse
     */
    public KkotalkDTO.BulkStatusResponse findBulkStatus(final KkotalkDTO.BulkStatusRequest reqDTO) {
        List<String> errors = new ArrayList<>();

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


        List<KkotalkApiDTO.EnvelopeStatusResponse> resDTO = new ArrayList<>();

        resDTO.add(KkotalkApiDTO.EnvelopeStatusResponse.builder()
            .envelopeId(reqDTO.getEnvelopes().get(0))
            .errorCode(ApiConstants.Error.NOT_FOUND.getCode())
            .errorMessage("요청 정보를 찾을 수 없습니다. documentBinder를 찾을수 없습니다.")
            .build()
        );

        resDTO.add(KkotalkApiDTO.EnvelopeStatusResponse.builder()
            .envelopeId(reqDTO.getEnvelopes().get(1))
            .externalId("ddhhdhdhdhdhdhd")
            .status(ApiConstants.KkotalkDocStatus.RECEIVED)
            .sentAt("2024-08-16T15:24:39")
            .receivedAt("2024-08-16T15:24:39")
            .authenticatedAt("2024-08-30T11:14:33")
            .isNotificationUnavailable(false)
            .userNotifiedAt("2024-08-16T15:24:47")
            .receivedAt("2024-09-30T23:59:59")
            .build()
        );

        resDTO.add(KkotalkApiDTO.EnvelopeStatusResponse.builder()
            .envelopeId(reqDTO.getEnvelopes().get(2))
            .externalId("ddhhdhdhdhdhdhd")
            .status(ApiConstants.KkotalkDocStatus.RECEIVED)
            .sentAt("2024-08-16T15:24:39")
            .receivedAt("2024-08-16T15:24:39")
            .authenticatedAt("2024-08-30T11:14:33")
            .isNotificationUnavailable(false)
            .userNotifiedAt("2024-08-16T15:24:47")
            .receivedAt("2024-09-30T23:59:59")
            .build()
        );

        KkotalkDTO.BulkStatusResponse res = KkotalkDTO.BulkStatusResponse.builder()
            .envelopeStatus(resDTO)
            .build();

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

    //-------------------------------------------------------------------------------------------------------------------
    private static <T> List<String> validate(T t, List<String> errList) {
        final Set<ConstraintViolation<T>> list = validator.validate(t);

        if(!list.isEmpty()) {
            final List<String> errors = list.stream()
                .map(row -> String.format("%s=%s", row.getPropertyPath(), row.getMessageTemplate()))
                .toList();

            // 추가적인 유효성 검증이 필요 없는 경우
            if(errList == null){
                if(!errors.isEmpty())   throw BizRuntimeException.create(errors.toString());
                return null;
            }
            errList.addAll(errors);
        }
        return errList;
    }
}

