package incheon.ags.pss.commission.service.impl;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import incheon.ags.pss.commission.mapper.CommissionMapper;
import incheon.ags.pss.commission.service.CommissionService;
import incheon.ags.pss.commission.vo.ScAgndMstVO;
import incheon.ags.pss.commission.vo.ScCmtMstVO;
import incheon.ags.pss.project.vo.ProjectVO;
import incheon.com.cmm.context.RequestContext;
import lombok.extern.slf4j.Slf4j;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.format.DateTimeFormatter;
import java.util.Date;
import java.util.List;
import java.util.Locale;
import java.util.Map;

@Slf4j
@Service("commissionService")
public class CommissionServiceImpl implements CommissionService {

    @Resource(name = "commissionMapper")
    private CommissionMapper commissionMapper;

    private final RestTemplate restTemplate = new RestTemplate();
    @Autowired private ObjectMapper objectMapper;
    private final DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy.MM.dd. (E) HH:mm", Locale.KOREAN);

    private static final String CRAWLER_ID = "CRAWLER";

    // ==========================================================
    // 조회 로직
    // ==========================================================
    @Override
    public List<ScCmtMstVO> selectCommitteeList(ScCmtMstVO vo) throws Exception {
        return commissionMapper.selectCmtMstList(vo);
    }

    @Override
    public int selectCommitteeListCnt(ScCmtMstVO vo) throws Exception {
        return commissionMapper.selectCmtMstListCnt(vo);
    }
    
    @Override
    public ScCmtMstVO selectCommittee(int cmtId) throws Exception {
        return commissionMapper.selectCmtMst(cmtId);
    }

    @Override
    public List<ScAgndMstVO> selectAgendaList(ScAgndMstVO vo) throws Exception {
        // 1. 안건 목록을 먼저 조회합니다.
        List<ScAgndMstVO> agendaList = commissionMapper.selectAgndMstList(vo);

        // 2. 각 안건에 연결된 안건지도 목록을 조회하여 추가합니다.
        for (ScAgndMstVO agenda : agendaList) {
            if (agenda.getAgndId() != null) {
                List<ProjectVO> projectList = commissionMapper.selectProjectListByAgndId(agenda.getAgndId());
                agenda.setProjectList(projectList);
            }
        }

        return agendaList;
    }

    // ==========================================================
    // 1. 크롤링 및 자동 갱신 로직
    // ==========================================================

    @Override
    @Transactional
    public void renewCommissionData() throws Exception {
        log.info(">>> 위원회 데이터 갱신 시작");

        // 임시 처리: CMT0001과 CMT0002에 대해 크롤링 수행
        log.info(">>> 'CMT0001' 위원회 안건 데이터 갱신 시작");
        fetchAllPagesForCommittee("CMT0001");
        log.info(">>> 'CMT0002' 위원회 안건 데이터 갱신 시작");
        fetchAllPagesForCommittee("CMT0002");

        log.info(">>> 전체 데이터 갱신 완료");
    }

    private List<Map<String, Object>> fetchCommissionCodes() throws Exception {
        String url = "https://commission.incheon.go.kr/common/getComList.json";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("user-agent", "Mozilla/5.0");

        String body = "{\"cdType\":\"CMTCD\",\"useYn\":true}";
        HttpEntity<String> request = new HttpEntity<>(body, headers);

        try {
            ResponseEntity<String> response = restTemplate.postForEntity(url, request, String.class);
            return objectMapper.readValue(response.getBody(), new TypeReference<List<Map<String, Object>>>() {});
        } catch (Exception e) {
            log.error("위원회 코드 목록 수집 중 API 호출 실패: URL={}", url, e);
            throw e; 
        }
    }

    private void fetchAllPagesForCommittee(String cmtCd) throws Exception {
        int currentPage = 1;
        int totalPages;

        do {
            totalPages = fetchAndProcessPage(currentPage, cmtCd);
            currentPage++;
            if (currentPage > totalPages) break;

            // 서버 부하 방지용 딜레이
            try { Thread.sleep(200); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }

        } while (true);
    }


    private int fetchAndProcessPage(int pageNo, String cmtCd) throws Exception {
        String listUrl = "https://commission.incheon.go.kr/pub/cmtRslt/getCmtList.json";
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_JSON);
        headers.set("user-agent", "Mozilla/5.0");

        String body = String.format("{\"cmtHdmtYr\":\"\",\"searchTxt\":\"\",\"pageNo\":%d,\"cmtCd\":\"%s\"}", pageNo, cmtCd);
        HttpEntity<String> request = new HttpEntity<>(body, headers);

        try {
            ResponseEntity<String> response = restTemplate.postForEntity(listUrl, request, String.class);
            JsonNode root = objectMapper.readTree(response.getBody());
            JsonNode cmtRoot = root.path("cmt");
            JsonNode content = cmtRoot.path("content");
            int totalPages = cmtRoot.path("totalPages").asInt(1);

            log.info("Processing Page {}/{} for cmtCd {}", pageNo, totalPages, cmtCd);

            if (content.isArray()) {
                for (JsonNode node : content) {
                    processCommitteeUpsert(node, cmtCd);
                }
            }
            return totalPages;
        } catch (Exception e) {
            log.error("Page {} 수집 실패 (cmtCd: {})", pageNo, cmtCd, e);
            throw e;
        }
    }

    private void processCommitteeUpsert(JsonNode node, String cmtCd) throws Exception {
        int cmtSn = node.get("cmtSn").asInt();
        String cmtNm = node.get("cmtHdmtInfo").asText();
        String dtTxt = node.get("cmtHdmtDtTxt").asText();
        String placeNm = node.get("placeNm").asText();

        Date hocuDt = null;
        try {
            LocalDateTime ldt = LocalDateTime.parse(dtTxt, formatter);
            hocuDt = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());
        } catch (Exception e) {
            // 날짜 형식이 잘못된 경우, null로 진행하지 않고 명확한 에러를 발생시켜 처리를 중단함
            log.error("날짜 파싱 실패 - 입력값: {} | 에러: {}", dtTxt, e.getMessage());
            throw new IllegalArgumentException("유효하지 않은 날짜 형식입니다: " + dtTxt, e);
        }

        // 중복 확인
        ScCmtMstVO searchVO = new ScCmtMstVO();
        searchVO.setCmtCd(cmtCd);
        searchVO.setCmtNm(cmtNm);
        searchVO.setHocuDt(hocuDt);
        ScCmtMstVO existing = commissionMapper.selectCmtMstByCmtCdAndNmAndDt(searchVO);

        Integer cmtId;
        if (existing != null) {
            cmtId = existing.getCmtId();
            existing.setHocuPlace(placeNm);
            existing.setLastMdfcnId(CRAWLER_ID);
            commissionMapper.updateCmtMst(existing);
        } else {
            ScCmtMstVO newVO = new ScCmtMstVO();
            newVO.setCmtCd(cmtCd);
            newVO.setCmtNm(cmtNm);
            newVO.setHocuDt(hocuDt);
            newVO.setHocuPlace(placeNm);
            newVO.setFrstRegId(CRAWLER_ID);
            newVO.setLastMdfcnId(CRAWLER_ID);
            commissionMapper.insertCmtMst(newVO);
            cmtId = newVO.getCmtId();
        }

        // 안건 재수집 (크롤러 등록분 삭제 후 재등록)
        commissionMapper.deleteAgndByCmtIdAndRegId(cmtId, CRAWLER_ID);
        collectAgendaData(cmtId, cmtSn, cmtCd);
    }

    private void collectAgendaData(Integer dbCmtId, int apiCmtSn, String cmtCd) {
        try {
            String url = "https://commission.incheon.go.kr/pub/cmtRslt/getAgndListByCmt.json";
            HttpHeaders headers = new HttpHeaders();
            headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
            headers.set("user-agent", "Mozilla/5.0");

            MultiValueMap<String, String> body = new LinkedMultiValueMap<>();
            body.add("cmtCd", cmtCd);
            body.add("cmtSn", String.valueOf(apiCmtSn));

            HttpEntity<MultiValueMap<String, String>> req = new HttpEntity<>(body, headers);
            ResponseEntity<String> res = restTemplate.postForEntity(url, req, String.class);
            JsonNode list = objectMapper.readTree(res.getBody()).path("agnd");

            if (list.isArray()) {
                int seq = 1;
                for (JsonNode n : list) {
                    String agndNm = n.get("agndNm").asText();
                    String dlbrTypeNm = n.get("dlbrTypeNm").asText();
                    String dlbrRsltNm = n.get("dlbrRsltNm").asText();
                    String agndRsltCn = n.get("agndRsltCn").asText();

                    ScAgndMstVO vo = new ScAgndMstVO();
                    vo.setCmtId(dbCmtId);
                    vo.setSortSeq(seq++);
                    vo.setAgndTtl(agndNm.length() > 500 ? agndNm.substring(0, 500) : agndNm); // 안건제목
                    vo.setAgndRsltCn(agndRsltCn.length() > 3000 ? agndRsltCn.substring(0, 3000) : agndRsltCn); // 안건결과내용
                    vo.setDlbrTypeNm(dlbrTypeNm.length() > 10 ? dlbrTypeNm.substring(0, 10) : dlbrTypeNm); // 심의유형명
                    vo.setDlbrRsltNm(dlbrRsltNm.length() > 10 ? dlbrRsltNm.substring(0, 10) : dlbrRsltNm); // 심의결과명
                    vo.setFrstRegId(CRAWLER_ID);
                    vo.setLastMdfcnId(CRAWLER_ID);
                    commissionMapper.insertAgndMst(vo);
                }
            }
        } catch (Exception e) {
            log.error("안건 수집 실패 (sn={}, cmtCd={})", apiCmtSn, cmtCd, e);
        }
    }

    // ==========================================================
    // 2. 수동(Manual) 조작 로직
    // ==========================================================

    private String getCurrentUserId() {
        try {
            String id = RequestContext.getCurrentUserId();
            return (id != null) ? id : "GUEST";
        } catch (Exception e) {
            // 인증 정보 조회 실패 시 로그를 남기고 GUEST 처리
            log.warn("사용자 ID 조회 실패 (GUEST로 처리): {}", e.getMessage());
            return "GUEST"; 
        }
    }

    @Override
    public void saveManualCommittee(ScCmtMstVO vo) throws Exception {
        String userId = getCurrentUserId();
        vo.setLastMdfcnId(userId);
        if (vo.getCmtId() == null || vo.getCmtId() == 0) {
            vo.setFrstRegId(userId);
            commissionMapper.insertCmtMst(vo);
        } else {
            commissionMapper.updateCmtMst(vo);
        }
    }

    @Override
    @Transactional
    public void deleteCommittee(int cmtId) throws Exception {
        commissionMapper.deleteAgndByCmtId(cmtId);
        commissionMapper.deleteCmtMst(cmtId);
    }

    @Override
    public void saveManualAgenda(ScAgndMstVO vo) throws Exception {
        String userId = getCurrentUserId();
        vo.setLastMdfcnId(userId);
        if (vo.getAgndId() == null || vo.getAgndId() == 0) {
            vo.setFrstRegId(userId);
            commissionMapper.insertAgndMst(vo);
        } else {
            commissionMapper.updateAgndMst(vo);
        }
    }

    @Override
    public void deleteAgenda(int agndId) throws Exception {
        commissionMapper.deleteAgndMst(agndId);
    }
}
