package incheon.ags.ias.dataHoprReg.service.impl;

import incheon.ags.ias.dataHoprReg.mapper.JbpsSyncMapper;
import incheon.ags.ias.dataHoprReg.service.JbpsSyncService;
import incheon.ags.ias.dataHoprReg.util.DataFileParseUtil;
import incheon.ags.ias.dataHoprReg.vo.JbpsVO;
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.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.File;
import java.nio.file.Paths;
import java.util.*;

/**
 * 직위 동기화 서비스 구현
 */
@Slf4j
@Service("jbpsSyncService")
@RequiredArgsConstructor
public class JbpsSyncServiceImpl extends EgovAbstractServiceImpl implements JbpsSyncService {

    private final JbpsSyncMapper jbpsSyncMapper;
    private final ComFileService comFileService;

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

    /**
     * 파일 헤더 → DB 컬럼 매핑 (직위)
     */
    private static final Map<String, String> JBPS_HEADER_TO_FIELD = new LinkedHashMap<>();
    static {
        // 직위코드 (jbpsCd)
        JBPS_HEADER_TO_FIELD.put("코드값", "jbpsCd");
        JBPS_HEADER_TO_FIELD.put("직위코드", "jbpsCd");
        JBPS_HEADER_TO_FIELD.put("직위 코드", "jbpsCd");
        JBPS_HEADER_TO_FIELD.put("jbps_cd", "jbpsCd");
        JBPS_HEADER_TO_FIELD.put("jbpscd", "jbpsCd");
        JBPS_HEADER_TO_FIELD.put("JBPS_CD", "jbpsCd");
        JBPS_HEADER_TO_FIELD.put("titlecode", "jbpsCd");
        JBPS_HEADER_TO_FIELD.put("title_code", "jbpsCd");
        // 직위명 (jbpsNm)
        JBPS_HEADER_TO_FIELD.put("코드값의미", "jbpsNm");
        JBPS_HEADER_TO_FIELD.put("직위명", "jbpsNm");
        JBPS_HEADER_TO_FIELD.put("직위 명", "jbpsNm");
        JBPS_HEADER_TO_FIELD.put("직위", "jbpsNm");
        JBPS_HEADER_TO_FIELD.put("jbps_nm", "jbpsNm");
        JBPS_HEADER_TO_FIELD.put("jbpsnm", "jbpsNm");
        JBPS_HEADER_TO_FIELD.put("JBPS_NM", "jbpsNm");
        JBPS_HEADER_TO_FIELD.put("title", "jbpsNm");
        JBPS_HEADER_TO_FIELD.put("titleorposition", "jbpsNm");
        // 비고 (rmck)
        JBPS_HEADER_TO_FIELD.put("비고", "rmck");
        JBPS_HEADER_TO_FIELD.put("rmck", "rmck");
        JBPS_HEADER_TO_FIELD.put("RMCK", "rmck");
        JBPS_HEADER_TO_FIELD.put("remark", "rmck");
        JBPS_HEADER_TO_FIELD.put("remarks", "rmck");
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public int syncFromFile(String atchFileId, String operatorId) throws Exception {
        log.info("========== 직위 파일 동기화 시작 (atchFileId: {}) ==========", atchFileId);

        // 1. 파일 정보 조회
        List<ComFileDtlVO> fileList = comFileService.selectComFileDtlList(atchFileId);
        if (fileList == null || fileList.isEmpty()) {
            throw new BusinessException("첨부파일 정보를 찾을 수 없습니다.");
        }
        ComFileDtlVO fileInfo = fileList.get(0);

        // 2. 파일 경로 구성
        String filePath = Paths.get(uploadPath, fileInfo.getStrgPathNm(), fileInfo.getStrgFileNm()).toString();
        File file = new File(filePath);
        if (!file.exists()) {
            throw new BusinessException("파일이 존재하지 않습니다: " + filePath);
        }
        log.info("파일 경로: {}", filePath);

        // 3. 파일 파싱 (MS949 우선)
        List<String> headers = new ArrayList<>();
        List<List<String>> dataRows = DataFileParseUtil.parseFile(file, false, headers);
        log.info("파싱 완료 - 헤더: {}, 데이터 행 수: {}", headers.size(), dataRows.size());

        // 4. VO 변환
        List<JbpsVO> jbpsList = new ArrayList<>();
        for (List<String> row : dataRows) {
            JbpsVO vo = convertToJbpsVO(headers, row);
            if (vo.getJbpsCd() != null && !vo.getJbpsCd().isEmpty()) {
                jbpsList.add(vo);
            }
        }
        log.info("유효한 직위 데이터 건수: {}", jbpsList.size());

        // 5. 동기화 실행
        Map<String, Integer> syncResult = syncJbpss(jbpsList, operatorId);
        int totalProcessed = syncResult.get("inserted") + syncResult.get("updated") + syncResult.get("deleted");

        log.info("========== 직위 파일 동기화 완료 ==========");
        return totalProcessed;
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public Map<String, Integer> syncJbpss(List<JbpsVO> fileDataList, String operatorId) throws Exception {
        log.info("========== 직위 동기화 시작 ==========");
        log.info("파일 데이터 건수: {}", fileDataList.size());

        List<JbpsVO> existingList = jbpsSyncMapper.selectAllJbpss();
        Map<String, JbpsVO> existingMap = new HashMap<>();
        for (JbpsVO jbps : existingList) {
            existingMap.put(jbps.getJbpsCd(), jbps);
        }
        log.info("기존 DB 직위 건수: {}", existingMap.size());

        Map<String, JbpsVO> fileMap = new HashMap<>();
        for (JbpsVO fileJbps : fileDataList) {
            if (fileJbps.getJbpsCd() != null && !fileJbps.getJbpsCd().isEmpty()) {
                fileMap.put(fileJbps.getJbpsCd(), fileJbps);
            }
        }

        List<JbpsVO> toInsert = new ArrayList<>();
        List<JbpsVO> toUpdate = new ArrayList<>();
        List<String> toDelete = new ArrayList<>();
        int unchangedCount = 0;

        for (Map.Entry<String, JbpsVO> entry : fileMap.entrySet()) {
            String jbpsCd = entry.getKey();
            JbpsVO fileJbps = entry.getValue();

            if (!existingMap.containsKey(jbpsCd)) {
                toInsert.add(fileJbps);
            } else {
                JbpsVO existing = existingMap.get(jbpsCd);
                if (isJbpsChanged(existing, fileJbps)) {
                    toUpdate.add(fileJbps);
                } else {
                    unchangedCount++;
                }
            }
        }

        for (String existingCd : existingMap.keySet()) {
            if (!fileMap.containsKey(existingCd)) {
                toDelete.add(existingCd);
            }
        }

        log.info("INSERT: {}, UPDATE: {}, DELETE: {}, 변경없음: {}",
                toInsert.size(), toUpdate.size(), toDelete.size(), unchangedCount);

        for (JbpsVO jbps : toUpdate) {
            jbpsSyncMapper.updateJbps(jbps);
        }
        for (String jbpsCd : toDelete) {
            jbpsSyncMapper.deleteJbps(jbpsCd);
        }
        for (JbpsVO jbps : toInsert) {
            jbpsSyncMapper.insertJbps(jbps);
        }

        log.info("========== 직위 동기화 완료 ==========");

        Map<String, Integer> result = new HashMap<>();
        result.put("inserted", toInsert.size());
        result.put("updated", toUpdate.size());
        result.put("deleted", toDelete.size());
        result.put("unchanged", unchangedCount);
        return result;
    }

    private boolean isJbpsChanged(JbpsVO existing, JbpsVO file) {
        if (!equalsNullSafe(existing.getJbpsNm(), file.getJbpsNm())) return true;
        if (!equalsNullSafe(existing.getRmck(), file.getRmck())) return true;
        return false;
    }

    @Override
    public JbpsVO convertToJbpsVO(List<String> headers, List<String> values) {
        JbpsVO vo = new JbpsVO();
        for (int i = 0; i < headers.size() && i < values.size(); i++) {
            String header = headers.get(i).trim().toLowerCase();
            String value = values.get(i).trim();
            String field = JBPS_HEADER_TO_FIELD.get(header);
            if (field == null) {
                for (Map.Entry<String, String> entry : JBPS_HEADER_TO_FIELD.entrySet()) {
                    if (entry.getKey().equalsIgnoreCase(header)) {
                        field = entry.getValue();
                        break;
                    }
                }
            }
            if (field != null && !value.isEmpty()) {
                setJbpsFieldValue(vo, field, value);
            }
        }
        return vo;
    }

    private void setJbpsFieldValue(JbpsVO vo, String field, String value) {
        switch (field) {
            case "jbpsCd": vo.setJbpsCd(value); break;
            case "jbpsNm": vo.setJbpsNm(value); break;
            case "rmck": vo.setRmck(value); break;
        }
    }

    private boolean equalsNullSafe(String a, String b) {
        String sa = (a == null) ? "" : a.trim();
        String sb = (b == null) ? "" : b.trim();
        return sa.equals(sb);
    }
}
