package incheon.cmm.g3d.DbMngSrvList.web;

import com.fasterxml.jackson.databind.ObjectMapper;

import incheon.cmm.g2f.basemap.vo.G2FBasemapVO;
import incheon.cmm.g3d.DbMngList.service.G3DDbMngService;
import incheon.cmm.g3d.DbMngList.vo.G3DDbMngVO;
import incheon.com.cmm.api.DefaultApiResponse;
import incheon.com.security.vo.LoginVO;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.text.StringEscapeUtils;
import org.egovframe.rte.ptl.mvc.tags.ui.pagination.PaginationInfo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import javax.validation.Valid;
import java.io.IOException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.regex.Pattern;
import org.apache.commons.text.StringEscapeUtils;

@Slf4j
@Controller
@RequestMapping("/cmm/g3d/dbMngSrvList")
public class G3DDbMngSrvListController {
    @Autowired private ObjectMapper objectMapper;

    private final G3DDbMngService dbMngService;

    // API 키 설정 (application.properties에서 주입)
    @Value("${external.api.key:default-secret-key}")
    private String validApiKey;

    // 허용되지 않는 특수문자 패턴: < > ' " / \
    private static final Pattern DANGEROUS_CHARS = Pattern.compile("[<>'\"/\\\\]");

    public G3DDbMngSrvListController(G3DDbMngService dbMngService) {
        this.dbMngService = dbMngService;
    }

    @GetMapping("/dbMngSrvListList.do")
    public String listService(Model model, @RequestParam(value = "page", defaultValue = "1") int page,
                              @RequestParam(value = "size", defaultValue = "10") int size,
                              @RequestParam(required = false) String categoryFilter,
                              @RequestParam(value = "searchKeyword", required = false) String searchKeyword,
                              @RequestParam(value = "searchType", required = false) String searchType,
                              @RequestParam(value = "dgtlPairGroupCd", required = false) String dgtlPairGroupCd
                              
    		) {

        PaginationInfo paginationInfo = new PaginationInfo();
        paginationInfo.setCurrentPageNo(page);
        paginationInfo.setRecordCountPerPage(size);
        paginationInfo.setPageSize(10);

        String srvcNum = "2";
        int total = dbMngService.getTotalCount(searchKeyword, categoryFilter, searchType, srvcNum, dgtlPairGroupCd);
        paginationInfo.setTotalRecordCount(total);

        List<G3DDbMngVO> list = dbMngService.getList(searchKeyword, categoryFilter, searchType, page, size, srvcNum, dgtlPairGroupCd);

        model.addAttribute("paginationInfo", paginationInfo);
        model.addAttribute("list", list);
        model.addAttribute("total", total);
        model.addAttribute("totalCount", total);
        model.addAttribute("page", page);
        model.addAttribute("size", size);
        model.addAttribute("categoryFilter", categoryFilter);
        model.addAttribute("dgtlPairGroupCd", dgtlPairGroupCd);
        model.addAttribute("searchKeyword", searchKeyword);
        model.addAttribute("searchType", searchType);
        model.addAttribute("totalPages", (int) Math.ceil((double) total / size));

        return "cmm/g3d/dbServiceList/DbServiceList";
    }
    @PostMapping("/dbMngSrvListList")
    public String selectService(Model model, 
                              @RequestParam(value = "page", defaultValue = "1") int page,
                              @RequestParam(value = "size", defaultValue = "10") int size,
                              @RequestParam(required = false) String categoryFilter,
                              @RequestParam(value = "searchKeyword", required = false) String searchKeyword,
                              @RequestParam(value = "searchType", required = false) String searchType,
                              @RequestParam(value = "dgtlPairGroupCd", required = false) String dgtlPairGroupCd) {

        PaginationInfo paginationInfo = new PaginationInfo();
        paginationInfo.setCurrentPageNo(page);
        paginationInfo.setRecordCountPerPage(size);
        paginationInfo.setPageSize(10);

        String srvcNum = "2";
        int total = dbMngService.getTotalCount(searchKeyword, categoryFilter, searchType, srvcNum, dgtlPairGroupCd);
        paginationInfo.setTotalRecordCount(total);

        List<G3DDbMngVO> list = dbMngService.getList(searchKeyword, categoryFilter, searchType, page, size, srvcNum, dgtlPairGroupCd);

        model.addAttribute("paginationInfo", paginationInfo);
        model.addAttribute("list", list);
        model.addAttribute("total", total);
        model.addAttribute("totalCount", total);
        model.addAttribute("page", page);
        model.addAttribute("size", size);
        model.addAttribute("categoryFilter", categoryFilter);
        model.addAttribute("dgtlPairGroupCd", dgtlPairGroupCd);
        model.addAttribute("searchKeyword", searchKeyword);
        model.addAttribute("searchType", searchType);
        model.addAttribute("totalPages", (int) Math.ceil((double) total / size));

        return "cmm/g3d/dbServiceList/DbServiceList";
    }
    
    
    @GetMapping("/create.do")
    public String showCreateForm(Model model, @RequestParam(required = false) String categoryFilter,
					    		 @RequestParam(value = "page", defaultValue = "1") int page,
					             @RequestParam(value = "size", defaultValue = "10") int size,
                                 @RequestParam(value = "searchKeyword", required = false) String searchKeyword,
                                 @RequestParam(value = "searchType", required = false) String searchType,
                                 @RequestParam(value = "dgtlPairGroupCd", required = false) String dgtlPairGroupCd
    		) {
        String srvcNum = "1";
        List<G3DDbMngVO> originalList = dbMngService.getList(searchKeyword, categoryFilter, searchType, 1, 1000, srvcNum, dgtlPairGroupCd);
        int total = dbMngService.getTotalCount(searchKeyword, categoryFilter, searchType, srvcNum, dgtlPairGroupCd);

        model.addAttribute("originalList", originalList);
        model.addAttribute("total", total);
        model.addAttribute("totalCount", total);
        model.addAttribute("page", page);
        model.addAttribute("size", size);
        model.addAttribute("categoryFilter", categoryFilter);
        model.addAttribute("dgtlPairGroupCd", dgtlPairGroupCd);
        model.addAttribute("searchKeyword", searchKeyword);
        model.addAttribute("searchType", searchType);
        model.addAttribute("poi", new G3DDbMngVO());
        model.addAttribute("totalPages", (int) Math.ceil((double) total / size));


        return "cmm/g3d/dbServiceList/DbServiceCreate";
    }

    @PostMapping("/create")
    public String create(@Valid @ModelAttribute("poi") G3DDbMngVO poi,
                         BindingResult bindingResult,
                         HttpSession session,
                         Model model,
                         RedirectAttributes redirectAttributes) {

        // 1) 특수문자 검사
        if (!validateSpecialCharacters(poi, bindingResult)) {
            return "cmm/g3d/dbServiceList/DbServiceCreate";
        }

        // 2) Binding 오류
        if (bindingResult.hasErrors()) {
            return "cmm/g3d/dbServiceList/DbServiceCreate";
        }

        try {
            LoginVO loginVO = (LoginVO) session.getAttribute("loginVO");
            if (loginVO == null) {
                loginVO = new LoginVO();
                loginVO.setUserId("admin");
                loginVO.setUserNm("관리자");
            }

            poi.setSrvcSeCd("2");
            poi.setFrstRegId(loginVO.getUserId());
            poi.setLastMdfcnId(loginVO.getUserId());

            if (poi.getDgtlPairMdlTrsfJobStts() == null || poi.getDgtlPairMdlTrsfJobStts().isEmpty()) {
                poi.setDgtlPairMdlTrsfJobStts("1");
            }

            // JSON 처리
            if (poi.getDgtlPairMdlTrsfOption() != null &&
                !poi.getDgtlPairMdlTrsfOption().isEmpty()) {

                String raw = poi.getDgtlPairMdlTrsfOption();
                String unescaped = StringEscapeUtils.unescapeHtml4(raw);

                try {
                    var node = objectMapper.readTree(unescaped);

                    if (node.isObject()) {
                        var objectNode = (com.fasterxml.jackson.databind.node.ObjectNode) node;

                        // input → dgtlPairMdlOrgnlPath
                        if (objectNode.has("input") && !objectNode.get("input").isNull()) {
                            poi.setDgtlPairMdlOrgnlPath(objectNode.get("input").asText());
                        }

                        // method → dgtlPairGroupCd
                        if (objectNode.has("method") && !objectNode.get("method").isNull()) {
                            poi.setDgtlPairGroupCd(objectNode.get("method").asText());
                        }

                        // 수정된 JSON 다시 저장
                        poi.setDgtlPairMdlTrsfOption(objectMapper.writeValueAsString(objectNode));
                    }

                } catch (Exception e) {
                    log.error("JSON 파싱 실패", e);
                    model.addAttribute("message", "빌드 옵션 JSON 형식이 잘못되었습니다.");
                    model.addAttribute("messageType", "error");
                    return "cmm/g3d/dbServiceList/DbServiceCreate";
                }
            }

//            // srvcGroupCd가 null이면 기본값 설정 (YEAR로 고정)
//            if (poi.getSrvcGroupCd() == null || poi.getSrvcGroupCd().isEmpty()) {
//                poi.setSrvcGroupCd("YEAR");
//            }

            // DB Insert
            dbMngService.create(poi);

            redirectAttributes.addFlashAttribute("message", "서비스 데이터가 등록되었습니다.");
            redirectAttributes.addFlashAttribute("messageType", "success");

            return "redirect:/cmm/g3d/dbMngSrvList/dbMngSrvListList.do";

        } catch (RuntimeException e) {
            log.error("서비스 데이터 등록 실패", e);

            String userMessage = getUserFriendlyErrorMessage(e.getMessage());

            model.addAttribute("message", userMessage);
            model.addAttribute("messageType", "error");

            return "cmm/g3d/dbServiceList/DbServiceCreate";
        }
    }

    private String getUserFriendlyErrorMessage(String errorMsg) {
        if (errorMsg == null) {
            return "알 수 없는 오류가 발생했습니다. 관리자에게 문의하세요.";
        }
        
        if (errorMsg.contains("API 호출 실패") || errorMsg.contains("ID를 받지 못했습니다")) {
            return "GIS 웹서버 API 연동에 실패했습니다.";
        }
        
        if (errorMsg.contains("DB 등록 실패")) {
            return "데이터베이스 저장에 실패했습니다. 관리자에게 문의하세요.";
        }
        
        if (errorMsg.contains("네트워크") || errorMsg.contains("timeout")) {
            return "네트워크 오류가 발생했습니다. 잠시 후 다시 시도해주세요.";
        }
        
        return "데이터 등록에 실패했습니다: " + errorMsg;
    }
    
    
    @GetMapping("/edit.do")
    public String editForm(@RequestParam Integer dgtlPairMdlId, @RequestParam String srvcSeCd, Model model) {

        G3DDbMngVO poi = dbMngService.getById(dgtlPairMdlId, srvcSeCd);

        if (poi == null) {
            log.warn("존재하지 않는 모델 - ID: {}, srvcSeCd: {}", dgtlPairMdlId, srvcSeCd);
            return "redirect:/cmm/g3d/dbMngSrvList/dbMngSrvListList.do";
        }

        model.addAttribute("poi", poi);
        return "cmm/g3d/dbServiceList/DbServiceEdit";
    }

    @PostMapping("/edit")
    public String edit(@RequestParam Integer dgtlPairMdlId, @RequestParam String srvcSeCd,
                       @Valid @ModelAttribute("poi") G3DDbMngVO poi, BindingResult bindingResult, HttpSession session,
                       Model model) {

        // 특수문자 유효성 검사
        if (!validateSpecialCharacters(poi, bindingResult)) {
            model.addAttribute("poi", poi);
            return "cmm/g3d/dbServiceList/DbServiceEdit";
        }

        if (bindingResult.hasErrors()) {
            return "cmm/g3d/dbServiceList/DbServiceEdit";
        }

        try {
            LoginVO loginVO = (LoginVO) session.getAttribute("loginVO");
            if (loginVO == null) {
                log.warn("로그인되지 않은 사용자의 수정 시도");
                loginVO = new LoginVO();
                loginVO.setUserNm("관리자");
                loginVO.setUserId("admin");
            }

            G3DDbMngVO existingPoi = dbMngService.getById(dgtlPairMdlId, srvcSeCd);

            if (existingPoi == null) {
                log.warn("존재하지 않는 모델 - ID: {}, srvcSeCd: {}", dgtlPairMdlId, srvcSeCd);
                return "redirect:/cmm/g3d/dbMngSrvList/dbMngSrvListList.do";
            }

            G3DDbMngVO updateVO = new G3DDbMngVO();
            updateVO.setDgtlPairMdlId(dgtlPairMdlId);
            updateVO.setSrvcSeCd(srvcSeCd);
            updateVO.setDgtlPairMdlNm(poi.getDgtlPairMdlNm());
            updateVO.setDgtlPairMdlExpln(poi.getDgtlPairMdlExpln());
            updateVO.setDgtlPairCtgry(poi.getDgtlPairCtgry());
            updateVO.setLastMdfcnId(loginVO.getUserId());
            updateVO.setDgtlPairMdlSrvcNm(existingPoi.getDgtlPairMdlSrvcNm());
            updateVO.setSrvcGroupCd(poi.getSrvcGroupCd());  
            updateVO.setSrvcClsfCd(poi.getSrvcClsfCd());    
            dbMngService.update(updateVO);
            
            model.addAttribute("message", "서비스 데이터가 수정되었습니다.");
            model.addAttribute("messageType", "success");
            model.addAttribute("redirectUrl", "/cmm/g3d/dbMngSrvList/dbMngSrvListList.do");
            
            log.info("서비스 데이터 수정 성공: {}", dgtlPairMdlId);
        } catch (Exception e) {
            log.error("서비스 데이터 수정 실패", e);
            
            model.addAttribute("message", "서비스 데이터 수정에 실패했습니다: " + e.getMessage());
            model.addAttribute("messageType", "error");
            model.addAttribute("poi", poi);
        }

        return "cmm/g3d/dbServiceList/DbServiceEdit";
    }

    @PostMapping("/delete")
    public String delete(@RequestParam Integer dgtlPairMdlId, 
                         @RequestParam String srvcSeCd,
                         RedirectAttributes redirectAttributes) {
        try {
            log.info("서비스 데이터 삭제 요청 - ID: {}, srvcSeCd: {}", dgtlPairMdlId, srvcSeCd);
            dbMngService.delete(dgtlPairMdlId, srvcSeCd);
            
            redirectAttributes.addFlashAttribute("message", "서비스 데이터가 삭제되었습니다.");
            redirectAttributes.addFlashAttribute("messageType", "success");
            
            log.info("서비스 데이터 삭제 성공: {}", dgtlPairMdlId);
        } catch (Exception e) {
            log.error("서비스 데이터 삭제 실패 - ID: {}, srvcSeCd: {}", dgtlPairMdlId, srvcSeCd, e);
            
            redirectAttributes.addFlashAttribute("message", "서비스 데이터 삭제에 실패했습니다.");
            redirectAttributes.addFlashAttribute("messageType", "error");
        }
        
        return "redirect:/cmm/g3d/dbMngSrvList/dbMngSrvListList.do";
    }

    @GetMapping(value = "/cmm/g3d/dbMngSrvList", produces = "application/json")
    @ResponseBody
    public ResponseEntity<DefaultApiResponse<List<G3DDbMngVO>>> dbMngSrvList() throws IOException {
        return ResponseEntity.ok(DefaultApiResponse.success(dbMngService.getList()));
    }

    /**
     * 특수문자 유효성 검사
     */
    private boolean validateSpecialCharacters(G3DDbMngVO poi, BindingResult bindingResult) {
        boolean isValid = true;

        if (poi.getDgtlPairMdlNm() != null && DANGEROUS_CHARS.matcher(poi.getDgtlPairMdlNm()).find()) {
            bindingResult.rejectValue("dgtlPairMdlNm",
                    "error.specialChar",
                    "데이터 이름에 허용되지 않은 특수문자가 포함되어 있습니다.");
            log.warn("데이터 이름에 허용되지 않은 특수문자 포함: {}", poi.getDgtlPairMdlNm());
            isValid = false;
        }

        if (poi.getDgtlPairMdlExpln() != null && DANGEROUS_CHARS.matcher(poi.getDgtlPairMdlExpln()).find()) {
            bindingResult.rejectValue("dgtlPairMdlExpln",
                    "error.specialChar",
                    "데이터 설명에 허용되지 않은 특수문자가 포함되어 있습니다.");
            log.warn("데이터 설명에 허용되지 않은 특수문자 포함");
            isValid = false;
        }

        return isValid;
    }
 // REST API
    @GetMapping(value = "/api/v1/dbService", produces = "application/json")
    @ResponseBody
    public ResponseEntity<DefaultApiResponse<List<G3DDbMngVO>>> basemaps(
            @RequestParam(value = "page", defaultValue = "1") int page,
            @RequestParam(value = "size", defaultValue = "10") int size,
            @RequestParam(required = false) String categoryFilter,
            @RequestParam(value = "searchKeyword", required = false) String searchKeyword,
            @RequestParam(value = "searchType", required = false) String searchType,
            @RequestParam(value = "dgtlPairGroupCd", required = false) String dgtlPairGroupCd
    ) {

        // Pagination
        PaginationInfo paginationInfo = new PaginationInfo();
        paginationInfo.setCurrentPageNo(page);
        paginationInfo.setRecordCountPerPage(size);
        paginationInfo.setPageSize(10);

        String srvcNum = "2";

        // Total count
        int total = dbMngService.getTotalCount(searchKeyword, categoryFilter, searchType, srvcNum, dgtlPairGroupCd);
        paginationInfo.setTotalRecordCount(total);

        List<G3DDbMngVO> list = dbMngService.getList(
                searchKeyword,
                categoryFilter,
                searchType,
                page,
                size,
                srvcNum,
                dgtlPairGroupCd
        );
        list.forEach(vo -> {
            if (vo.getDgtlPairMdlTrsfOption() != null) {
                String result = vo.getDgtlPairMdlTrsfOption();
                result = result.replace("&quot;", "\"");
                result = result.replace("&amp;quot;", "\"");
                vo.setDgtlPairMdlTrsfOption(result);
            }
        });

        return ResponseEntity.ok(DefaultApiResponse.success(list));
    }

    /**
     * 외부 API - 상태 업데이트
     */
    @GetMapping(value = "/api/v1/dbService/result", produces = "application/json")
    @ResponseBody
    public ResponseEntity<DefaultApiResponse<Void>> updateRst(
            @RequestParam("dgtlPairMdlId") Integer dgtlPairMdlId,
            @RequestParam("dgtlPairMdlTrsfJobStts") String dgtlPairMdlTrsfJobStts,
            @RequestParam("dgtlPairMdlSrvcNm") String dgtlPairMdlSrvcNm,
            HttpServletRequest request
    ) {
        // 1. API 키 체크
        String apiKey = request.getHeader("X-API-Key");
        if (!isValidApiKey(apiKey)) {
            log.warn("Invalid API Key 시도 - ID: {}", dgtlPairMdlId);
            return ResponseEntity.status(HttpStatus.UNAUTHORIZED)
                .body(DefaultApiResponse.error(dgtlPairMdlId, "인증에 실패했습니다.", apiKey));
        }

        // 2. 상태값 검증
        List<String> allowedStatuses = Arrays.asList("0", "2");
        if (!allowedStatuses.contains(dgtlPairMdlTrsfJobStts)) {
            log.warn("Invalid status value: {} - ID: {}", dgtlPairMdlTrsfJobStts, dgtlPairMdlId);
            return ResponseEntity.badRequest()
                .body(DefaultApiResponse.error(dgtlPairMdlId, "유효하지 않은 상태값입니다.", apiKey));
        }

        // 3. XSS 방어 (상태값)
        dgtlPairMdlTrsfJobStts = StringEscapeUtils.escapeHtml4(dgtlPairMdlTrsfJobStts);

        // 4. VO 세팅
        G3DDbMngVO vo = new G3DDbMngVO();
        vo.setDgtlPairMdlId(dgtlPairMdlId);
        vo.setDgtlPairMdlTrsfJobStts(dgtlPairMdlTrsfJobStts);
        vo.setLastMdfcnId("BuildSever_API");

        if ("0".equals(dgtlPairMdlTrsfJobStts)) {
            vo.setDgtlPairMdlSrvcNm(dgtlPairMdlSrvcNm);
        }

        try {
            int updated = dbMngService.statUpdate(vo);

            if (updated == 0) {
                log.warn("업데이트 대상 없음 - ID: {}", dgtlPairMdlId);
                return ResponseEntity.status(HttpStatus.NOT_FOUND)
                    .body(DefaultApiResponse.error(dgtlPairMdlId, "해당 모델을 찾을 수 없습니다.", apiKey));
            }

            log.info(
                "상태 업데이트 성공 - ID: {}, Status: {}, SrvcNmUpdate: {}",
                dgtlPairMdlId,
                dgtlPairMdlTrsfJobStts,
                "0".equals(dgtlPairMdlTrsfJobStts)
            );

            return ResponseEntity.ok(
                DefaultApiResponse.success("상태가 업데이트 되었습니다.")
            );

        } catch (Exception e) {
            log.error("상태 업데이트 실패 - ID: {}", dgtlPairMdlId, e);
            return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .body(DefaultApiResponse.error(dgtlPairMdlId, "상태 업데이트에 실패했습니다.", apiKey));
        }
    }
    @ResponseBody
    @GetMapping("/checkPath")
    public Map<String, Object> checkPath(@RequestParam String path) {

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

        try {
            Map<String, Object> apiResult = dbMngService.checkStoragePath(path);

            boolean success = Boolean.TRUE.equals(apiResult.get("success"));

            result.put("success", success);

            if (!success) {
                result.put(
                    "message",
                    apiResult.getOrDefault("message", "원본 경로가 존재하지 않습니다.")
                );
            }

        } catch (Exception e) {
            result.put("success", false);
            result.put("message", e.getMessage());
        }

        return result;
    }


    /**
     * API 키 검증 메서드
     */
    private boolean isValidApiKey(String apiKey) {
        if (apiKey == null || apiKey.trim().isEmpty()) {
            return false;
        }
        return validApiKey.equals(apiKey);
    }
}