package incheon.ags.aip.web;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.egovframe.rte.ptl.mvc.tags.ui.pagination.PaginationInfo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.util.FileCopyUtils;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.server.ResponseStatusException;

import incheon.ags.aip.service.AipFlightService;
import incheon.ags.aip.service.AipService;
import incheon.ags.aip.util.AipAuthUtil;
import incheon.ags.aip.util.AipAuthUtil.AipAuthInfo;
import incheon.ags.aip.util.AipFileUtil;
import incheon.ags.aip.vo.AipSearchVO;
import incheon.cmm.g2f.layer.service.G2FLayerService;
import incheon.cmm.g2f.layer.vo.FlightPhotoLayerSearchRequestDTO;
import incheon.cmm.g2f.layer.vo.FlightPhotoLyrVO;
import incheon.com.cmm.api.DefaultApiResponse;
import lombok.RequiredArgsConstructor;

@Controller
@RequestMapping("/aip")
@RequiredArgsConstructor
public class AipController {
	private static final Logger log = LoggerFactory.getLogger(AipController.class);

	private final AipService aipService;
	private final AipFileUtil aipFileUtil;
	private final AipFlightService aiFlightService;
    private final G2FLayerService g2FLayerService;

	private ResponseStatusException serverError(String op, RuntimeException e){
	    String errId = UUID.randomUUID().toString();
	    log.error("AipController {} failed. errId={}", op, errId, e);
	    return new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "처리 중 오류가 발생했습니다. errId=" + errId, e);
	}


	/**
	 * 기본 지도 화면 (기존 호환성 유지)
	 */
	@GetMapping("/view.do")
	public String viewMap(ModelMap model, Authentication authentication) throws Exception {
		try{
			AipAuthInfo auth = AipAuthUtil.from(authentication);

			List<PanelConfig> leftPanels = Arrays.asList(
					new PanelConfig("layerSearchPanel", "/WEB-INF/jsp/ags/aip/panels/layerSearchPanel.jsp", "레이어", "left"),
					new PanelConfig("extracSearchPanel", "/WEB-INF/jsp/ags/aip/panels/extracSearchPanel.jsp", "조서검색", "left"),
					new PanelConfig("flightSearchPanel", "/WEB-INF/jsp/ags/aip/panels/flightSearchPanel.jsp", "항공영상", "left")
			);

			List<Map<String, Object>> yearList = aipService.juckYearList();

			model.addAttribute("isAdmin", auth.isAdmin());
			model.addAttribute("isTask", auth.isTask());
			model.addAttribute("stdgCd", auth.getStdgCd());
			model.addAttribute("loginUserId", auth.getLoginUserId());
			model.addAttribute("yearList", yearList);
			model.addAttribute("leftPanels", leftPanels);
			model.addAttribute("domainType", "aip");
			model.addAttribute("pageTitle", "항공사진 판독");

			return "ags/aip/aipView";
		}catch(RuntimeException e){
			throw serverError("viewMap", e);
		}
	}

	public static class PanelConfig {
		private String id;
		private String componentPath;
		private String title;
		private String position;
		private Map<String, Object> data;

		public PanelConfig(String id, String componentPath, String title) {
			this(id, componentPath, title, "left");
		}

		public PanelConfig(String id, String componentPath, String title, String position) {
			this.id = id;
			this.componentPath = componentPath;
			this.title = title;
			this.position = position;
			this.data = new HashMap<>();
		}

		public String getId() { return id; }
		public void setId(String id) { this.id = id; }
		public String getComponentPath() { return componentPath; }
		public void setComponentPath(String componentPath) { this.componentPath = componentPath; }
		public String getTitle() { return title; }
		public void setTitle(String title) { this.title = title; }
		public String getPosition() { return position; }
		public void setPosition(String position) { this.position = position; }
		public Map<String, Object> getData() { return data; }
		public void setData(Map<String, Object> data) { this.data = data; }
	}

	/**
	 * 적출년도 조회
	 */
	@PostMapping("/pop/getExtrYear.do")
	@ResponseBody
	public Map<String, Object> getExtrYear() throws Exception {
		try{
			List<Map<String, Object>> yearList = aipService.juckYearList();
			Map<String,Object> res = new HashMap<>();
			res.put("yearList", yearList);
			return res;
		}catch(RuntimeException e){
			throw serverError("getExtrYear", e);
		}
	}
	
	/**
	 * 촬영년도 조회
	 */
	@PostMapping("/pop/getFlightYear.do")
	@ResponseBody
	public Map<String, Object> getFlightYear() throws Exception {
		try{
			List<Map<String, Object>> yearList = aiFlightService.flightYearList();
			Map<String,Object> res = new HashMap<>();
			res.put("yearList", yearList);
			return res;
		}catch(RuntimeException e){
			throw serverError("getFlightYear", e);
		}
	}

	@GetMapping("/pop/intprPop.do")
	public String intprPop() {
		return "ags/aip/popup/intprPop";
	}
	
    @GetMapping("/layer/flight")
    public ResponseEntity<DefaultApiResponse<Map<String, Object>>> getFlightPhotoLayerList(FlightPhotoLayerSearchRequestDTO searchVO) {
        if (searchVO.getPageIndex() < 1) {
            searchVO.setPageIndex(1);
        }
        
        boolean otsdRlsEn = true;
        
        List<FlightPhotoLyrVO> content = null;
        content = g2FLayerService.selectFlightPhotoLayerList(searchVO, otsdRlsEn);

        Map<String, Object> response = new HashMap<>();
        response.put("content", content);
        if(searchVO.getPageSize() > 1) {
            response.put("page", searchVO.getPageIndex());
            response.put("size", searchVO.getPageSize());

            long total = g2FLayerService.selectFlightPhotoLayerListTotCnt(searchVO, otsdRlsEn);
            response.put("totalElements", total);
            response.put("totalPages", (int) Math.ceil((double) total / searchVO.getPageSize()));
        }

        return ResponseEntity.ok(DefaultApiResponse.success(response));
    }
	/**
	 * 북마크
	 */
	@GetMapping("/pop/bmkList.do")
	public String bmkList(@RequestParam(defaultValue = "1") int page, @ModelAttribute AipSearchVO vo, Authentication authentication, ModelMap model) throws Exception{
		try{
			AipAuthInfo auth = AipAuthUtil.from(authentication);
			vo.setUser_id(auth.getLoginUserId());

			int totalCount = aipService.getBmkCount(vo);

			vo.setPageIndex(page);
			PaginationInfo paginationInfo = new PaginationInfo();
			paginationInfo.setCurrentPageNo(page);
			paginationInfo.setRecordCountPerPage(vo.getRecordCountPerPage());
			paginationInfo.setPageSize(vo.getPageSize());
			paginationInfo.setTotalRecordCount(totalCount);

			vo.setFirstIndex(paginationInfo.getFirstRecordIndex());
			vo.setLastIndex(paginationInfo.getLastRecordIndex());
			vo.setRecordCountPerPage(paginationInfo.getRecordCountPerPage());

			List<Map<String,Object>> bmkList = aipService.getBmkList(vo);

			model.addAttribute("bmkList", bmkList);
			model.addAttribute("totalCount", totalCount);
			model.addAttribute("paginationInfo", paginationInfo);
			model.addAttribute("searchVO", vo);

			return "ags/aip/popup/bmkPop";
		}catch(RuntimeException e){
			throw serverError("bmkList", e);
		}
	}

	/**
	 * 북마크 삭제
	 */
	@PostMapping("/pop/deleteBmk.do")
	@ResponseBody
	public ResponseEntity<DefaultApiResponse<Map<String, Object>>> deleteBmk(@RequestParam("idxList") List<String> param, Authentication authentication) throws Exception{
		Map<String, Object> result = new HashMap<>();
		try {
			if(param == null || param.isEmpty()){
				return ResponseEntity.badRequest().body(
					DefaultApiResponse.error(400, "삭제할 항목이 없습니다.", "bad request")
				);
			}

			Map<String, Object> commandMap = new HashMap<>();
			AipAuthInfo auth = AipAuthUtil.from(authentication);
			commandMap.put("user_id", auth.getLoginUserId());

			List<Long> idxList = new ArrayList<>();
			for(String idx : param){
				if(!StringUtils.hasText(idx)) continue;
				String[] parts = idx.split(",");
				for(String p : parts){
					if(StringUtils.hasText(p)) idxList.add(Long.parseLong(p.trim()));
				}
			}

			if(idxList.isEmpty()){
				return ResponseEntity.badRequest().body(
					DefaultApiResponse.error(400, "삭제할 항목이 없습니다.", "bad request")
				);
			}

			commandMap.put("idxList", idxList);

			int deleted = aipService.deleteBookmarks(commandMap);

			result.put("deleted", deleted);
			result.put("idxList", idxList);

			return ResponseEntity.ok(DefaultApiResponse.success(result, deleted + "건 삭제되었습니다."));
		} catch (RuntimeException e) {
		    throw serverError("deleteBmk", e);
		}
	}

	/**
	 * 통계 팝업 호출
	 */
	@GetMapping("/pop/statsPop.do")
	public String statsPop(Authentication authentication, ModelMap model) throws Exception{
		try{
			AipAuthInfo auth = AipAuthUtil.from(authentication);
			model.addAttribute("stdg_cd", auth.getStdgCd());
			return "ags/aip/popup/statiPop";
		}catch(RuntimeException e){
			throw serverError("statsPop", e);
		}
	}

	/**
	 * 통계조회
	 */
	@PostMapping("/pop/getStatsList.do")
	@ResponseBody
	public Map<String, Object> getStatsList(@RequestParam Map<String, Object> commandMap) throws Exception {
		try{
			List<Map<String, Object>> totalList = aipService.getExtracObjtListTotal(commandMap);
			List<Map<String, Object>> statsList = aipService.getExtracObjtList(commandMap);
			List<Map<String, Object>> statsChart = aipService.getExtracObjtChart(commandMap);

			Map<String,Object> res = new HashMap<>();
			res.put("totalList", totalList);
			res.put("statsList", statsList);
			res.put("statsChart", statsChart);
			return res;
		}catch(RuntimeException  e){
			throw serverError("getStatsList", e);
		}
	}

	/**
	 * 엑셀업로드 팝업 호출
	 */
	@GetMapping("/pop/excelUpload.do")
	public String excelUploadPop() {
		return "ags/aip/popup/excelUpForm";
	}
	
	@GetMapping("/fileDown.do")
	public void aipFile(@RequestParam("gubun") String gubun, @RequestParam("fileId") Long fileId,
			@RequestParam(value="mode", required=false, defaultValue="download") String mode,
			HttpServletRequest request, HttpServletResponse response ) throws Exception {

		Map<String, Object> list = aipService.selectAipFile(gubun, fileId);
		if(list == null){
			throw new FileNotFoundException("파일 정보를 찾을 수 없습니다.");
		}

		String fileNm = (String) list.get("file_nm");
		String flpth = (String) list.get("flpth");
		String contentType = (String) list.get("file_type");

		File uFile = aipFileUtil.toPhysicalFile(flpth);
		long fSize = uFile.length();

		if(!uFile.isFile() || fSize <= 0){
			throw new FileNotFoundException("파일이 존재하지 않거나 크기가 0입니다.");
		}
		
		String ct = (contentType != null && !contentType.trim().isEmpty()) ? contentType.trim() : java.nio.file.Files.probeContentType(uFile.toPath());

		if(ct == null || ct.trim().isEmpty()) ct = "application/octet-stream";
		
		response.setContentType(ct);
		response.setContentLengthLong(fSize);
		
		boolean inline = "inline".equalsIgnoreCase(mode);

		String lowerCt = ct.toLowerCase(Locale.ROOT);
		boolean inlineOk = inline && (lowerCt.startsWith("image/") || "application/pdf".equals(lowerCt)) && !"image/svg+xml".equals(lowerCt);

		setDisposition(fileNm, request, response, inlineOk);

		try(BufferedInputStream in = new BufferedInputStream(new FileInputStream(uFile));
			BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream())){
			FileCopyUtils.copy(in, out);
			out.flush();
		}
	}

	private void setDisposition(String filename, HttpServletRequest request, HttpServletResponse response, boolean inline) throws Exception {
		String safeName = (filename == null) ? "download" : filename;

		String encoded = URLEncoder.encode(safeName, StandardCharsets.UTF_8).replace("+", "%20");

		String type = inline ? "inline" : "attachment";

		String cd = type
				+ "; filename=\"" + safeName.replace("\"", "") + "\""
				+ "; filename*=UTF-8''" + encoded;

		response.setHeader("Content-Disposition", cd);
		response.setHeader("X-Content-Type-Options", "nosniff");
	}


	@SuppressWarnings("unused")
	private String getBrowser(HttpServletRequest request){
		String header = request.getHeader("User-Agent");
		if(header == null) return "Firefox";
		if(header.contains("MSIE")) return "MSIE";
		if(header.contains("Trident")) return "Trident";
		if(header.contains("Chrome")) return "Chrome";
		if(header.contains("Opera")) return "Opera";
		return "Firefox";
	}

}
