package incheon.ags.dtm.web;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.nio.file.FileSystemException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.egovframe.rte.ptl.mvc.tags.ui.pagination.PaginationInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
import org.springframework.dao.DataAccessException;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.client.RestClientResponseException;
import org.springframework.web.client.RestTemplate;
import org.springframework.web.multipart.MultipartFile;
import org.springframework.web.util.HtmlUtils;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;

import incheon.ags.dtm.service.DtmService;
import incheon.ags.dtm.vo.DmapDownloadVO;
import incheon.ags.dtm.vo.DtmAddressVO;
import incheon.ags.dtm.vo.DtmApplyItemVO;
import incheon.ags.dtm.vo.DtmApplyListVO;
import incheon.ags.dtm.vo.DtmApplySearchVO;
import incheon.ags.dtm.vo.DtmApproveVO;
import incheon.ags.dtm.vo.DtmComboVO;
import incheon.ags.dtm.vo.DtmDmapHistDtVO;
import incheon.ags.dtm.vo.DtmDmapDtVO;
import incheon.ags.mrb.analysis.web.SpatialAnalysisController;
import incheon.cmm.g2f.layer.vo.TaskLayerSearchRequestDTO;
import incheon.cmm.g2f.layer.vo.TaskLayerVO;
import incheon.com.security.annotation.RequirePermission;
import incheon.com.security.util.SecurityUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;

import javax.servlet.http.HttpServletResponse;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.util.FileCopyUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;

@Controller
@RequestMapping("/dtm")
@RequiredArgsConstructor
@Slf4j
public class agsDtmController {

	private static final Logger logger = LoggerFactory.getLogger(SpatialAnalysisController.class);
	private final DtmService dtmService;
	@Value("${gis.server.url}")
	private String BASE_URL;
    @Autowired private ObjectMapper objectMapper;

	@GetMapping("/main.do")
	public String viewMap(ModelMap model) throws Exception {
		if (!SecurityUtil.canAccessSystem("DTM")) {
			return "redirect:/error/403";
		}
//        LoginVO user = RequestContext.getCurrentUser();
//        String userId = RequestContext.getCurrentUserId();
//        String userName = RequestContext.getCurrentUserName();
//        String deptCd = RequestContext.getCurrentUserDeptCd();
//        String menuCd = RequestContext.getCurrentMenuCd();
//        String sysCd = RequestContext.getCurrentSysCd();

		Map<String, Boolean> permission_data = new HashMap<>();
		/* 정보공개 등급별 데이터 권한 */
		permission_data.put("permission_data_unclassified", SecurityUtil.hasPermission("DTM", "PERM_DATA_DTMUN"));
		permission_data.put("permission_data_public", SecurityUtil.hasPermission("DTM", "PERM_DATA_DTMPB"));
		permission_data.put("permission_data_limit", SecurityUtil.hasPermission("DTM", "PERM_DATA_DTMLM"));
		permission_data.put("permission_data_private", SecurityUtil.hasPermission("DTM", "PERM_DATA_DTMPR"));
		/* 관리자 및 담당자 권한 */
		permission_data.put("permission_data_admin", SecurityUtil.hasRole("DTM", "ROLE_DTM_ADMIN"));
		permission_data.put("permission_data_task", SecurityUtil.hasRole("DTM", "ROLE_DTM_TASK"));

		String jsonPermissionData = objectMapper.writeValueAsString(permission_data);

		model.addAttribute("permission_data", jsonPermissionData);
		model.addAttribute("isAdmin", SecurityUtil.hasRole("DTM", "ROLE_DTM_ADMIN"));
		model.addAttribute("isTaskAdmin", SecurityUtil.hasRole("DTM", "ROLE_DTM_TASK"));
		List<DtmComboVO> bizList = dtmService.selectBizList();
		model.addAttribute("bizList", bizList);

		return "ags/dtm/dtmView";
	}

	@GetMapping("/getLayerList.do")
	public ResponseEntity<Map<String, Object>> getLayerList(TaskLayerSearchRequestDTO searchVO) {
		Map<String, Object> response = new HashMap<>();

		try {
			searchVO.setLyrGroupCd("pba");
			List<TaskLayerVO> taskLayerList = dtmService.getLayerList(searchVO);
			response.put("taskLayerList", taskLayerList);
			searchVO.setLyrGroupCd("lnd");
			List<TaskLayerVO> lndLayerList = dtmService.getLayerList(searchVO);
			response.put("lndLayerList", lndLayerList);

			return ResponseEntity.ok(response);

		} catch (DataIntegrityViolationException e) {
			logger.warn("시스템 오류가 발생했습니다: {}", "레이어 목록 조회", e);
			return ResponseEntity.status(HttpStatus.CONFLICT).body(response);

		} catch (NullPointerException e) {
			logger.error("시스템 오류가 발생했습니다: {}", "레이어 목록 조회", e);
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();

		} catch (DataAccessException e) {
			logger.error("시스템 오류가 발생했습니다: {}", "레이어 목록 조회", e);
			return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build();

		} catch (Exception e) {
			logger.error("시스템 오류가 발생했습니다: {}", "레이어 목록 조회", e);
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
		}
	}

	@GetMapping("/dmapDtList.do")
	@RequirePermission(system = "DTM", permissions = "PERM_FUNC_DTMR")
	public ResponseEntity<Map<String, Object>> selectDmapDtList(@RequestParam(defaultValue = "1") int page,
			@RequestParam(required = false) List<String> searchMapNoList,
			@RequestParam(required = false) String searchMapNo, @RequestParam(required = false) String searchScale,
			@RequestParam(required = false) String searchBizCd, @RequestParam(required = false) String searchPubYn,
			@RequestParam(required = false) String sortKey, @RequestParam(required = false) String sortDirection) {
		DtmDmapDtVO dtmDmapDtVO = new DtmDmapDtVO();
		PaginationInfo paginationInfo = new PaginationInfo();
		Map<String, Object> response = new HashMap<>();
		Map<String, Boolean> permission_func = new HashMap<>();

		try {
			/* CRUD 기능별 권한 */
			permission_func.put("permission_func_create", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMC"));
			permission_func.put("permission_func_read", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMR"));
			permission_func.put("permission_func_update", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMU"));
			permission_func.put("permission_func_delete", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMD"));

			dtmDmapDtVO.setPageIndex(page);

			dtmDmapDtVO.setMapNoList(searchMapNoList);
			dtmDmapDtVO.setMapno(searchMapNo);
			dtmDmapDtVO.setBizCd(searchBizCd);
			dtmDmapDtVO.setRlsYn(searchPubYn);
			dtmDmapDtVO.setScale(searchScale);
			dtmDmapDtVO.setSortKey(sortKey);
			dtmDmapDtVO.setSortDirection(sortDirection);
			dtmDmapDtVO.setPageSize(3);

			paginationInfo.setCurrentPageNo(page);
			paginationInfo.setRecordCountPerPage(dtmDmapDtVO.getRecordCountPerPage());
			paginationInfo.setPageSize(dtmDmapDtVO.getPageSize());

			dtmDmapDtVO.setFirstIndex(paginationInfo.getFirstRecordIndex());
			dtmDmapDtVO.setLastIndex(paginationInfo.getLastRecordIndex());
			dtmDmapDtVO.setRecordCountPerPage(paginationInfo.getRecordCountPerPage());

			List<DtmDmapDtVO> resultList = dtmService.selectDmapDtList(dtmDmapDtVO);

			response.put("permission_func", permission_func);
			response.put("result", resultList);
			return ResponseEntity.ok(response);

		} catch (DataIntegrityViolationException e) {
			logger.warn("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.CONFLICT).body(response);

		} catch (NullPointerException e) {
			logger.error("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();

		} catch (DataAccessException e) {
			logger.error("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build();

		} catch (Exception e) {
			logger.error("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
		}
	}

	@GetMapping("/dmapDtListBySig.do")
	@RequirePermission(system = "DTM", permissions = "PERM_FUNC_DTMR")
	public ResponseEntity<Map<String, Object>> selectDmapDtListBySig(@RequestParam(defaultValue = "1") int page,
			@RequestParam(required = false) String searchSigCd, @RequestParam(required = false) String searchScale,
			@RequestParam(required = false) String searchPubYn,
			@RequestParam(required = false) String sortKey, @RequestParam(required = false) String sortDirection) {
		DtmDmapDtVO dtmDmapDtVO = new DtmDmapDtVO();
		PaginationInfo paginationInfo = new PaginationInfo();
		Map<String, Object> response = new HashMap<>();
		Map<String, Boolean> permission_func = new HashMap<>();

		try {
			/* CRUD 기능별 권한 */
			permission_func.put("permission_func_create", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMC"));
			permission_func.put("permission_func_read", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMR"));
			permission_func.put("permission_func_update", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMU"));
			permission_func.put("permission_func_delete", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMD"));

			dtmDmapDtVO.setPageIndex(page);

			dtmDmapDtVO.setSigCd(searchSigCd);
			dtmDmapDtVO.setRlsYn(searchPubYn);
			dtmDmapDtVO.setScale(searchScale);
			dtmDmapDtVO.setSortKey(sortKey);
			dtmDmapDtVO.setSortDirection(sortDirection);
			dtmDmapDtVO.setPageSize(3);

			paginationInfo.setCurrentPageNo(page);
			paginationInfo.setRecordCountPerPage(dtmDmapDtVO.getRecordCountPerPage());
			paginationInfo.setPageSize(dtmDmapDtVO.getPageSize());

			dtmDmapDtVO.setFirstIndex(paginationInfo.getFirstRecordIndex());
			dtmDmapDtVO.setLastIndex(paginationInfo.getLastRecordIndex());
			dtmDmapDtVO.setRecordCountPerPage(paginationInfo.getRecordCountPerPage());

			List<DtmDmapDtVO> resultList = dtmService.selectDmapDtListBySig(dtmDmapDtVO);

			response.put("permission_func", permission_func);
			response.put("result", resultList);
			return ResponseEntity.ok(response);

		} catch (DataIntegrityViolationException e) {
			logger.warn("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.CONFLICT).body(response);

		} catch (NullPointerException e) {
			logger.error("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();

		} catch (DataAccessException e) {
			logger.error("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build();

		} catch (Exception e) {
			logger.error("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
		}
	}

	@GetMapping("/dmapDtListByEmd.do")
	@RequirePermission(system = "DTM", permissions = "PERM_FUNC_DTMR")
	public ResponseEntity<Map<String, Object>> selectDmapDtListByEmd(@RequestParam(defaultValue = "1") int page,
			@RequestParam(required = false) String searchEmdCd, @RequestParam(required = false) String searchScale,
			@RequestParam(required = false) String searchPubYn,
			@RequestParam(required = false) String sortKey, @RequestParam(required = false) String sortDirection) {
		DtmDmapDtVO dtmDmapDtVO = new DtmDmapDtVO();
		PaginationInfo paginationInfo = new PaginationInfo();
		Map<String, Object> response = new HashMap<>();
		Map<String, Boolean> permission_func = new HashMap<>();

		try {
			/* CRUD 기능별 권한 */
			permission_func.put("permission_func_create", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMC"));
			permission_func.put("permission_func_read", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMR"));
			permission_func.put("permission_func_update", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMU"));
			permission_func.put("permission_func_delete", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMD"));

			dtmDmapDtVO.setPageIndex(page);

			dtmDmapDtVO.setEmdCd(searchEmdCd);
			dtmDmapDtVO.setRlsYn(searchPubYn);
			dtmDmapDtVO.setScale(searchScale);
			dtmDmapDtVO.setSortKey(sortKey);
			dtmDmapDtVO.setSortDirection(sortDirection);
			dtmDmapDtVO.setPageSize(3);

			paginationInfo.setCurrentPageNo(page);
			paginationInfo.setRecordCountPerPage(dtmDmapDtVO.getRecordCountPerPage());
			paginationInfo.setPageSize(dtmDmapDtVO.getPageSize());

			dtmDmapDtVO.setFirstIndex(paginationInfo.getFirstRecordIndex());
			dtmDmapDtVO.setLastIndex(paginationInfo.getLastRecordIndex());
			dtmDmapDtVO.setRecordCountPerPage(paginationInfo.getRecordCountPerPage());

			List<DtmDmapDtVO> resultList = dtmService.selectDmapDtListByEmd(dtmDmapDtVO);

			response.put("permission_func", permission_func);
			response.put("result", resultList);
			return ResponseEntity.ok(response);

		} catch (DataIntegrityViolationException e) {
			logger.warn("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.CONFLICT).body(response);

		} catch (NullPointerException e) {
			logger.error("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();

		} catch (DataAccessException e) {
			logger.error("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build();

		} catch (Exception e) {
			logger.error("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
		}
	}

	@GetMapping("/dmapDtDetail.do")
	@RequirePermission(system = "DTM", permissions = "PERM_FUNC_DTMR")
	public ResponseEntity<Map<String, Object>> searchDmapDtDetail(@RequestParam(required = false) String searchMapNo,
			@RequestParam(required = false) String searchScale) {
		Map<String, Object> response = new HashMap<>();
		try {
			DtmDmapDtVO dtmDmapDtVO = new DtmDmapDtVO();
			dtmDmapDtVO.setMapno(searchMapNo);
			dtmDmapDtVO.setScale(searchScale);

			List<DtmDmapHistDtVO> dmapHistVO = dtmService.searchDmapDtDetail(dtmDmapDtVO);
			
			response.put("result", dmapHistVO);
			return ResponseEntity.ok(response);

		} catch (DataIntegrityViolationException e) {
			logger.warn("시스템 오류가 발생했습니다: {}", "상세 조회", e);
			return ResponseEntity.status(HttpStatus.CONFLICT).body(response);

		} catch (NullPointerException e) {
			logger.error("시스템 오류가 발생했습니다: {}", "상세 조회", e);
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();

		} catch (DataAccessException e) {
			logger.error("시스템 오류가 발생했습니다: {}", "상세 조회", e);
			return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build();

		} catch (Exception e) {
			logger.error("시스템 오류가 발생했습니다: {}", "상세 조회", e);
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
		}
	}

	@GetMapping("/applyDmapDtList.do")
	@RequirePermission(system = "DTM", permissions = "PERM_FUNC_DTMR")
	public ResponseEntity<Map<String, Object>> selectApplyDmapDtList(@RequestParam(defaultValue = "1") int page,
			@RequestParam(required = false) String searchAplySn, @RequestParam(required = false) String sortKey,
			@RequestParam(required = false) String sortDirection) {
		DtmDmapDtVO dtmDmapDtVO = new DtmDmapDtVO();
		PaginationInfo paginationInfo = new PaginationInfo();
		Map<String, Object> response = new HashMap<>();
		Map<String, Boolean> permission_func = new HashMap<>();

		try {
			/* CRUD 기능별 권한 */
			permission_func.put("permission_func_create", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMC"));
			permission_func.put("permission_func_read", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMR"));
			permission_func.put("permission_func_update", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMU"));
			permission_func.put("permission_func_delete", SecurityUtil.hasPermission("DTM", "PERM_FUNC_DTMD"));

			dtmDmapDtVO.setPageIndex(page);

			dtmDmapDtVO.setAplySn(searchAplySn);
			dtmDmapDtVO.setSortKey(sortKey);
			dtmDmapDtVO.setSortDirection(sortDirection);
			dtmDmapDtVO.setPageSize(3);

			paginationInfo.setCurrentPageNo(page);
			paginationInfo.setRecordCountPerPage(dtmDmapDtVO.getRecordCountPerPage());
			paginationInfo.setPageSize(dtmDmapDtVO.getPageSize());

			dtmDmapDtVO.setFirstIndex(paginationInfo.getFirstRecordIndex());
			dtmDmapDtVO.setLastIndex(paginationInfo.getLastRecordIndex());
			dtmDmapDtVO.setRecordCountPerPage(paginationInfo.getRecordCountPerPage());

			List<DtmDmapDtVO> resultList = dtmService.selectApplyDmapDtList(dtmDmapDtVO);

			response.put("permission_func", permission_func);
			response.put("result", resultList);
			return ResponseEntity.ok(response);

		} catch (DataIntegrityViolationException e) {
			logger.warn("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.CONFLICT).body(response);

		} catch (NullPointerException e) {
			logger.error("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();

		} catch (DataAccessException e) {
			logger.error("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.SERVICE_UNAVAILABLE).build();

		} catch (Exception e) {
			logger.error("시스템 오류가 발생했습니다: {}", "도엽 리스트 조회", e);
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(response);
		}
	}
	//수치지형도 신청서 양식 파일 다운로드
	@GetMapping("/applicationDown.do")
	public void download(HttpServletResponse response) throws Exception {
		// log.info("dtm => applicationDown");
		dtmService.downloadFile(response);
	}

	

	// Ajax 요청 처리용 JSON 응답
	@RequestMapping(value = "/getAddrCode.do", method = RequestMethod.POST, produces = "application/json; charset=UTF-8")
	@ResponseBody
	public List<DtmAddressVO> getAddressAjax(@RequestBody DtmAddressVO filterVO) {
		log.info("DtmAddressVO=" + filterVO.toString());
		List<DtmAddressVO> addr = dtmService.getAddrCodeByPoi(filterVO);
		log.info("AddressVO=" + addr.toString());
		// model.addAttribute("tdr", tdr);
		return addr;
	}

	// 수치지형도 신청 등록
	@PostMapping(value = "/insertApply.do", consumes = MediaType.MULTIPART_FORM_DATA_VALUE, produces = "application/json; charset=UTF-8")
	@ResponseBody
	public Map<String, Object> insertApply(@RequestPart("reqUse") String reqUse, @RequestPart("rlsYn") String rlsYn,
			@RequestPart("appfileType") String appfileType, @RequestPart("reqList") String reqList,
			@RequestPart("files") List<MultipartFile> files, HttpSession session) {

		Map<String, Object> result = new HashMap<>();

		try {
			logger.info("[DTM][REQ][STEP-1] multipart 요청 수신");

			logger.info("[DTM][REQ][STEP-2] reqList JSON 파싱 시작");
			List<DtmApplyItemVO> items = objectMapper.readValue(reqList, new TypeReference<List<DtmApplyItemVO>>() {
			});
			logger.info("[DTM][REQ][STEP-2] 신청 도엽 수={}", items.size());

			logger.info("[DTM][REQ][STEP-3] 서비스 호출");
			dtmService.insertApply(reqUse, rlsYn, appfileType, items, files, session);

			result.put("result", "SUCCESS");
			result.put("message", "신청 완료");

			logger.info("[DTM][REQ][STEP-4] 신청 완료");

		} catch (Exception e) {
			logger.error("[DTM][REQ][ERROR] 신청 실패", e);
			result.put("result", "FAIL");
			result.put("message", "신청 처리에 실패했습니다. 잠시 후 다시 시도해 주세요.");
		}

		return result;
	}

	@PostMapping(value = "/searchApplyList.do", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
	@ResponseBody
	public ResponseEntity<List<DtmApplyListVO>> selectApplyList(@RequestBody DtmApplySearchVO vo, HttpSession session) {
		try {
			log.info("[DTM][SEARCH] searchApplyList 요청: {}", vo);

			if (vo == null) {
				log.warn("[DTM][SEARCH] 요청 VO가 null");
				return ResponseEntity.badRequest().build();
			}

			List<DtmApplyListVO> result = dtmService.selectApplyList(vo,session);

			log.info("[DTM][SEARCH] 조회 결과 건수={}", result != null ? result.size() : 0);

			return ResponseEntity.ok(result);

		} catch (Exception e) {
			log.error("[DTM][SEARCH][ERROR] 신청내역 조회 실패", e);
			return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build();
		}
	}

	// 관리자 인수증 업로드
	@PostMapping("/adminUploadCert.do")
	@ResponseBody
	public Map<String, Object> uploadCert(@RequestParam("file") MultipartFile file, @RequestParam("aplySn") String aplySn,
			@RequestParam("reqYear") String reqYear) {

		Map<String, Object> result = new HashMap<>();

		try {
			dtmService.uploadCertFile(file, aplySn, reqYear);
			result.put("success", true);
		} catch (IllegalStateException e) {
			result.put("success", false);
			result.put("message", e.getMessage());
		} catch (Exception e) {
			result.put("success", false);
			result.put("message", "서버 처리 중 오류가 발생했습니다.");
		}

		return result;
	}

	// 관리자 승인
	@PostMapping("/approve.do")
	@ResponseBody
	public Map<String, Object> approve(@RequestBody DtmApproveVO vo, HttpServletRequest request, HttpSession session) {
		Map<String, Object> result = new HashMap<>();
		log.info("=== [API] /approve.do START === aplySn={}", vo.getAplySn());

		try {
			String basePath = request.getServletContext().getRealPath("/");
			log.info("[SERVICE CALL] approveDigitalMap 호출. aplySn={}", vo.getAplySn());

			dtmService.approveDigitalMap(vo, basePath, session);

			result.put("result", "OK");
			log.info("[SERVICE CALL] approveDigitalMap 정상 종료. aplySn={}", vo.getAplySn());
			log.info("[RESPONSE] result=OK, aplySn={}", vo.getAplySn());

		} catch (Exception e) {
			result.put("result", "FAIL");
			result.put("message", e.getMessage());
			log.error("[SERVICE ERROR] aplySn={}, message={}", vo.getAplySn(), e.getMessage(), e);
		}

		log.info("=== [API] /approve.do END === aplySn={}", vo.getAplySn());
		return result;
	}

	@PostMapping("/applyCancel.do")
	@ResponseBody
	public Map<String, Object> applyCancel(@RequestBody DtmApproveVO vo ,HttpSession session) {

		Map<String, Object> result = new HashMap<>();

		try {
			dtmService.applyCancel(vo, session);
			result.put("success", true);
		} catch (Exception e) {
			result.put("success", false);
			result.put("message", e.getMessage());
		}

		return result;
	}

	@RequestMapping("/download.do")
	public void download(@RequestParam("aplySn") long aplySn, HttpServletResponse response) throws Exception {

		DmapDownloadVO vo = dtmService.selectDownloadInfo(aplySn);
		if (vo == null) {
			response.setContentType("text/plain; charset=UTF-8");
			response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
			response.getWriter().write("요청하신 자료의 다운로드 정보가 존재하지 않습니다.");
			return;
		}

		if (!"Y".equals(vo.getDownYn())) {
			response.setContentType("text/plain; charset=UTF-8");
			response.setStatus(HttpServletResponse.SC_FORBIDDEN);
			response.getWriter().write("다운로드 권한이 없거나 기간이 만료되었습니다.");
			return;
		}

		File file = new File(vo.getDwnldFlpth());
		if (!file.exists()) {
			response.setStatus(HttpServletResponse.SC_NOT_FOUND);
			response.setContentType("text/plain; charset=UTF-8");
			response.getWriter().write("서버에 실제 파일이 존재하지 않습니다. 관리자에게 문의해주세요.");
			return;
		}

		String encodedFileName = URLEncoder.encode(file.getName(), StandardCharsets.UTF_8.toString()).replaceAll("\\+",
				"%20");

		response.setContentType("application/octet-stream");
		response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\"");
		response.setContentLengthLong(file.length());

		try (InputStream in = new FileInputStream(file); OutputStream out = response.getOutputStream()) {
			FileCopyUtils.copy(in, out);
		}
	}

	@RequestMapping("/reqDownloadApplyFile.do")
	public void downloadRequestZip(@RequestParam("aplySn") long aplySn, @RequestParam("reqYear") String reqYear,
			HttpServletResponse response) throws Exception {

		File zipFile = dtmService.createRequestZip(aplySn, reqYear);

		if (zipFile == null || !zipFile.exists()) {
			throw new RuntimeException("신청서 파일이 존재하지 않습니다.");
		}

		String encodedFileName = URLEncoder.encode(zipFile.getName(), StandardCharsets.UTF_8).replaceAll("\\+", "%20");

		response.setContentType("application/octet-stream");
		response.setHeader("Content-Disposition", "attachment; filename=\"" + encodedFileName + "\"");
		response.setContentLengthLong(zipFile.length());

		try (InputStream in = new FileInputStream(zipFile); OutputStream out = response.getOutputStream()) {
			FileCopyUtils.copy(in, out);
		}
	}

}