package incheon.ags.ias.linkData.web;

import incheon.ags.ias.clct.service.ClctHstryService;
import incheon.ags.ias.clct.service.ClctIntgHstryService;
import incheon.ags.ias.dataHoprReg.service.DeptSyncService;
import incheon.ags.ias.dataHoprReg.service.JbgdSyncService;
import incheon.ags.ias.dataHoprReg.service.JbpsSyncService;
import incheon.ags.ias.dataHoprReg.service.UserSyncService;
import incheon.ags.ias.dataHoprReg.service.KbRtmsBjdcdSyncService;
import incheon.ags.ias.dataHoprReg.service.KbRtmsInfoSyncService;
import incheon.ags.ias.dataHoprReg.service.KbRtmsPriceSyncService;
import incheon.ags.ias.dataHoprReg.service.KbRtmsPyongSyncService;
import incheon.ags.ias.linkData.service.LinkDataService;
import incheon.ags.ias.linkData.vo.LinkDataSearchVO;
import incheon.ags.ias.linkData.vo.LinkDataVO;
import incheon.ags.ias.linkData.web.dto.LinkDataRequestDTO;
import incheon.com.cmm.api.DefaultApiResponse;
import incheon.com.cmm.exception.BusinessException;
import incheon.com.file.service.ComFileService;
import incheon.com.file.vo.ComFileDtlVO;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.egovframe.rte.ptl.mvc.tags.ui.pagination.PaginationInfo;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.List;
import java.util.Map;

@RestController
@RequiredArgsConstructor
@Slf4j
public class LinkDataApiController {
    private final LinkDataService linkDataService;
    private final ClctIntgHstryService clctIntgHstryService;
    private final ClctHstryService clctHstryService;
    private final UserSyncService userSyncService;
    private final DeptSyncService deptSyncService;
    private final JbgdSyncService jbgdSyncService;
    private final JbpsSyncService jbpsSyncService;
    private final KbRtmsBjdcdSyncService kbRtmsBjdcdSyncService;
    private final KbRtmsInfoSyncService kbRtmsInfoSyncService;
    private final KbRtmsPriceSyncService kbRtmsPriceSyncService;
    private final KbRtmsPyongSyncService kbRtmsPyongSyncService;
    private final ComFileService comFileService;

    @Value("${Globals.comfile.storage.path:../upload}")
    private String uploadPath;

    @PostMapping("/ags/ias/linkData/linkDataRegistAction.do")
    public ResponseEntity<DefaultApiResponse<LinkDataVO>> linkDataRegistAction(
            @RequestBody LinkDataRequestDTO linkDataRequestDTO) throws Exception {

        log.info("자료 연계 설정 등록 요청: {}", linkDataRequestDTO.toString());

        LinkDataVO linkDataVO = linkDataRequestDTO.toEntity();

        // 중복 체크
        LinkDataVO existingLink = new LinkDataVO();
        existingLink.setLinkId(linkDataVO.getLinkId());
        LinkDataVO checkLink = linkDataService.selectLinkDataDetail(existingLink);

        if (checkLink != null) {
            throw new BusinessException("이미 존재하는 연계ID입니다: " + linkDataVO.getLinkId());
        }
        linkDataVO.setLastLinkStcd("READY");

        int result = linkDataService.insertLinkData(linkDataVO);

        if (result <= 0) {
            throw new BusinessException("자료 연계 정보 등록에 실패했습니다.");
        }

        log.info("자료 연계 정보 등록 성공 - linkId: {}", linkDataVO.getLinkNm());
        return ResponseEntity.ok(
                DefaultApiResponse.success("자료 연계 정보가 성공적으로 등록되었습니다.")
        );
    }

    @PutMapping("/ags/ias/linkData/linkDataModifyAction.do")
    public ResponseEntity<DefaultApiResponse<LinkDataVO>> linkDataModifyAction(
            @RequestBody LinkDataRequestDTO linkDataRequestDTO) throws Exception {

        log.info("자료 연계 설정 수정 요청: {}", linkDataRequestDTO.toString());

        LinkDataVO linkDataVO = linkDataRequestDTO.toEntity();

        // 연계 ID 유무 체크
        LinkDataVO existingLink = new LinkDataVO();
        existingLink.setLinkId(linkDataVO.getLinkId());
        LinkDataVO checkLink = linkDataService.selectLinkDataDetail(existingLink);

        if (checkLink == null) {
            throw new BusinessException("존재하지 않는 연계ID입니다: " + linkDataVO.getLinkId());
        }

        int result = linkDataService.updateLinkData(linkDataVO);

        if (result <= 0) {
            throw new BusinessException("자료 연계 정보 수정에 실패했습니다.");
        }

        log.info("자료 연계 정보 수정 성공 - linkId: {}", linkDataVO.getLinkNm());
        return ResponseEntity.ok(
                DefaultApiResponse.success("자료 연계 정보가 성공적으로 수정되었습니다.")
        );
    }

    @DeleteMapping("/ags/ias/linkData/linkDataDeleteAction.do/{linkId}")
    public ResponseEntity<DefaultApiResponse<LinkDataVO>> deleteLinkData(
            @PathVariable String linkId)throws Exception {
        log.info("자료 연계 설정 삭제 요청 - linkId: {}", linkId);

        if (linkId == null) {
            throw new BusinessException("삭제할 자료 연계 아이디가 없습니다.");
        }

        LinkDataVO linkDataVO = new LinkDataVO();
        linkDataVO.setLinkId(linkId);
        LinkDataVO existingLink = linkDataService.selectLinkDataDetail(linkDataVO);
        if (existingLink == null) {
            throw new BusinessException("삭제할 자료 연계 정보를 찾을 수 없습니다.");
        }

        int result = linkDataService.deleteLinkData(linkDataVO);

        if(result <= 0) {
            throw new BusinessException("자료 연계 정보 삭제에 실패했습니다.");
        }

        return ResponseEntity.ok(
            DefaultApiResponse.success("자료 연계 정보가 성공적으로 삭제되었습니다.")
        );
    }

    @PostMapping("/ags/ias/linkData/linkDataSettingProcessAction.do")
    public ResponseEntity<DefaultApiResponse<LinkDataVO>> linkDataSettingProcessAction(
            @RequestBody LinkDataRequestDTO linkDataRequestDTO) throws Exception {

        log.info("자료 연계 연계데이터설정 요청: {}", linkDataRequestDTO.toString());

        // Metadata CUD 처리
        int metadataCount = linkDataService.processMetadataCUD(
            linkDataRequestDTO.getMetadata(),
            linkDataRequestDTO.getLinkId()
        );

        // API Data CUD 처리
        int apiDataCount = linkDataService.processApiDataCUD(
            linkDataRequestDTO.getApiData(),
            linkDataRequestDTO.getLinkId(),
            linkDataRequestDTO.getLinkTypeCd()
        );

        int totalProcessed = metadataCount + apiDataCount;
        log.info("연계 데이터 설정 저장 완료 - 총 처리 건수: {} (메타데이터: {}, API데이터: {})", totalProcessed, metadataCount, apiDataCount);
        
        return ResponseEntity.ok(
                DefaultApiResponse.success("연계 데이터 설정 정보가 성공적으로 저장되었습니다. (총 " + totalProcessed + "건 처리)")
        );
    }

    @GetMapping("/ags/ias/linkData/collectionStatusHstryList.do")
    public ResponseEntity<DefaultApiResponse<Map<String,Object>>> collectionStatusHstryList(
            @RequestParam(defaultValue = "1") int page,
            @RequestParam String linkId,
            @ModelAttribute LinkDataSearchVO searchVO,
            @RequestParam(required = false, defaultValue = "WEEK") String period) throws Exception {
        
        log.info("수집현황 조회 API 요청 - linkId: {}, period: {}, page: {}", linkId, period, page);
        searchVO.setLinkId(linkId);

        LocalDate searchLocalDate;
        if ("WEEK".equals(period)) {
            searchLocalDate = java.time.LocalDate.now().minusDays(7);
        } else {
            searchLocalDate = java.time.LocalDate.now().minusDays(30);
        }
        searchVO.setSearchDate(searchLocalDate);

        // 1. 총 개수 조회
        int totalCount = linkDataService.selectClctHstryTotCnt(searchVO);

        // 2. 페이징 설정
        searchVO.setPageIndex(page);
        PaginationInfo paginationInfo = new PaginationInfo();
        paginationInfo.setCurrentPageNo(page);
        paginationInfo.setRecordCountPerPage(searchVO.getRecordCountPerPage());
        paginationInfo.setPageSize(searchVO.getPageSize());
        paginationInfo.setTotalRecordCount(totalCount);

        // 3. 페이징 파라미터 설정
        searchVO.setFirstIndex(paginationInfo.getFirstRecordIndex());
        searchVO.setLastIndex(paginationInfo.getLastRecordIndex());
        searchVO.setRecordCountPerPage(paginationInfo.getRecordCountPerPage());

        // 데이터 조회
        java.util.List<java.util.Map<String, Object>> clctDalyList = linkDataService.selectClctDalySmryList(searchVO);
        java.util.List<java.util.Map<String, Object>> clctHstryList = linkDataService.selectClctIntgHstryList(searchVO);
        int TotPrcsCnt = linkDataService.selectDalySmryTotPrcsCnt(searchVO);
        int TotScsCnt = linkDataService.selectDalySmryTotScsCnt(searchVO);
        int TotFailCnt = linkDataService.selectDalySmryTotFailCnt(searchVO);
        int AvgScsRpblty = linkDataService.selectDalyScsRpbltyAvg(searchVO);
        long AvgPrcsHr = linkDataService.selectDalyPrcsHrAvg(searchVO);

        // 응답 데이터 구성
        java.util.Map<String, Object> result = new java.util.HashMap<>();
        result.put("TotPrcsCnt", TotPrcsCnt);
        result.put("TotScsCnt", TotScsCnt);
        result.put("TotFailCnt", TotFailCnt);
        result.put("AvgScsRpblty", AvgScsRpblty);
        result.put("AvgPrcsHr", AvgPrcsHr);
        result.put("clctDalyList", clctDalyList);
        result.put("clctHstryList", clctHstryList);
        result.put("totalCount", totalCount);
        result.put("paginationInfo", paginationInfo);
        result.put("searchVO", searchVO);

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

    @PostMapping("/ags/ias/linkData/linkDataInsertHoprHstryAction.do")
    public ResponseEntity<DefaultApiResponse<LinkDataVO>> linkDataInsertHoprHstryAction(
            @RequestBody LinkDataRequestDTO linkDataRequestDTO) throws Exception {

        log.info("자료 연계 데이터 수동입력 요청: {}", linkDataRequestDTO.toString());

        LinkDataVO linkDataVO = linkDataRequestDTO.toEntity();

        // LinkData 상세 정보 조회 (linkNm, pvsnSysNm 가져오기)
        LinkDataVO linkDataDetail = linkDataService.selectLinkDataDetail(linkDataVO);
        if (linkDataDetail == null) {
            throw new BusinessException("존재하지 않는 연계ID입니다: " + linkDataVO.getLinkId());
        }

        // 0. 실행 ID 생성
        String executionId = java.util.UUID.randomUUID().toString();

        // 1. 수집 이력 시작 기록 (먼저 호출하여 clct_hstry_sn 받기)
        Long clctHstrySn = clctHstryService.registerHistoryStart(
                executionId,
                linkDataDetail.getLinkNm(),
                "수동 데이터 업로드: " + linkDataVO.getLinkId()
        );

        // 2. 수집 통합 이력 시작 기록 (ref_hstry_id에 clct_hstry_sn 설정)
        clctIntgHstryService.logCollectionStart(
                executionId,
                clctHstrySn,
                linkDataVO.getLinkId(),
                linkDataDetail.getLinkNm(),
                linkDataDetail.getPvsnSysNm(),
                "수동 데이터 업로드: " + linkDataVO.getLinkId()
        );

        boolean historySaved = false; // 히스토리 저장 여부 추적

        try {
            // 2. 파일 파싱 및 동기화 (linkId에 따라 분기)
            int parsedResult = syncFromFileByLinkId(
                    linkDataVO.getLinkId(),
                    linkDataVO.getAtchFileId(),
                    linkDataVO.getLOGIN_USER_ID()
            );
            log.info("파일 파싱 및 동기화 완료 - 처리된 건수: {}", parsedResult);

            // 3. 처리 건수 및 성공 상태를 VO에 설정
            linkDataVO.setPrcsNocs((long) parsedResult);
            linkDataVO.setPrcsStcd("SUCCESS");

            // 4. 히스토리 저장
            int result = linkDataService.insertLinkDataHoprHstry(linkDataVO);
            historySaved = true; // 히스토리 저장 성공 표시

            if (result <= 0) {
                // 히스토리 저장 실패
                log.error("자료 연계 데이터 수동 입력 실패");

                // 수집 통합 이력 실패 기록
                clctIntgHstryService.logCollectionFailure(executionId, new Exception("히스토리 저장 실패"), 0);

                // 수집 이력 실패 기록
                clctHstryService.markHistoryAsFailed(executionId, 0, "히스토리 저장 실패");

                linkDataVO.setPrcsNocs(0L);
                linkDataVO.setPrcsStcd("FAIL");
                linkDataService.insertLinkDataHoprHstry(linkDataVO);
                throw new BusinessException("데이터 이상으로 등록 실패했습니다.");
            }

            // 5. 수집 통합 이력 성공 기록
            clctIntgHstryService.logCollectionSuccess(
                    executionId,
                    parsedResult,
                    parsedResult
            );

            // 6. 수집 이력 완료 처리
            clctHstryService.markHistoryAsCompleted(
                    executionId,
                    parsedResult,
                    parsedResult,
                    "파일 업로드 및 데이터 등록 성공"
            );

            // 7. LinkData 최종 연계 상태를 SUCCESS로 업데이트
            try {
                linkDataDetail.setLastLinkStcd("SUCCESS");
                linkDataService.updateLinkData(linkDataDetail);
                log.info("LinkData 최종 연계 상태를 SUCCESS로 업데이트 완료: {}", linkDataVO.getLinkId());
            } catch (Exception updateException) {
                log.error("LinkData 상태 업데이트 실패 (SUCCESS): {}", updateException.getMessage());
            }

            log.info("자료 연계 데이터 등록 성공 - hstrySn: {}, executionId: {}",
                    linkDataVO.getHstrySn(), executionId);

            return ResponseEntity.ok(
                    DefaultApiResponse.success(linkDataVO, "자료 연계 데이터가 성공적으로 등록되었습니다.")
            );
        } catch (BusinessException e) {
            // 비즈니스 예외는 상위로 전파하되, 히스토리는 실패 상태로 저장
            log.error("자료 연계 데이터 수동 입력 실패: {}", e.getMessage(), e);

            // 수집 통합 이력 실패 기록
            clctIntgHstryService.logCollectionFailure(executionId, e, 0);

            // 수집 이력 실패 처리
            clctHstryService.markHistoryAsFailed(executionId, 0, e.getMessage());

            // 히스토리가 아직 저장되지 않았을 경우에만 FAIL 상태로 저장
            if (!historySaved) {
                try {
                    linkDataVO.setPrcsNocs(0L);
                    linkDataVO.setPrcsStcd("FAIL");
                    linkDataVO.setRmrkCn(e.getMessage());
                    linkDataService.insertLinkDataHoprHstry(linkDataVO);
                } catch (Exception historyException) {
                    log.error("히스토리 저장 실패: {}", historyException.getMessage());
                }
            }

            // LinkData 최종 연계 상태를 FAIL로 업데이트
            try {
                linkDataDetail.setLastLinkStcd("FAIL");
                linkDataService.updateLinkData(linkDataDetail);
                log.info("LinkData 최종 연계 상태를 FAIL로 업데이트 완료: {}", linkDataVO.getLinkId());
            } catch (Exception updateException) {
                log.error("LinkData 상태 업데이트 실패 (FAIL): {}", updateException.getMessage());
            }

            throw new BusinessException("데이터 이상으로 등록 실패했습니다.");
        } catch (Exception e) {
            // 예외 발생 시 실패 상태로 히스토리 저장
            log.error("자료 연계 데이터 수동 입력 실패: {}", e.getMessage(), e);

            // 수집 통합 이력 실패 기록
            clctIntgHstryService.logCollectionFailure(executionId, e, 0);

            // 수집 이력 실패 처리
            clctHstryService.markHistoryAsFailed(executionId, 0, e.getMessage());

            // 히스토리가 아직 저장되지 않았을 경우에만 FAIL 상태로 저장
            if (!historySaved) {
                try {
                    linkDataVO.setPrcsNocs(0L);
                    linkDataVO.setPrcsStcd("FAIL");
                    linkDataVO.setRmrkCn("데이터 처리 중 시스템 오류가 발생했습니다. 관리자에게 문의하세요.");
                    linkDataService.insertLinkDataHoprHstry(linkDataVO);
                } catch (Exception historyException) {
                    log.error("히스토리 저장 실패: {}", historyException.getMessage());
                }
            }

            // LinkData 최종 연계 상태를 FAIL로 업데이트
            try {
                linkDataDetail.setLastLinkStcd("FAIL");
                linkDataService.updateLinkData(linkDataDetail);
                log.info("LinkData 최종 연계 상태를 FAIL로 업데이트 완료: {}", linkDataVO.getLinkId());
            } catch (Exception updateException) {
                log.error("LinkData 상태 업데이트 실패 (FAIL): {}", updateException.getMessage());
            }

            throw new BusinessException("데이터 이상으로 등록 실패했습니다.");
        }
    }


    @GetMapping("/ags/ias/linkData/dataRegistrationPopupList.do")
    public ResponseEntity<DefaultApiResponse<Map<String,Object>>> dataRegistrationPopupList(
            @RequestParam(defaultValue = "1") int page,
            @RequestParam String linkId,
            @ModelAttribute LinkDataSearchVO searchVO) throws Exception {

        log.info("데이터등록 팝업 목록 조회 API 요청 - linkId: {}, page: {}", linkId, page);
        searchVO.setLinkId(linkId);

        // 1. 총 개수 조회
        int totalCount = linkDataService.selectLinkDataHoprHstryTotCnt(searchVO);

        // 2. 페이징 설정
        searchVO.setPageIndex(page);
        PaginationInfo paginationInfo = new PaginationInfo();
        paginationInfo.setCurrentPageNo(page);
        paginationInfo.setRecordCountPerPage(searchVO.getRecordCountPerPage());
        paginationInfo.setPageSize(searchVO.getPageSize());
        paginationInfo.setTotalRecordCount(totalCount);

        // 3. 페이징 파라미터 설정
        searchVO.setFirstIndex(paginationInfo.getFirstRecordIndex());
        searchVO.setLastIndex(paginationInfo.getLastRecordIndex());
        searchVO.setRecordCountPerPage(paginationInfo.getRecordCountPerPage());

        // 데이터 조회
        java.util.List<java.util.Map<String, Object>> hoprHstryList = linkDataService.selectLinkDataHoprHstryList(searchVO);

        // 응답 데이터 구성
        java.util.Map<String, Object> result = new java.util.HashMap<>();
        result.put("hoprHstryList", hoprHstryList);
        result.put("totalCount", totalCount);
        result.put("paginationInfo", paginationInfo);

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

    /**
     * 첨부파일 ID로 원본 파일명 조회
     *
     * @param atchFileId 첨부파일 ID
     * @return 원본 파일명
     */
    private String getOriginalFileName(String atchFileId) throws Exception {
        if (atchFileId == null || atchFileId.trim().isEmpty()) {
            throw new BusinessException("첨부파일 ID가 없습니다.");
        }

        List<ComFileDtlVO> fileList = comFileService.selectComFileDtlList(atchFileId);
        if (fileList == null || fileList.isEmpty()) {
            throw new BusinessException("파일을 찾을 수 없습니다. atchFileId: " + atchFileId);
        }

        ComFileDtlVO fileInfo = fileList.get(0);
        return fileInfo.getOrgnlFileNm();
    }

    /**
     * 리포트 파일 경로 구성
     * 형식: {uploadPath}/reports/pending/yyyyMMdd/{fileName}
     *
     * @param fileName 파일명
     * @return 구성된 파일 경로
     */
    private String buildReportFilePath(String fileName) {
        String dateFolder = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        return uploadPath + "/reports/pending/" + dateFolder + "/" + fileName;
    }

    /**
     * linkId에 따라 적절한 SyncService의 syncFromFile 호출
     *
     * @param linkId 연계 ID (ICLDAPUSER, ICLDAPDEPT, JBGD001, JBPS001, KBRTMS_BJDCD, KBRTMS_INFO, KBRTMS_PRICE, KBRTMS_PYONG)
     * @param atchFileId 첨부파일 ID
     * @param operatorId 작업자 ID
     * @return 처리된 건수
     */
    private int syncFromFileByLinkId(String linkId, String atchFileId, String operatorId) throws Exception {
        if (linkId == null || linkId.isEmpty()) {
            throw new BusinessException("연계 ID가 없습니다.");
        }

        String upperLinkId = linkId.toUpperCase();
        log.info("동기화 서비스 분기 처리 - linkId: {}", upperLinkId);

        switch (upperLinkId) {
            case "ICLDAPUSER":
                return userSyncService.syncFromFile(atchFileId, operatorId);
            case "ICLDAPDEPT":
                return deptSyncService.syncFromFile(atchFileId, operatorId);
            case "JBGD001":
                return jbgdSyncService.syncFromFile(atchFileId, operatorId);
            case "JBPS001":
                return jbpsSyncService.syncFromFile(atchFileId, operatorId);
            case "KBRTMS_BJDCD":
                return kbRtmsBjdcdSyncService.syncFromFile(atchFileId, operatorId);
            case "KBRTMS_INFO":
                return kbRtmsInfoSyncService.syncFromFile(atchFileId, operatorId);
            case "KBRTMS_PRICE":
                return kbRtmsPriceSyncService.syncFromFile(atchFileId, operatorId);
            case "KBRTMS_PYONG":
                return kbRtmsPyongSyncService.syncFromFile(atchFileId, operatorId);
            default:
                throw new BusinessException("지원하지 않는 연계 ID입니다: " + linkId);
        }
    }
}
