package com.interplug.qcast.biz.object;

import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang3.RandomStringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpMethod;
import org.springframework.stereotype.Service;
import org.springframework.web.util.UriComponentsBuilder;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.interplug.qcast.biz.canvasStatus.CanvasStatusService;
import com.interplug.qcast.biz.canvasStatus.dto.CanvasStatus;
import com.interplug.qcast.biz.canvasStatus.dto.CanvasStatusCopyRequest;
import com.interplug.qcast.biz.estimate.EstimateMapper;
import com.interplug.qcast.biz.estimate.dto.EstimateCopyRequest;
import com.interplug.qcast.biz.estimate.dto.EstimateRequest;
import com.interplug.qcast.biz.estimate.dto.EstimateResponse;
import com.interplug.qcast.biz.estimate.dto.ItemRequest;
import com.interplug.qcast.biz.estimate.dto.ItemResponse;
import com.interplug.qcast.biz.object.dto.ObjectRequest;
import com.interplug.qcast.biz.object.dto.ObjectResponse;
import com.interplug.qcast.biz.object.dto.PlanReqRequest;
import com.interplug.qcast.biz.object.dto.PlanReqResponse;
import com.interplug.qcast.biz.object.dto.PlanRequest;
import com.interplug.qcast.biz.object.dto.PlanResponse;
import com.interplug.qcast.biz.object.dto.PrefResponse;
import com.interplug.qcast.biz.object.dto.SaleStoreResponse;
import com.interplug.qcast.biz.object.dto.WindSpeedResponse;
import com.interplug.qcast.biz.storeFavorite.StoreFavoriteService;
import com.interplug.qcast.config.Exception.ErrorCode;
import com.interplug.qcast.config.Exception.QcastException;
import com.interplug.qcast.config.message.Messages;
import com.interplug.qcast.util.InterfaceQsp;
import io.micrometer.common.util.StringUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
public class ObjectService {
  private final InterfaceQsp interfaceQsp;

  @Autowired
  Messages message;

  @Value("${file.ini.root.path}")
  private String baseDirPath;

  @Value("${qsp.url}")
  private String QSP_API_URL;

  private final ObjectMapper objectMapper;

  private final EstimateMapper estimateMapper;

  private final StoreFavoriteService storeFavoriteService;

  @Autowired
  private CanvasStatusService canvasStatusService;

  /**
   * 도도부현 목록 조회
   *
   * @return List<PrefResponse> 도도부현 목록
   * @throws Exception
   */
  public List<PrefResponse> selectPrefList() throws Exception {
    return objectMapper.selectPrefList();
  }

  /**
   * 발전시뮬레이션 지역 목록 조회
   *
   * @param prefId 도도부현 코드
   * @return List<PrefResponse> 발전시뮬레이션 지역 목록
   * @throws Exception
   */
  public List<PrefResponse> selectPrefAreaList(String prefId) throws Exception {
    return objectMapper.selectPrefAreaList(prefId);
  }

  /**
   * 기준 풍속 목록 조회
   *
   * @param city 지역코드
   * @return List<WindSpeedResponse> 기준 풍속 목록
   * @throws Exception
   */
  public List<WindSpeedResponse> selectWindSpeedList(String city) throws Exception {
    return objectMapper.selectWindSpeedList(city);
  }

  /**
   * 1차점 판매점 목록 조회
   *
   * @param saleStoreId 1차점 판매점 아이디
   * @param userId 유저 아이디
   * @return List<SaleStoreResponse> 1차점 판매점 목록
   * @throws Exception
   */
  public List<SaleStoreResponse> selectFirstSaleStoreList(String saleStoreId, String userId)
      throws Exception {
    // Validation
    if (StringUtils.isEmpty(saleStoreId)) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Sale Store ID"));
    }
    if (StringUtils.isEmpty(userId)) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "User ID"));
    }

    // 1차점 판매점 목록 조회
    List<SaleStoreResponse> storeList = new ArrayList<SaleStoreResponse>();

    if ("T01".equals(saleStoreId)) {
      storeList = objectMapper.selectSaleStoreFirstList(userId);
    }

    return storeList;
  }

  /**
   * 하위 판매점 목록 조회
   *
   * @param saleStoreId 판매점 아이디
   * @param firstFlg 1차점 판매점 여부
   * @param userId 유저 아이디
   * @return List<SaleStoreResponse> 하위 판매점 목록
   * @throws Exception
   */
  public List<SaleStoreResponse> selectSaleStoreList(String saleStoreId, String firstFlg,
      String userId) throws Exception {
    // Validation
    if (StringUtils.isEmpty(saleStoreId)) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Sale Store ID"));
    }
    if (StringUtils.isEmpty(userId)) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "User ID"));
    }

    // 판매점 목록 조회
    List<SaleStoreResponse> storeList = new ArrayList<SaleStoreResponse>();
    storeList = objectMapper.selectSaleStoreList(saleStoreId, firstFlg, userId);

    return storeList;
  }

  /**
   * 판매점 정보 상세 조회
   *
   * @param saleStoreId 판매점 아이디
   * @return SaleStoreResponse 판매점 상세 정보
   * @throws Exception
   */
  public SaleStoreResponse selectSaleStoreInfo(String saleStoreId) throws Exception {
    return objectMapper.selectSaleStoreInfo(saleStoreId);
  }

  /**
   * 물건정보 메인 목록 조회
   *
   * @param objectRequest 물건정보 검색어 정보
   * @return List<ObjectResponse> 물건정보 목록
   * @throws Exception
   */
  public List<ObjectResponse> selectObjectMainList(ObjectRequest objectRequest) throws Exception {
    if (StringUtils.isEmpty(objectRequest.getSaleStoreId())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Sale Store ID"));
    }

    return objectMapper.selectObjectMainList(objectRequest);
  }

  /**
   * 물건정보 목록 조회
   *
   * @param objectRequest 물건정보 검색어 정보
   * @return List<ObjectResponse> 물건정보 목록
   * @throws Exception
   */
  public List<ObjectResponse> selectObjectList(ObjectRequest objectRequest) throws Exception {
    if (StringUtils.isEmpty(objectRequest.getSaleStoreId())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Sale Store ID"));
    }

    if (!"T01".equals(objectRequest.getSaleStoreId())) {
      objectRequest.setSchSelSaleStoreId("");
    }

    return objectMapper.selectObjectList(objectRequest);
  }

  /**
   * 물건정보 상세 조회
   *
   * @param objectNo 물건번호
   * @return ObjectResponse 물건정보 상세
   * @throws Exception
   */
  public ObjectResponse selectObjectDetail(String objectNo) throws Exception {
    String splitStr = "、";

    // object 상세 정보 조회
    ObjectResponse objectResponse = objectMapper.selectObjectDetail(objectNo);

    if (objectResponse != null) {
      // Plan 목록 조회
      PlanRequest planRequest = new PlanRequest();
      planRequest.setObjectNo(objectNo);
      List<PlanResponse> planList = objectMapper.selectPlanList(planRequest);

      for (PlanResponse planResponse : planList) {
        String roofCheckDatas = "";
        String roofMaterialIdMultis = "";
        String constructSpecificationMultis = "";
        String supportMethodIdMultis = "";
        String orgRoofMaterialIdMultis = planResponse.getRoofMaterialIdMulti();
        String orgConstructSpecificationMultis = planResponse.getConstructSpecificationMulti();
        String orgSupportMethodIdMultis = planResponse.getSupportMethodIdMulti();

        // Plan 목록에서 지붕재, 시공방법 동일 데이타 중복 제거
        if (!StringUtils.isEmpty(orgRoofMaterialIdMultis)
            && !StringUtils.isEmpty(orgConstructSpecificationMultis)) {
          String[] arrOrgRoofMaterialIdMultis = orgRoofMaterialIdMultis.split(splitStr);
          String[] arrOrgConstructSpecificationMultis =
              orgConstructSpecificationMultis.split(splitStr);

          if (arrOrgRoofMaterialIdMultis.length == arrOrgConstructSpecificationMultis.length) {
            for (int i = 0; i < arrOrgRoofMaterialIdMultis.length; i++) {

              if (!roofCheckDatas.contains(
                  arrOrgRoofMaterialIdMultis[i] + "_" + arrOrgConstructSpecificationMultis[i])) {
                roofMaterialIdMultis +=
                    StringUtils.isEmpty(roofMaterialIdMultis) ? arrOrgRoofMaterialIdMultis[i]
                        : splitStr + arrOrgRoofMaterialIdMultis[i];
                constructSpecificationMultis += StringUtils.isEmpty(constructSpecificationMultis)
                    ? arrOrgConstructSpecificationMultis[i]
                    : splitStr + arrOrgConstructSpecificationMultis[i];
              }

              roofCheckDatas +=
                  arrOrgRoofMaterialIdMultis[i] + "_" + arrOrgConstructSpecificationMultis[i];
            }

            if (!StringUtils.isEmpty(roofMaterialIdMultis)) {
              planResponse.setRoofMaterialIdMulti(roofMaterialIdMultis);
            }
            if (!StringUtils.isEmpty(roofMaterialIdMultis)) {
              planResponse.setConstructSpecificationMulti(constructSpecificationMultis);
            }
          }

          // 공법 중복 제거
          if (!StringUtils.isEmpty(orgSupportMethodIdMultis)) {
            String[] arrOrgSupportMethodIdMultis = orgSupportMethodIdMultis.split(splitStr);
            for (String str : arrOrgSupportMethodIdMultis) {
              if (!supportMethodIdMultis.contains(str)) {
                supportMethodIdMultis +=
                    StringUtils.isEmpty(supportMethodIdMultis) ? str : splitStr + str;
              }
            }

            if (!StringUtils.isEmpty(supportMethodIdMultis)) {
              planResponse.setSupportMethodIdMulti(supportMethodIdMultis);
            }
          }
        }
      }

      objectResponse.setPlanList(planList);
    }

    return objectResponse;
  }

  /**
   * 물건정보 저장
   *
   * @param objectRequest 물건정보
   * @return ObjectResponse 물건정보 저장 결과 값
   * @throws Exception
   */
  public ObjectResponse insertObject(ObjectRequest objectRequest) throws Exception {
    int result = 0;
    String objectNo = "";

    // Validation
    if (StringUtils.isEmpty(objectRequest.getSaleStoreId())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Sale Store ID"));
    }
    if (StringUtils.isEmpty(objectRequest.getSaleStoreLevel())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Sale Store Level"));
    }
    if (StringUtils.isEmpty(objectRequest.getUserId())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "User ID"));
    }

    objectRequest.setSourceOrigin("QCAST_III");

    // 물건번호 등록/조회
    if ("0".equals(objectRequest.getTempFlg())) {
      objectRequest.setDelFlg("1");
      objectRequest.setOrgDelFlg("0");
      objectRequest.setTempFlg("0");
      objectRequest.setTempDelFlg("0");

      result += objectMapper.insertObjectNo(objectRequest);
      objectNo = objectMapper.selectObjectNo(objectRequest);
    } else if ("1".equals(objectRequest.getTempFlg())) {
      objectRequest.setDelFlg("1");
      objectRequest.setOrgDelFlg("1");
      objectRequest.setTempFlg("1");
      objectRequest.setTempDelFlg("0");

      // 임시저장인 경우 임시 물건번호 생성 yyyymmddhh24missSSS_랜덤문자 3개
      SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmssSSS");
      Calendar dateTime = Calendar.getInstance();
      objectNo = sdf.format(dateTime.getTime());

      objectNo = "T" + objectNo.substring(2) + RandomStringUtils.randomAlphanumeric(3);
      objectNo = objectNo.toUpperCase();
    }

    objectRequest.setObjectNo(objectNo);

    // 물건정보 등록
    objectRequest.setAddress(
        ((!StringUtils.isEmpty(objectRequest.getAddress())) ? objectRequest.getAddress() : ""));
    objectRequest.setAddresseeCompanyName(
        objectRequest.getObjectName() + ' ' + objectRequest.getObjectNameOmit());
    objectRequest.setAddresseeCompanyNameOmit(objectRequest.getObjectNameOmit());
    objectRequest.setContentsPath(baseDirPath + "\\\\" + objectRequest.getObjectNo());
    result += objectMapper.insertObject(objectRequest);
    result += objectMapper.insertObjectInfo(objectRequest);
    result += objectMapper.updateObjectDelivery(objectRequest);

    // 디폴트 Plan 등록
    /*
     * PlanRequest planRequest = new PlanRequest(); planRequest.setObjectNo(objectNo);
     * planRequest.setRoofKindId("0"); planRequest.setCharger(objectRequest.getReceiveUser());
     * planRequest.setStatus("1"); planRequest.setDelFlg("0"); planRequest.setNorthArrangement("0");
     * planRequest.setDiffRoofEnabled("0"); planRequest.setOrderFlg("0");
     * planRequest.setTempFlg("1"); planRequest.setUserId(objectRequest.getUserId()); result +=
     * objectMapper.insertPlan(planRequest);
     */

    // 설계의뢰번호 존재 시 물건번호 업데이트
    if ("0".equals(objectRequest.getTempFlg())
        && !StringUtils.isEmpty(objectRequest.getPlanReqNo())) {
      PlanReqResponse response = null;

      PlanReqRequest planReqRequest = new PlanReqRequest();
      planReqRequest.setSaleStoreId(objectRequest.getSaleStoreId());
      planReqRequest.setSaleStoreLevel(objectRequest.getSaleStoreLevel());
      planReqRequest.setPlanReqNo(objectRequest.getPlanReqNo());
      planReqRequest.setObjectNo(objectRequest.getObjectNo());

      String strResponse = interfaceQsp.callApi(HttpMethod.POST,
          QSP_API_URL + "/api/planReq/updateObjectNo", planReqRequest);
      if (!"".equals(strResponse)) {
        com.fasterxml.jackson.databind.ObjectMapper om =
            new com.fasterxml.jackson.databind.ObjectMapper()
                .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        response = om.readValue(strResponse, PlanReqResponse.class);

        Map<String, Object> map = (Map<String, Object>) response.getResult();
        if ("E".equals(String.valueOf(map.get("resultCode")))) {
          throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR,
              String.valueOf(map.get("resultMsg")));
        }
      }
    }

    // object 상세 정보 조회 결과 데이터 리턴
    ObjectResponse objectResponse = objectMapper.selectObjectDetail(objectNo);

    return objectResponse;
  }

  /**
   * 물건정보 수정
   *
   * @param objectRequest 물건정보
   * @return ObjectResponse 물건정보 수정 결과 값
   * @throws Exception
   */
  public ObjectResponse updateObject(ObjectRequest objectRequest) throws Exception {
    int result = 0;
    boolean tempChgFlg = false;

    // Validation
    if (StringUtils.isEmpty(objectRequest.getSaleStoreId())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Sale Store ID"));
    }
    if (StringUtils.isEmpty(objectRequest.getSaleStoreLevel())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Sale Store Level"));
    }
    if (StringUtils.isEmpty(objectRequest.getUserId())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "User ID"));
    }

    // object 상세 정보 조회
    ObjectResponse objectResponse = objectMapper.selectObjectDetail(objectRequest.getObjectNo());

    if (objectResponse != null) {
      // 임시저장에서 저장상태 바뀌었는지 확인
      if ("1".equals(objectResponse.getTempFlg()) && "0".equals(objectRequest.getTempFlg())) {
        tempChgFlg = true;

        result += objectMapper.insertObjectNo(objectRequest);
        objectRequest.setNewObjectNo(objectMapper.selectObjectNo(objectRequest));
      }

      // 물건정보 수정
      objectRequest.setAddress(
          ((!StringUtils.isEmpty(objectRequest.getAddress())) ? objectRequest.getAddress() : ""));
      objectRequest.setAddresseeCompanyName(
          objectRequest.getObjectName() + ' ' + objectRequest.getObjectNameOmit());
      objectRequest.setAddresseeCompanyNameOmit(objectRequest.getObjectNameOmit());
      objectRequest.setContentsPath(baseDirPath + "\\\\"
          + (tempChgFlg ? objectRequest.getNewObjectNo() : objectRequest.getObjectNo()));
      result += objectMapper.updateObject(objectRequest);
      result += objectMapper.updateObjectInfo(objectRequest);

      // 임시저장에서 저장상태로 돌리기
      if (tempChgFlg) {
        objectMapper.updateObjectNoChange(objectRequest);
        objectMapper.updateObjectInfoNoChange(objectRequest);
        objectMapper.updatePlanObjectNoChange(objectRequest);

        // 설계의뢰번호 존재 시 물건번호 업데이트
        if (!StringUtils.isEmpty(objectRequest.getPlanReqNo())) {
          PlanReqResponse response = null;

          PlanReqRequest planReqRequest = new PlanReqRequest();
          planReqRequest.setSaleStoreId(objectRequest.getSaleStoreId());
          planReqRequest.setSaleStoreLevel(objectRequest.getSaleStoreLevel());
          planReqRequest.setPlanReqNo(objectRequest.getPlanReqNo());
          planReqRequest.setObjectNo(objectRequest.getNewObjectNo());

          String strResponse = interfaceQsp.callApi(HttpMethod.POST,
              QSP_API_URL + "/api/planReq/updateObjectNo", planReqRequest);
          if (!"".equals(strResponse)) {
            com.fasterxml.jackson.databind.ObjectMapper om =
                new com.fasterxml.jackson.databind.ObjectMapper()
                    .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
            response = om.readValue(strResponse, PlanReqResponse.class);

            Map<String, Object> map = (Map<String, Object>) response.getResult();
            if ("E".equals(String.valueOf(map.get("resultCode")))) {
              throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR,
                  String.valueOf(map.get("resultMsg")));
            }
          }
        }
      }
    }

    // 모든 변경 완료 후 재호출
    objectResponse = objectMapper.selectObjectDetail(
        (tempChgFlg ? objectRequest.getNewObjectNo() : objectRequest.getObjectNo()));
    return objectResponse;
  }

  /**
   * 물건정보 갱신일 수정
   *
   * @param objectRequest
   * @throws Exception
   */
  public void updateObjectLastEditDate(ObjectRequest objectRequest) throws Exception {
    // Validation
    if (StringUtils.isEmpty(objectRequest.getObjectNo())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Object No"));
    }
    if (StringUtils.isEmpty(objectRequest.getUserId())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "User ID"));
    }

    try {
      // object 갱신일 및 상세정보 수정
      objectMapper.updateObjectLastEditDate(objectRequest);
      objectMapper.updateObjectInfoLastEditDate(objectRequest);
    } catch (Exception e) {
      throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR);
    }
  }

  /**
   * 물건정보 삭제
   *
   * @param objectRequest 물건정보
   * @return int 결과 값
   * @throws Exception
   */
  public int deleteObject(ObjectRequest objectRequest) throws Exception {
    int result = 0;

    // Validation
    if (StringUtils.isEmpty(objectRequest.getObjectNo())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Object No"));
    }

    // object 상세 정보 조회
    ObjectResponse objectResponse = objectMapper.selectObjectDetail(objectRequest.getObjectNo());
    if (objectResponse != null) {
      // 설계의뢰 번호가 존재하고 물건정보에 견적서가 없을 경우 QSP 설계의뢰 물건번호 초기화
      if (!StringUtils.isEmpty(objectResponse.getPlanReqNo())
          && objectResponse.getEstimateTotCnt() == 0) {
        PlanReqResponse response = null;

        PlanReqRequest planReqRequest = new PlanReqRequest();
        planReqRequest.setSaleStoreId(objectResponse.getSaleStoreId());
        planReqRequest.setSaleStoreLevel(objectResponse.getSaleStoreLevel());
        planReqRequest.setObjectNo(objectResponse.getObjectNo());
        planReqRequest.setPlanReqNo(objectResponse.getPlanReqNo());
        planReqRequest.setDelFlg("1");

        String strResponse = interfaceQsp.callApi(HttpMethod.POST,
            QSP_API_URL + "/api/planReq/updateObjectNo", planReqRequest);
        if (!"".equals(strResponse)) {
          com.fasterxml.jackson.databind.ObjectMapper om =
              new com.fasterxml.jackson.databind.ObjectMapper()
                  .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
          response = om.readValue(strResponse, PlanReqResponse.class);

          Map<String, Object> map = (Map<String, Object>) response.getResult();
          if ("E".equals(String.valueOf(map.get("resultCode")))) {
            throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR,
                String.valueOf(map.get("resultMsg")));
          }
        }
      }

      // 물건정보 삭제
      result = objectMapper.deleteObject(objectRequest);
      result = objectMapper.deleteObjectInfo(objectRequest);
    }

    return result;
  }

  /**
   * 신규 플랜정보 추가
   *
   * @param planRequest 플랜정보
   * @return String 추가 플랜번호
   * @throws Exception
   */
  public PlanResponse insertPlan(PlanRequest planRequest) throws Exception {

    PlanResponse planResponse = new PlanResponse();

    // Validation
    if (StringUtils.isEmpty(planRequest.getObjectNo())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Object No"));
    }
    if (StringUtils.isEmpty(planRequest.getUserId())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "User ID"));
    }

    if ("1".equals(planRequest.getCopyFlg()) && StringUtils.isEmpty(planRequest.getPlanNo())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Plan No"));
    }

    // Plan 추가 가능 체크
    List<PlanResponse> planList = objectMapper.selectPlanList(planRequest);
    if (planList.size() > 9) {
      throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR,
          message.getMessage("common.message.plan.save.limit"));
    }

    try {
      // 복사조건인 경우 (copyFlg = '1')
      if ("1".equals(planRequest.getCopyFlg())) {
        String copyPlanNo = objectMapper.selectPlanNo(planRequest);

        // [1]. 복사대상 견적서 여부 조회
        EstimateRequest estimateRequest = new EstimateRequest();
        estimateRequest.setObjectNo(planRequest.getObjectNo());
        estimateRequest.setPlanNo(planRequest.getPlanNo());

        // 견적서 상세 조회
        EstimateResponse estimateResponse = estimateMapper.selectEstimateDetail(estimateRequest);

        // [2]. 아이템 관련 데이터 셋팅 (복사 시 정가 셋팅)
        List<ItemRequest> itemList = new ArrayList<ItemRequest>();
        List<ItemResponse> estimateItemList =
            estimateMapper.selectEstimateItemList(estimateRequest);
        for (ItemResponse itemResponse : estimateItemList) {
          ItemRequest itemRequest = new ItemRequest();
          itemRequest.setDispOrder(itemResponse.getDispOrder());
          itemRequest.setPaDispOrder(itemResponse.getPaDispOrder());
          itemRequest.setItemId(itemResponse.getItemId());
          itemRequest.setItemNo(itemResponse.getItemNo());
          itemRequest.setItemName(itemResponse.getItemName());
          itemRequest.setUnit(itemResponse.getUnit());
          itemRequest.setAmount(itemResponse.getAmount());
          itemRequest.setBomAmount(itemResponse.getBomAmount());
          itemRequest.setSpecialNoteCd(itemResponse.getSpecialNoteCd());
          itemRequest.setItemChangeFlg("0");
          itemRequest.setDispCableFlg(itemResponse.getDispCableFlg());

          itemList.add(itemRequest);
        }

        if (!itemList.isEmpty()) {
          String[] arrItemId = new String[itemList.size()];
          int i = 0;
          for (ItemRequest itemRequest : itemList) {
            arrItemId[i++] = itemRequest.getItemId();
          }
          estimateRequest.setArrItemId(arrItemId);
          // 아이템의 마스터 정보 및 정가 정보 조회
          List<ItemResponse> itemResponseList =
              estimateMapper.selectItemMasterList(estimateRequest);

          for (ItemRequest itemRequest : itemList) {
            for (ItemResponse itemResponse : itemResponseList) {
              if (itemRequest.getItemId().equals(itemResponse.getItemId())) {
                itemRequest.setItemNo(itemResponse.getItemNo());
                itemRequest.setItemName(itemResponse.getItemName());
                itemRequest.setUnit(itemResponse.getUnit());
                itemRequest.setPnowW(itemResponse.getPnowW());
                itemRequest.setSpecification(itemResponse.getPnowW());
                itemRequest.setUnitPrice(itemResponse.getSalePrice());
                itemRequest.setSalePrice(itemResponse.getSalePrice());
                itemRequest.setPkgMaterialFlg(itemResponse.getPkgMaterialFlg());
                itemRequest.setFileUploadFlg(itemResponse.getFileUploadFlg());
                itemRequest.setItemGroup(itemResponse.getItemGroup());
                itemRequest.setOpenFlg(itemResponse.getOpenFlg());
                itemRequest.setUnitOpenFlg(itemResponse.getUnitOpenFlg());

                break;
              }
            }
          }
        }

        // [3]. 견적서 복사
        EstimateCopyRequest estimateCopyRequest = new EstimateCopyRequest();
        estimateCopyRequest.setObjectNo(planRequest.getObjectNo());
        estimateCopyRequest.setPlanNo(planRequest.getPlanNo());
        estimateCopyRequest.setCopyObjectNo(planRequest.getObjectNo());
        estimateCopyRequest.setCopyPlanNo(copyPlanNo);
        estimateCopyRequest.setUserId(planRequest.getUserId());
        estimateMapper.insertEstimateCopy(estimateCopyRequest);
        estimateMapper.insertEstimateInfoCopy(estimateCopyRequest);

        if (estimateResponse != null && !StringUtils.isEmpty(estimateResponse.getEstimateDate())) {
          // [4]. 견적서 아이템 복사
          for (ItemRequest itemRequest : itemList) {
            itemRequest.setObjectNo(estimateCopyRequest.getCopyObjectNo());
            itemRequest.setPlanNo(estimateCopyRequest.getCopyPlanNo());
            itemRequest
                .setPartAdd(!org.apache.commons.lang3.StringUtils.isEmpty(itemRequest.getPartAdd())
                    ? itemRequest.getPartAdd()
                    : "0");
            itemRequest.setItemChangeFlg(
                !org.apache.commons.lang3.StringUtils.isEmpty(itemRequest.getItemChangeFlg())
                    ? itemRequest.getItemChangeFlg()
                    : "0");
            itemRequest.setUserId(estimateCopyRequest.getUserId());

            estimateMapper.insertEstimateItem(itemRequest);
            estimateMapper.insertEstimateInfoItem(itemRequest);
          }

          // [5]. 견적서 지붕면 및 도면 초기 데이터 복사
          // 견적서 지붕면 복사
          estimateMapper.insertEstimateRoofCopy(estimateCopyRequest);
          // 견적서 지붕면 아이템 복사
          estimateMapper.insertEstimateRoofItemCopy(estimateCopyRequest);
          // 견적서 지붕면 회로구성 아이템 복사
          estimateMapper.insertEstimateCircuitItemCopy(estimateCopyRequest);
          // 도면 초기 데이타 복사(초기화 위해 필요)
          estimateMapper.insertEstimateDrawingItemCopy(estimateCopyRequest);
        }

        // [6]. 견적서 도면 복사
        CanvasStatusCopyRequest cs = new CanvasStatusCopyRequest();
        cs.setOriginObjectNo(planRequest.getObjectNo());
        cs.setOriginPlanNo(planRequest.getPlanNo());
        cs.setObjectNo(planRequest.getObjectNo());
        cs.setPlanNo(copyPlanNo);
        cs.setUserId(planRequest.getUserId());
        Integer canvasId = canvasStatusService.copyCanvasStatus(cs, false);

        planResponse.setObjectNo(planRequest.getObjectNo());
        planResponse.setPlanNo(copyPlanNo);
        planResponse.setCanvasId(canvasId);

      } else {
        // 추가 Plan 등록
        planRequest.setRoofKindId("0");
        planRequest.setStatus("1");
        planRequest.setDelFlg("0");
        planRequest.setNorthArrangement("0");
        planRequest.setDiffRoofEnabled("0");
        planRequest.setOrderFlg("0");
        planRequest.setTempFlg("1");
        objectMapper.insertPlan(planRequest);
        objectMapper.insertPlanInfo(planRequest);

        // 도면 기본정보 등록
        CanvasStatus cs = new CanvasStatus();
        cs.setObjectNo(planRequest.getObjectNo());
        cs.setPlanNo(planRequest.getPlanNo());
        cs.setCanvasStatus("");
        cs.setUserId(planRequest.getUserId());
        Integer canvasId = canvasStatusService.insertCanvasStatus(cs);

        planResponse.setObjectNo(planRequest.getObjectNo());
        planResponse.setPlanNo(planRequest.getPlanNo());
        planResponse.setCanvasId(canvasId);
      }

    } catch (Exception e) {
      throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR);
    }

    return planResponse;
  }

  /**
   * 플랜정보 삭제
   *
   * @param planRequest 플랜정보
   * @throws Exception
   */
  public void deletePlan(PlanRequest planRequest) throws Exception {
    // Validation
    if (StringUtils.isEmpty(planRequest.getObjectNo())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Object No"));
    }
    if (StringUtils.isEmpty(planRequest.getPlanNo())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Plan No"));
    }
    if (StringUtils.isEmpty(planRequest.getUserId())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "User ID"));
    }

    // Plan 삭제 가능 체크
    List<PlanResponse> planList = objectMapper.selectPlanList(planRequest);
    if (planList.size() < 2) {
      throw new QcastException(ErrorCode.INTERNAL_SERVER_ERROR,
          message.getMessage("common.message.plan.delete.limit"));
    }

    // Plan 삭제
    objectMapper.deletePlan(planRequest);
  }

  /**
   * 설계의뢰 목록 조회
   *
   * @param planReqRequest 설계의뢰 요청 정보
   * @return PlanReqResponse 설계의뢰 응답 정보
   * @throws Exception
   */
  public PlanReqResponse selectPlanReqList(PlanReqRequest planReqRequest) throws Exception {

    // Validation
    if (StringUtils.isEmpty(planReqRequest.getSaleStoreId())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Sale Store ID"));
    }
    if (StringUtils.isEmpty(planReqRequest.getSaleStoreLevel())) {
      throw new QcastException(ErrorCode.INVALID_INPUT_VALUE,
          message.getMessage("common.message.required.data", "Sale Store Level"));
    }

    PlanReqResponse response = null;

    /* [1]. QSP API (url + param) Setting */
    String encodedSchPlanReqNo =
        URLEncoder.encode(StringUtils.isEmpty(planReqRequest.getSchPlanReqNo()) ? ""
            : planReqRequest.getSchPlanReqNo(), StandardCharsets.UTF_8);
    String encodedSchTitle = URLEncoder.encode(
        StringUtils.isEmpty(planReqRequest.getSchTitle()) ? "" : planReqRequest.getSchTitle(),
        StandardCharsets.UTF_8);
    String encodedSchAddress = URLEncoder.encode(
        StringUtils.isEmpty(planReqRequest.getSchAddress()) ? "" : planReqRequest.getSchAddress(),
        StandardCharsets.UTF_8);

    String encodedSchSaleStoreName =
        URLEncoder.encode(StringUtils.isEmpty(planReqRequest.getSchSaleStoreName()) ? ""
            : planReqRequest.getSchSaleStoreName(), StandardCharsets.UTF_8);
    String encodedSchPlanReqName =
        URLEncoder.encode(StringUtils.isEmpty(planReqRequest.getSchPlanReqName()) ? ""
            : planReqRequest.getSchPlanReqName(), StandardCharsets.UTF_8);

    String url = QSP_API_URL + "/api/planReq/list";

    String apiUrl = UriComponentsBuilder.fromHttpUrl(url)
        .queryParam("saleStoreId", planReqRequest.getSaleStoreId())
        .queryParam("saleStoreLevel", planReqRequest.getSaleStoreLevel())
        .queryParam("schPlanReqNo", encodedSchPlanReqNo).queryParam("schTitle", encodedSchTitle)
        .queryParam("schAddress", encodedSchAddress)
        .queryParam("schSaleStoreName", encodedSchSaleStoreName)
        .queryParam("schPlanReqName", encodedSchPlanReqName)
        .queryParam("schPlanStatCd", planReqRequest.getSchPlanStatCd())
        .queryParam("schDateGbn", planReqRequest.getSchDateGbn())
        .queryParam("schStartDt", planReqRequest.getSchStartDt())
        .queryParam("schEndDt", planReqRequest.getSchEndDt())
        .queryParam("startRow", planReqRequest.getStartRow())
        .queryParam("endRow", planReqRequest.getEndRow()).build().toUriString();

    /* [2]. QSP API CALL -> Response */
    String strResponse = interfaceQsp.callApi(HttpMethod.GET, apiUrl, null);

    if (!"".equals(strResponse)) {
      com.fasterxml.jackson.databind.ObjectMapper om =
          new com.fasterxml.jackson.databind.ObjectMapper()
              .configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);

      response = om.readValue(strResponse, PlanReqResponse.class);
    } else {
      // [msg] No data
      throw new QcastException(ErrorCode.NOT_FOUND, message.getMessage("common.message.no.data"));
    }

    return response;
  }
}
