package go.kr.project.api.service.impl;

import egovframework.exception.MessageException;
import go.kr.project.api.config.ApiConstant;
import go.kr.project.api.config.properties.VmisProperties;
import go.kr.project.api.model.*;
import go.kr.project.api.service.ExternalVehicleApiService;
import go.kr.project.api.util.ExceptionDetailUtil;
import go.kr.project.api.model.request.OldBasicRequest;
import go.kr.project.api.model.request.NewBasicRequest;
import go.kr.project.api.model.request.NewLedgerRequest;
import go.kr.project.api.model.response.*;
import go.kr.project.api.service.VmisCarBassMatterInqireLogService;
import go.kr.project.api.service.VmisCarLedgerFrmbkLogService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.http.*;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;

import java.util.List;
import java.util.stream.Collectors;

/**
 * 외부 VMIS-interface API 호출 서비스 구현체
 * VMIS-interface 프로그램의 API를 호출하여 차량 정보를 조회하는 서비스
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ExternalVehicleApiServiceImpl extends EgovAbstractServiceImpl implements ExternalVehicleApiService {

    private final RestTemplate restTemplate;
    private final VmisProperties vmisProperties;
    private final VmisCarBassMatterInqireLogService bassMatterLogService;
    private final VmisCarLedgerFrmbkLogService ledgerLogService;


    /**
     * 기본정보 조회 (YAML 설정에 따라 old/new 자동 선택)
     * vmis.external.api.url.basic.old-or-new 설정 값에 따라 자동 분기
     */
    @Override
    public NewBasicResponse getBasicInfo(NewBasicRequest request) {
        String apiVersion = vmisProperties.getExternal().getApi().getUrl().getBasic().getOldOrNew();
        log.debug("[AUTO-BASIC] YAML 설정에 따른 API 버전: {}", apiVersion);

        if ("old".equalsIgnoreCase(apiVersion)) {
            return callOldBasicApi(request);
        } else {
            return callNewBasicApi(request);
        }
    }

    /**
     * 등록원부(갑) 조회 (YAML 설정에 따라 old/new 자동 선택)
     * vmis.external.api.url.ledger.old-or-new 설정 값에 따라 자동 분기
     */
    @Override
    public NewLedgerResponse getLedgerInfo(NewLedgerRequest request) {
        String apiVersion = vmisProperties.getExternal().getApi().getUrl().getLedger().getOldOrNew();
        log.debug("[AUTO-LEDGER] YAML 설정에 따른 API 버전: {}", apiVersion);

        if ("old".equalsIgnoreCase(apiVersion)) {
            return null;
        } else {
            return callNewLedgerApi(request);
        }
    }

    /**
     * 구 기본정보 API 호출 (내부용)
     */
    private NewBasicResponse callOldBasicApi(NewBasicRequest newRequest) {
        log.debug("[OLD-BASIC] 자동차 기본정보 조회 API 호출");

        String generatedId = null;
        try {
            // NewBasicRequest → OldBasicRequest 변환
            OldBasicRequest oldRequest = convertToOldBasicRequest(newRequest);

            CarBassMatterInqireVO logEntity = CarBassMatterInqireVO.fromOldRequest(oldRequest);
            generatedId = bassMatterLogService.createInitialRequestNewTx(logEntity);

            Envelope<OldBasicRequest> requestEnvelope = new Envelope<>(oldRequest);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<Envelope<OldBasicRequest>> requestEntity = new HttpEntity<>(requestEnvelope, headers);

            ResponseEntity<ApiExchangeEnvelope<OldBasicRequest, OldBasicResponse>> responseEntity = restTemplate.exchange(
                    vmisProperties.getExternal().getApi().getUrl().buildOldBasicUrl(),
                    HttpMethod.POST,
                    requestEntity,
                    new ParameterizedTypeReference<ApiExchangeEnvelope<OldBasicRequest, OldBasicResponse>>() {}
            );

            if (responseEntity.getStatusCode() == HttpStatus.OK && responseEntity.getBody() != null) {
                ApiExchangeEnvelope<OldBasicRequest, OldBasicResponse> env = responseEntity.getBody();
                List<OldBasicResponse> data = (env.getResponse() != null) ? env.getResponse().getData() : null;
                if (data != null && !data.isEmpty()) {
                    OldBasicResponse oldBody = data.get(0);
                    // 응답 정보 업데이트 (txId 포함)
                    CarBassMatterInqireVO responseUpdate = CarBassMatterInqireVO.fromOldExchange(generatedId, env);
                    bassMatterLogService.updateResponseNewTx(responseUpdate);

                    log.debug("[OLD-BASIC] txId: {}", env.getTxId());
                    // OldBasicResponse를 NewBasicResponse로 변환하여 반환
                    NewBasicResponse newResponse = convertToNewBasicResponse(oldBody);
                    // txId를 response에 설정
                    newResponse.setTxId(env.getTxId());
                    newResponse.setGeneratedId(generatedId);

                    return newResponse;
                }
            }

            log.warn("[OLD-BASIC] 응답이 비어있음");
            return null;
        } catch (Exception e) {
            if (generatedId != null) {
                try {
                    String detail = ExceptionDetailUtil.buildForLog(e);
                    CarBassMatterInqireVO errorLog = CarBassMatterInqireVO.builder()
                            .carBassMatterInqireId(generatedId)
                            .linkRsltCd(ApiConstant.CNTC_RESULT_CODE_ERROR)
                            .linkRsltDtl(detail)
                            .build();
                    bassMatterLogService.updateResponseNewTx(errorLog);
                    log.error("[EXTERNAL-OLD-BASIC-ERR-LOG] 저장 완료 - ID: {}, detail: {}", generatedId, detail, e);
                } catch (Exception ignore) {
                    log.error("[EXTERNAL-OLD-BASIC-ERR-LOG] 에러 로그 저장 실패 - ID: {}", generatedId, ignore);
                }
            }
            throw new MessageException("[OLD-BASIC] 차량 기본정보 조회 실패: " + e.getMessage(), e);
        }
    }

    /**
     * 신 기본정보 API 호출 (내부용)
     */
    private NewBasicResponse callNewBasicApi(NewBasicRequest request) {
        log.debug("[NEW-BASIC] 자동차 기본정보 조회 API 호출");

        String generatedId = null;
        try {
            CarBassMatterInqireVO logEntity = CarBassMatterInqireVO.fromNewRequest(request);
            generatedId = bassMatterLogService.createInitialRequestNewTx(logEntity);

            Envelope<NewBasicRequest> requestEnvelope = new Envelope<>(request);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<Envelope<NewBasicRequest>> requestEntity = new HttpEntity<>(requestEnvelope, headers);

            ResponseEntity<ApiExchangeEnvelope<NewBasicRequest, NewBasicResponse>> responseEntity = restTemplate.exchange(
                    vmisProperties.getExternal().getApi().getUrl().buildNewBasicUrl(),
                    HttpMethod.POST,
                    requestEntity,
                    new ParameterizedTypeReference<ApiExchangeEnvelope<NewBasicRequest, NewBasicResponse>>() {}
            );

            if (responseEntity.getStatusCode() == HttpStatus.OK && responseEntity.getBody() != null) {
                ApiExchangeEnvelope<NewBasicRequest, NewBasicResponse> env = responseEntity.getBody();
                List<NewBasicResponse> data = (env.getResponse() != null) ? env.getResponse().getData() : null;
                if (data != null && !data.isEmpty()) {
                    NewBasicResponse body = data.get(0);
                    // 응답 정보 업데이트 (txId 포함)
                    CarBassMatterInqireVO responseUpdate = CarBassMatterInqireVO.fromNewExchange(generatedId, env);
                    bassMatterLogService.updateResponseNewTx(responseUpdate);

                    // txId를 response에 설정
                    body.setTxId(env.getTxId());
                    body.setGeneratedId(generatedId);

                    log.debug("[NEW-BASIC] txId: {}", env.getTxId());
                    return body;
                }
            }

            log.warn("[NEW-BASIC] 응답이 비어있음");
            return null;
        } catch (Exception e) {
            if (generatedId != null) {
                try {
                    String detail = ExceptionDetailUtil.buildForLog(e);
                    CarBassMatterInqireVO errorLog = CarBassMatterInqireVO.builder()
                            .carBassMatterInqireId(generatedId)
                            .linkRsltCd(ApiConstant.CNTC_RESULT_CODE_ERROR)
                            .linkRsltDtl(detail)
                            .build();
                    bassMatterLogService.updateResponseNewTx(errorLog);
                    log.error("[EXTERNAL-NEW-BASIC-ERR-LOG] 저장 완료 - ID: {}, detail: {}", generatedId, detail, e);
                } catch (Exception ignore) {
                    log.error("[EXTERNAL-NEW-BASIC-ERR-LOG] 에러 로그 저장 실패 - ID: {}", generatedId, ignore);
                }
            }
            throw new MessageException("[NEW-BASIC] 차량 기본정보 조회 실패: " + e.getMessage(), e);
        }
    }

    /**
     * 신 등록원부 API 호출 (내부용)
     */
    private NewLedgerResponse callNewLedgerApi(NewLedgerRequest request) {
        log.debug("[NEW-LEDGER] 자동차 등록원부 조회 API 호출");

        String generatedId = null;
        try {
            CarLedgerFrmbkVO init = CarLedgerFrmbkVO.fromNewRequest(request);
            generatedId = ledgerLogService.createInitialRequestNewTx(init);

            Envelope<NewLedgerRequest> requestEnvelope = new Envelope<>(request);
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_JSON);
            HttpEntity<Envelope<NewLedgerRequest>> requestEntity = new HttpEntity<>(requestEnvelope, headers);

            ResponseEntity<ApiExchangeEnvelope<NewLedgerRequest, NewLedgerResponse>> responseEntity = restTemplate.exchange(
                    vmisProperties.getExternal().getApi().getUrl().buildNewLedgerUrl(),
                    HttpMethod.POST,
                    requestEntity,
                    new ParameterizedTypeReference<ApiExchangeEnvelope<NewLedgerRequest, NewLedgerResponse>>() {}
            );

            if (responseEntity.getStatusCode() == HttpStatus.OK && responseEntity.getBody() != null) {
                ApiExchangeEnvelope<NewLedgerRequest, NewLedgerResponse> env = responseEntity.getBody();
                List<NewLedgerResponse> data = (env.getResponse() != null) ? env.getResponse().getData() : null;
                if (data != null && !data.isEmpty()) {
                    NewLedgerResponse body = data.get(0);
                    CarLedgerFrmbkVO masterUpdate = CarLedgerFrmbkVO.fromNewResponseMaster(generatedId, body);
                    masterUpdate.setTxId(env.getTxId());
                    ledgerLogService.updateResponseNewTx(masterUpdate);

                    List<CarLedgerFrmbkDtlVO> details = CarLedgerFrmbkDtlVO.listNewFromExchange(env, generatedId);
                    if (details != null && !details.isEmpty()) {
                        ledgerLogService.saveDetailsNewTx(generatedId, details);
                    }

                    // txId를 response에 설정
                    body.setTxId(env.getTxId());
                    body.setGeneratedId(generatedId);

                    log.debug("[NEW-LEDGER] txId: {}", env.getTxId());
                    return body;
                }
            }

            log.warn("[NEW-LEDGER] 자동차 등록원부 조회 응답이 비어있음");
            return null;

        } catch (Exception e) {
            if (generatedId != null) {
                try {
                    String detail = ExceptionDetailUtil.buildForLog(e);
                    CarLedgerFrmbkVO errorLog = CarLedgerFrmbkVO.builder()
                            .carLedgerFrmbkId(generatedId)
                            .linkRsltCd(ApiConstant.CNTC_RESULT_CODE_ERROR)
                            .linkRsltDtl(detail)
                            .build();
                    ledgerLogService.updateResponseNewTx(errorLog);
                    log.error("[EXTERNAL-NEW-LEDGER-ERR-LOG] API 호출 에러 정보 저장 완료(별도TX) - ID: {}, detail: {}", generatedId, detail, e);
                } catch (Exception ignore) {
                    log.error("[EXTERNAL-NEW-LEDGER-ERR-LOG] 에러 로그 저장 실패 - ID: {}", generatedId, ignore);
                }
            }
            log.error("[NEW-LEDGER] 자동차 등록원부 조회 API 호출 실패", e);
            throw new MessageException("[NEW-LEDGER] 자동차 등록원부 조회 실패: " + e.getMessage(), e);
        }
    }

    /**
     * NewBasicRequest를 OldBasicRequest로 변환
     */
    private OldBasicRequest convertToOldBasicRequest(NewBasicRequest newRequest) {
        OldBasicRequest oldRequest = new OldBasicRequest();

        // 공통 메타 정보 복사 (필드명이 다름)
        oldRequest.setInfoSysId(newRequest.getInfoSysId());
        oldRequest.setInfoSysIp(newRequest.getInfoSysIpAddr());
        oldRequest.setSigunguCode(newRequest.getSggCd());
        oldRequest.setCntcInfoCode(newRequest.getLinkInfoCd());
        oldRequest.setChargerId(newRequest.getPicId());
        oldRequest.setChargerIp(newRequest.getPicIpAddr());
        oldRequest.setChargerNm(newRequest.getPicNm());

        // Record 변환
        if (newRequest.getRecord() != null && !newRequest.getRecord().isEmpty()) {
            List<OldBasicRequest.Record> oldRecords = newRequest.getRecord().stream()
                    .map(this::convertToOldRecord)
                    .collect(Collectors.toList());
            oldRequest.setRecord(oldRecords);
        }

        return oldRequest;
    }

    /**
     * NewBasicRequest.Record를 OldBasicRequest.Record로 변환
     */
    private OldBasicRequest.Record convertToOldRecord(NewBasicRequest.Record newRecord) {
        OldBasicRequest.Record oldRecord = new OldBasicRequest.Record();
        oldRecord.setLevyStdde(newRecord.getLevyCrtrYmd());
        oldRecord.setInqireSeCode(newRecord.getInqSeCd());
        oldRecord.setVhrno(newRecord.getVhrno());
        oldRecord.setVin(newRecord.getVin());
        return oldRecord;
    }

    /**
     * OldBasicResponse를 NewBasicResponse로 변환
     *
     * @param oldResponse 구 API 응답 객체
     * @return 신 API 응답 형식의 객체
     */
    private NewBasicResponse convertToNewBasicResponse(OldBasicResponse oldResponse) {
        if (oldResponse == null) {
            return null;
        }

        NewBasicResponse newResponse = new NewBasicResponse();
        
        // 공통 응답 필드 변환 (필드명이 다름)
        newResponse.setLinkRsltCd(oldResponse.getCntcResultCode());
        newResponse.setLinkRsltDtl(oldResponse.getCntcResultDtls());

        // Record 변환
        if (oldResponse.getRecord() != null && !oldResponse.getRecord().isEmpty()) {
            List<NewBasicResponse.Record> newRecords = oldResponse.getRecord().stream()
                    .map(this::convertOldRecordToNewRecord)
                    .collect(Collectors.toList());
            newResponse.setRecord(newRecords);
        }

        return newResponse;
    }

    /**
     * OldBasicResponse.Record를 NewBasicResponse.Record로 변환
     *
     * @param oldRecord 구 API Record 객체
     * @return 신 API Record 객체
     */
    private NewBasicResponse.Record convertOldRecordToNewRecord(OldBasicResponse.Record oldRecord) {
        NewBasicResponse.Record newRecord = new NewBasicResponse.Record();

        // 필드명 매핑 (구 → 신)
        newRecord.setYridnw(oldRecord.getPrye());
        newRecord.setRegYmd(oldRecord.getRegistDe());
        newRecord.setErsrRegSeCd(oldRecord.getErsrRegistSeCode());
        newRecord.setErsrRegSeNm(oldRecord.getErsrRegistSeNm());
        newRecord.setErsrRegYmd(oldRecord.getErsrRegistDe());
        newRecord.setRegDtlCd(oldRecord.getRegistDetailCode());
        newRecord.setDsplvl(oldRecord.getDsplvl());
        newRecord.setUsgsrhldStdgCd(oldRecord.getUseStrnghldLegaldongCode());
        newRecord.setUsgsrhldDongCd(oldRecord.getUseStrnghldAdstrdCode());
        newRecord.setUsgsrhldMtnYn(oldRecord.getUseStrnghldMntn());
        newRecord.setUsgsrhldLnbr(oldRecord.getUseStrnghldLnbr());
        newRecord.setUsgsrhldHo(oldRecord.getUseStrnghldHo());
        newRecord.setUsgsrhldAddrNm(oldRecord.getUseStrnghldAdresNm());
        newRecord.setUsgsrhldRoadNmCd(oldRecord.getUseStrnghldRoadNmCode());
        newRecord.setUsgsrhldUdgdBldgSeCd(oldRecord.getUsgsrhldUndgrndBuldSeCode());
        newRecord.setUsgsrhldBmno(oldRecord.getUseStrnghldBuldMainNo());
        newRecord.setUsgsrhldBsno(oldRecord.getUseStrnghldBuldSubNo());
        newRecord.setUsgsrhldWholAddr(oldRecord.getUsgsrhldAdresFull());
        newRecord.setRprsOwnrMbrSeCd(oldRecord.getMberSeCode());
        newRecord.setRprsOwnrNm(oldRecord.getMberNm());
        newRecord.setRprsvOwnrIdecno(oldRecord.getMberSeNo());
        newRecord.setRprsOwnrTelno(oldRecord.getTelno());
        newRecord.setOwnrStdgCd(oldRecord.getOwnerLegaldongCode());
        newRecord.setOwnrDongCd(oldRecord.getOwnerAdstrdCode());
        newRecord.setOwnrMtnYn(oldRecord.getOwnerMntn());
        newRecord.setOwnrLnbr(oldRecord.getOwnerLnbr());
        newRecord.setOwnrHo(oldRecord.getOwnerHo());
        newRecord.setOwnrAddrNm(oldRecord.getOwnerAdresNm());
        newRecord.setOwnrRoadNmCd(oldRecord.getOwnerRoadNmCode());
        newRecord.setOwnrUdgdBldgSeCd(oldRecord.getOwnerUndgrndBuldSeCode());
        newRecord.setOwnrBmno(oldRecord.getOwnerBuldMainNo());
        newRecord.setOwnrBsno(oldRecord.getOwnerBuldSubNo());
        newRecord.setOwnrWholAddr(oldRecord.getOwnerAdresFull());
        newRecord.setRearVhrno(oldRecord.getAftrVhrno());
        newRecord.setUseFuelCd(oldRecord.getUseFuelCode());
        newRecord.setUsgSeCd(oldRecord.getPrposSeCode());
        newRecord.setMtrsFomNm(oldRecord.getMtrsFomNm());
        newRecord.setBfrVhrno(oldRecord.getFrntVhrno());
        newRecord.setVhrno(oldRecord.getVhrno());
        newRecord.setVin(oldRecord.getVin());
        newRecord.setAtmbNm(oldRecord.getCnm());
        newRecord.setVhclTotlWt(oldRecord.getVhcleTotWt());
        newRecord.setVeagEndYmd(oldRecord.getCaagEndde());
        newRecord.setChgYmd(oldRecord.getChangeDe());
        newRecord.setCarmdlAsortCd(oldRecord.getVhctyAsortCode());
        newRecord.setCarmdlTypeCd(oldRecord.getVhctyTyCode());
        newRecord.setCarmdlSeCd(oldRecord.getVhctySeCode());
        newRecord.setMxmmLdg(oldRecord.getMxmmLdg());
        newRecord.setCarmdlAsortNm(oldRecord.getVhctyAsortNm());
        newRecord.setCarmdlTypeNm(oldRecord.getVhctyTyNm());
        newRecord.setCarmdlClsfNm(oldRecord.getVhctySeNm());
        newRecord.setFrstRegYmd(oldRecord.getFrstRegistDe());
        newRecord.setFomNm(oldRecord.getFomNm());
        newRecord.setAcqsYmd(oldRecord.getAcqsDe());
        newRecord.setAcqsEndYmd(oldRecord.getAcqsEndDe());
        newRecord.setFbctnYmd(oldRecord.getYblMd());
        newRecord.setTransrRegYmd(oldRecord.getTransrRegistDe());
        newRecord.setSpcfRegSttsCd(oldRecord.getSpcfRegistSttusCode());
        newRecord.setColorNm(oldRecord.getColorNm());
        newRecord.setMrtgCnt(oldRecord.getMrtgCo());
        newRecord.setSzrCnt(oldRecord.getSeizrCo());
        newRecord.setStrctChgCnt(oldRecord.getStmdCo());
        newRecord.setNoplTcsdyYn(oldRecord.getNmplCsdyAt());
        newRecord.setNoplTcsdyAvtsmtYmd(oldRecord.getNmplCsdyRemnrDe());
        newRecord.setSrcSeCd(oldRecord.getOriginSeCode());
        newRecord.setNoplTSpcfctCd(oldRecord.getNmplStndrdCode());
        newRecord.setAcqsAmt(oldRecord.getAcqsAmount());
        newRecord.setInspVldPdBgngYmd(oldRecord.getInsptValidPdBgnde());
        newRecord.setInspVldPdEndYmd(oldRecord.getInsptValidPdEndde());
        newRecord.setUsgsrhldGrcCd(oldRecord.getUseStrnghldGrcCode());
        newRecord.setRdcpctCnt(oldRecord.getTkcarPscapCo());
        newRecord.setSpmnno(oldRecord.getSpmnno());
        newRecord.setDrvngDstnc(oldRecord.getTrvlDstnc());
        newRecord.setFrstRegAplyRcptNo(oldRecord.getFrstRegistRqrcno());
        newRecord.setVlntErsrPrvntcAvtsmtYmd(oldRecord.getVlntErsrPrvntcNticeDe());
        newRecord.setOgnzNm(oldRecord.getRegistInsttNm());
        newRecord.setPrcsImprtyRsnCd(oldRecord.getProcessImprtyResnCode());
        newRecord.setPrcsImprtyRsnDtls(oldRecord.getProcessImprtyResnDtls());
        newRecord.setCbdLt(oldRecord.getCbdLt());
        newRecord.setCbdBt(oldRecord.getCbdBt());
        newRecord.setCbdHg(oldRecord.getCbdHg());
        newRecord.setFrstMxmmLdg(oldRecord.getFrstMxmmLdg());
        newRecord.setFuelCnsmprt(oldRecord.getFuelCnsmpRt());
        newRecord.setElctyCmpndFuelCnsmprt(oldRecord.getElctyCmpndFuelCnsmpRt());

        return newRecord;
    }
}
