package incheon.ags.aip.web;

import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.InvalidPathException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.time.LocalDate;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
import java.util.UUID;

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.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.security.core.Authentication;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
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.multipart.MultipartFile;
import org.springframework.web.server.ResponseStatusException;

import incheon.ags.aip.service.AipIntprService;
import incheon.ags.aip.util.AipAuthUtil;
import incheon.ags.aip.util.AipFileUtil;
import incheon.ags.aip.util.AipAuthUtil.AipAuthInfo;
import incheon.ags.aip.vo.AipIntprSearchVO;
import incheon.ags.aip.vo.AipIntprVO;
import incheon.com.cmm.api.DefaultApiResponse;
import incheon.com.cmm.service.EgovProperties;
import lombok.RequiredArgsConstructor;

@Controller
@RequestMapping("/aip/intpr")
@RequiredArgsConstructor
public class AipIntprController {
	private static final Logger log = LoggerFactory.getLogger(AipIntprController.class);

	private final AipIntprService aipIntprService;
	private final AipFileUtil aipFileUtils;

	private String newErrId(){
		return UUID.randomUUID().toString();
	}

	private ResponseStatusException viewServerError(String op, RuntimeException e){
		String errId = newErrId();
		log.error("AipIntprController {} failed. errId={}", op, errId, e);
		return new ResponseStatusException(HttpStatus.INTERNAL_SERVER_ERROR, "처리 중 오류가 발생했습니다. errId=" + errId, e);
	}

	private ResponseEntity<DefaultApiResponse<Map<String, Object>>> intprServerError(String op, RuntimeException e){
		String errId = newErrId();
		log.error("AipIntprController {} failed. errId={}", op, errId, e);
		return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
				.body(DefaultApiResponse.error(500, "처리 중 오류가 발생했습니다.", "errId=" + errId));
	}

	private ResponseEntity<DefaultApiResponse<Map<String, Object>>> intprServerError(String op, IOException e){
		String errId = newErrId();
		log.error("AipIntprController {} failed. errId={}", op, errId, e);
		return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
				.body(DefaultApiResponse.error(500, "처리 중 오류가 발생했습니다.", "errId=" + errId));
	}

	@GetMapping("/getIntprList.do")
	public String getIntprList(@RequestParam(defaultValue = "1") int page, @ModelAttribute AipIntprSearchVO vo,
			Authentication authentication, ModelMap model) throws Exception{
		try{
			AipAuthInfo auth = AipAuthUtil.from(authentication);
			if(!auth.isAdmin()) vo.setUser_id(auth.getLoginUserId());

			int totalCount = aipIntprService.getIntprCount(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>> intprList = aipIntprService.getIntprList(vo);

			model.addAttribute("isTask", auth.isTask());
			model.addAttribute("intprList", intprList);
			model.addAttribute("totalCount", totalCount);
			model.addAttribute("paginationInfo", paginationInfo);
			model.addAttribute("searchVO", vo);

			return "ags/aip/popup/intprPop";
		}catch(RuntimeException e){
			throw viewServerError("getIntprList", e);
		}
	}

	@GetMapping("/intprDetail.do")
	public String intprDetail(@RequestParam(value = "rqstId", required = false) Long rqstId,
			Authentication authentication, ModelMap model) throws Exception{
		try{
			AipAuthInfo auth = AipAuthUtil.from(authentication);
			String stat;

			Map<String, Object> rqstMap = null;
			List<Map<String, Object>> rqstFileList = null;
			Map<String, Object> rsltMap = null;
			List<Map<String, Object>> rsltFileList = null;

			if(rqstId == null){
				stat = "INS";
			}else{
				Map<String, Object> commandMap = new HashMap<>();
				commandMap.put("intpr_rqst_idntfr", rqstId);

				rqstMap = aipIntprService.getRqstMap(commandMap);
				rqstFileList = aipIntprService.getRqstFileList(commandMap);

				String stts = null;
				if(rqstMap != null && rqstMap.get("rqst_stts") != null) stts = String.valueOf(rqstMap.get("rqst_stts"));

				if("WRT000".equals(stts)) stat = "WRT000";
				else if("WRT001".equals(stts)) stat = "WRT001";
				else if("WRT002".equals(stts)) stat = "WRT002";
				else stat = "WRT001";

				if(auth.isAdmin()){
					if(!"WRT000".equals(stts)) {
						rsltMap = aipIntprService.getRsltMap(commandMap);
						rsltFileList = aipIntprService.getRsltFileList(commandMap);
					}
				}else if(auth.isTask()){
					if("WRT002".equals(stat)){
						rsltMap = aipIntprService.getRsltMap(commandMap);
						rsltFileList = aipIntprService.getRsltFileList(commandMap);
					}
				}
			}

			model.addAttribute("rqstMap", rqstMap);
			model.addAttribute("rqstFileList", rqstFileList);
			model.addAttribute("rsltMap", rsltMap);
			model.addAttribute("rsltFileList", rsltFileList);
			model.addAttribute("isAdmin", auth.isAdmin());
			model.addAttribute("isTask", auth.isTask());
			model.addAttribute("stat", stat);

			if("INS".equals(stat)) return "ags/aip/popup/intprReq";
			return "ags/aip/popup/intprRes";
		}catch(RuntimeException e){
			throw viewServerError("intprDetail", e);
		}
	}

	@PostMapping(value = "/insertIntpr.do", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
	@ResponseBody
	public ResponseEntity<DefaultApiResponse<Map<String, Object>>> updateIntpr(@ModelAttribute AipIntprVO vo,
			@RequestParam("part") String part, Authentication authentication,
			@RequestParam(value = "files", required = false) List<MultipartFile> files,
			@RequestParam(value = "file_idntfr", required = false) List<String> fileIdList) throws Exception{

		Map<String, Object> result = new HashMap<>();

		try{
			String basePath = EgovProperties.getProperty("Globals.resources.path");
			Path baseRoot = Paths.get(basePath).toAbsolutePath().normalize();
			AipAuthInfo auth = AipAuthUtil.from(authentication);

			vo.setUser_id(auth.getLoginUserId());

			String partKey = "rs".equalsIgnoreCase(part) ? "rs" : "rq";

			Long rqstId = vo.getIntpr_rqst_idntfr();
			Long rsltId = vo.getIntpr_rslt_idntfr();

			if("rq".equals(partKey)){
				if(rqstId != null){
					aipIntprService.updateRqst(vo);
				}else{
					aipIntprService.insertRqst(vo);
					rqstId = vo.getIntpr_rqst_idntfr();
				}
			}else{
				if(rsltId != null){
					aipIntprService.updateRslt(vo);
				}else{
					aipIntprService.insertRslt(vo);
					rsltId = vo.getIntpr_rslt_idntfr();

					if(rqstId != null){
						Map<String, Object> commandMap = new HashMap<>();
						commandMap.put("intpr_rqst_idntfr", rqstId);
						commandMap.put("rqst_stts", "WRT001");
						commandMap.put("user_id", auth.getLoginUserId());
						aipIntprService.updateStts(commandMap);
					}
				}
			}

			if(fileIdList != null && !fileIdList.isEmpty()){
				List<Map<String, Object>> delTargets;
				if("rq".equals(partKey)) delTargets = aipIntprService.deleteRqstFiles(fileIdList);
				else delTargets = aipIntprService.deleteRsltFiles(fileIdList);

				for(Map<String, Object> map : delTargets){
					String filePath = (String) map.get("flpth");
					if(filePath == null || filePath.isEmpty()) continue;

					try{
						boolean ok = aipFileUtils.deleteByDbPath(filePath);
						if(!ok) log.warn("파일 삭제 실패: {}", filePath);
					}catch(Exception ex){
						log.warn("파일 삭제 실패: {}", filePath);
					}
				}
			}

			if(files != null && !files.isEmpty()){
				LocalDate today = LocalDate.now();
				int year = today.getYear();
				int month = today.getMonthValue();

				List<AipFileUtil.SavedFile> saved = aipFileUtils.storeIntprFiles(partKey, files, year, month);

				for(AipFileUtil.SavedFile sf : saved){
					Map<String, Object> fileRow = new HashMap<>();
					fileRow.put("file_uuid", sf.getFileUuid());

					if("rq".equals(partKey)) fileRow.put("intpr_rqst_idntfr", rqstId);
					else fileRow.put("intpr_rslt_idntfr", rsltId);

					fileRow.put("file_nm", Paths.get(sf.getOriginalFileNm()).getFileName().toString());
					fileRow.put("file_path", sf.getDbFilePath());
					fileRow.put("size", sf.getSize());
					fileRow.put("content_type", sf.getContentType());
					fileRow.put("user_id", auth.getLoginUserId());

					if("rq".equals(partKey)) aipIntprService.insertRqstFile(fileRow);
					else aipIntprService.insertRsltFile(fileRow);
				}
			}


			result.put("intpr_rqst_idntfr", rqstId);
			result.put("intpr_rslt_idntfr", rsltId);

			String msg = "rq".equals(partKey) ? "의뢰 내용 저장 완료" : "결과 내용 저장 완료";
			return ResponseEntity.ok(DefaultApiResponse.success(result, msg));

		}catch(IOException e){
			return intprServerError("updateIntpr", e);
		}catch(RuntimeException e){
			return intprServerError("updateIntpr", e);
		}
	}

	@PostMapping("/completeIntpr.do")
	@ResponseBody
	public ResponseEntity<DefaultApiResponse<Map<String, Object>>> completeIntpr(@RequestParam("rqstId") Long rqstId, Authentication authentication) throws Exception{
		Map<String, Object> result = new HashMap<>();

		try{
			AipAuthInfo auth = AipAuthUtil.from(authentication);

			if(!auth.isAdmin()){
				return ResponseEntity.status(HttpStatus.FORBIDDEN).body(DefaultApiResponse.error(403, "권한이 없습니다.", null));
			}

			if(rqstId == null){
				return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(DefaultApiResponse.error(400, "요청ID가 없습니다.", null));
			}

			Map<String, Object> commandMap = new HashMap<>();
			commandMap.put("intpr_rqst_idntfr", rqstId);
			commandMap.put("rqst_stts", "WRT002");
			commandMap.put("user_id", auth.getLoginUserId());

			aipIntprService.updateStts(commandMap);

			result.put("intpr_rqst_idntfr", rqstId);
			return ResponseEntity.ok(DefaultApiResponse.success(result, "작성완료 처리되었습니다."));

		}catch(RuntimeException e){
			return intprServerError("completeIntpr", e);
		}
	}

	@PostMapping("/deleteIntpr.do")
	@ResponseBody
	public ResponseEntity<DefaultApiResponse<Map<String, Object>>> deleteIntpr(@RequestParam("rqstId") Long rqstId, @RequestParam("part") String part, Authentication authentication) throws Exception{
		Map<String, Object> result = new HashMap<>();

		try{
			AipAuthInfo auth = AipAuthUtil.from(authentication);

			if(rqstId == null){
				return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(DefaultApiResponse.error(400, "내역이 존재하지 않습니다.", null));
			}

			String partKey = "rs".equalsIgnoreCase(part) ? "rs" : "rq";
			boolean isRs = "rs".equals(partKey);

			Map<String, Object> rqCmd = new HashMap<>();
			rqCmd.put("intpr_rqst_idntfr", rqstId);

			Map<String, Object> rqstMap = aipIntprService.getRqstMap(rqCmd);
			if(rqstMap == null){
				return ResponseEntity.status(HttpStatus.BAD_REQUEST).body(DefaultApiResponse.error(400, "내역이 존재하지 않습니다.", null));
			}

			String stts = null;
			if(rqstMap.get("rqst_stts") != null) stts = String.valueOf(rqstMap.get("rqst_stts"));

			if(isRs){
				if(!auth.isAdmin()){
					return ResponseEntity.status(HttpStatus.FORBIDDEN).body(DefaultApiResponse.error(403, "권한이 없습니다.", null));
				}
			}else{
				if(!(auth.isAdmin() || auth.isTask())){
					return ResponseEntity.status(HttpStatus.FORBIDDEN).body(DefaultApiResponse.error(403, "권한이 없습니다.", null));
				}

				if(auth.isTask() && !auth.isAdmin()){
					if(!"WRT000".equals(stts) && !"WRT001".equals(stts)){
						return ResponseEntity.status(HttpStatus.FORBIDDEN).body(DefaultApiResponse.error(403, "삭제할 수 없는 상태입니다.", null));
					}
				}
			}

			String basePath = EgovProperties.getProperty("Globals.resources.path");
			Path baseRoot = Paths.get(basePath).toAbsolutePath().normalize();

			List<Map<String, Object>> delTargets;

			if(isRs){
				delTargets = aipIntprService.deleteRsltCascadeByRqstId(rqstId);

				Map<String, Object> sttsMap = new HashMap<>();
				sttsMap.put("intpr_rqst_idntfr", rqstId);
				sttsMap.put("rqst_stts", "WRT000");
				sttsMap.put("user_id", auth.getLoginUserId());
				aipIntprService.updateStts(sttsMap);
			}else{
				delTargets = aipIntprService.deleteRqstCascadeByRqstId(rqstId, auth);
			}

			if(delTargets != null){
				for(Map<String, Object> map : delTargets){
					String filePath = String.valueOf(map.get("file_path"));
					if(filePath == null || filePath.isEmpty()) continue;

					try{
						String prefix = "/resources/";
						String relPath = filePath.startsWith(prefix) ? filePath.substring(prefix.length()) : filePath;
						Path target = baseRoot.resolve(relPath).normalize();
						if(!target.startsWith(baseRoot)){
							log.warn("파일 삭제 경로 차단: {}", filePath);
							continue;
						}
						Files.deleteIfExists(target);
					}catch(IOException | SecurityException | InvalidPathException ex){
						log.warn("파일 삭제 실패: {}", filePath);
					}
				}
			}

			result.put("intpr_rqst_idntfr", rqstId);
			return ResponseEntity.ok(DefaultApiResponse.success(result, isRs ? "결과가 삭제되었습니다." : "의뢰가 삭제되었습니다."));

		}catch(RuntimeException e){
			return intprServerError("deleteIntpr", e);
		}
	}
}
