package go.kr.project.system.code.service.impl;

import egovframework.exception.MessageException;
import egovframework.util.SessionUtil;
import go.kr.project.system.code.mapper.CodeMapper;
import go.kr.project.system.code.model.*;
import go.kr.project.system.code.service.CodeService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.egovframe.rte.fdl.cmmn.EgovAbstractServiceImpl;
import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.stereotype.Service;

import java.util.List;

/**
 * packageName    : go.kr.project.system.code.service.impl
 * fileName       : CodeServiceImpl
 * author         : 개발자
 * date           : 2025-05-09
 * description    : 공통 코드 관련 비즈니스 로직을 처리하는 서비스 구현체
 * ===========================================================
 * DATE              AUTHOR             NOTE
 * -----------------------------------------------------------
 * 2025-05-09        개발자       최초 생성
 * 2025-05-13        개발자       CodeMaster를 CodeGroup으로 변경
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class CodeServiceImpl extends EgovAbstractServiceImpl implements CodeService {

    private final CodeMapper codeMapper;

    /**
     * 코드 그룹 목록을 조회합니다.
     * 
     * @param vo 검색 조건을 담은 VO 객체
     * @return 코드 그룹 목록
     */
    @Override
    public List<CodeGroupVO> selectCodeGroupList(CodeSearchVO vo) {
        return codeMapper.selectCodeGroupList(vo);
    }

    /**
     * 코드 그룹 목록의 총 개수를 조회합니다.
     * 
     * @param vo 검색 조건을 담은 VO 객체
     * @return 코드 그룹 목록의 총 개수
     */
    @Override
    public int selectCodeGroupListTotalCount(CodeSearchVO vo) {
        return codeMapper.selectCodeGroupListTotalCount(vo);
    }

    /**
     * 코드 상세 목록을 조회합니다.
     * 
     * @param vo 검색 조건을 담은 VO 객체
     * @return 코드 상세 목록
     */
    @Override
    public List<CodeDetailVO> selectCodeDetailList(CodeSearchVO vo) {
        return codeMapper.selectCodeDetailList(vo);
    }

    /**
     * 코드 상세 목록의 총 개수를 조회합니다.
     * 
     * @param vo 검색 조건을 담은 VO 객체
     * @return 코드 상세 목록의 총 개수
     */
    @Override
    public int selectCodeDetailListTotalCount(CodeSearchVO vo) {
        return codeMapper.selectCodeDetailListTotalCount(vo);
    }

    /**
     * 여러 코드 상세 정보를 일괄 저장합니다.
     * 
     * @param modifyData 저장할 코드 상세 정보 목록
     * @return 등록, 수정, 삭제 된 행의 수
     * @throws MessageException 중복 에러 발생 시 발생
     */
    @Override
    public int saveCodeDetails(CodeDetailModifiedDataVO modifyData) {
        int result = 0;

        // 1. 삭제된 행 처리
        List<CodeDetailVO> deletedRows = modifyData.getDeletedRows();
        if (deletedRows != null && !deletedRows.isEmpty()) {
            for (CodeDetailVO codeDetailVO : deletedRows) {
                result += codeMapper.deleteCodeDetail(codeDetailVO);
            }
        }

        // 2. 추가된 행 처리
        List<CodeDetailVO> createdRows = modifyData.getCreatedRows();
        if (createdRows != null && !createdRows.isEmpty()) {
            for (CodeDetailVO codeDetailVO : createdRows) {
                codeDetailVO.setRgtr(SessionUtil.getUserId());
                codeDetailVO.setMdfr(SessionUtil.getUserId());
                // 사용 여부가 없으면 기본값 설정
                if (codeDetailVO.getUseYn() == null || codeDetailVO.getUseYn().isEmpty()) {
                    codeDetailVO.setUseYn("Y");
                }
                try {
                    // 코드 상세 등록 시도
                    result += codeMapper.insertCodeDetail(codeDetailVO);
                } catch (DuplicateKeyException e) {
                    // 중복 에러 처리
                    // 동일한 그룹코드ID와 코드ID 조합이 이미 존재하는 경우 중복 에러 발생
                    log.error("중복 에러 발생: {}", e.getMessage());
                    throw new MessageException("상세코드ID가 중복되었습니다");
                }
            }
        }

        // 3. 수정된 행 처리
        List<CodeDetailVO> updatedRows = modifyData.getUpdatedRows();
        if (updatedRows != null && !updatedRows.isEmpty()) {
            for (CodeDetailVO codeDetailVO : updatedRows) {
                codeDetailVO.setMdfr(SessionUtil.getUserId());
                // 사용 여부가 없으면 기본값 설정
                if (codeDetailVO.getUseYn() == null || codeDetailVO.getUseYn().isEmpty()) {
                    codeDetailVO.setUseYn("Y");
                }
                try {
                    // 코드 상세 수정 시도
                    result += codeMapper.updateCodeDetail(codeDetailVO);
                } catch (DuplicateKeyException e) {
                    // 중복 에러 처리
                    // 수정 시 다른 코드와 ID가 중복되는 경우 에러 발생
                    log.error("중복 에러 발생: {}", e.getMessage());
                    throw new MessageException("상세코드ID가 중복되었습니다");
                }
            }
        }

        return result;
    }

    /**
     * 여러 코드 그룹 정보를 일괄 저장합니다.
     * 
     * @param modifyData 저장할 코드 그룹 정보 목록
     * @return 등록, 수정, 삭제 된 행의 수
     * @throws MessageException 중복 에러 또는 외래키 제약조건 위반 시 발생
     */
    @Override
    public int saveCodeGroups(CodeGroupModifiedDataVO modifyData) {
        int result = 0;

        // 1. 삭제된 행 처리
        List<CodeGroupVO> deletedRows = modifyData.getDeletedRows();
        if (deletedRows != null && !deletedRows.isEmpty()) {
            for (CodeGroupVO codeGroupVO : deletedRows) {
                try {
                    // 코드 그룹 삭제 시도
                    result += codeMapper.deleteCodeGroup(codeGroupVO);
                } catch (DataIntegrityViolationException e) {
                    // 외래키 제약조건 위반 예외 처리
                    // 코드 그룹에 연결된 코드 상세가 있을 경우 삭제할 수 없음
                    log.error("외래키 제약조건 위반 에러 발생: {}", e.getMessage());
                    throw new MessageException("상세코드 데이터 삭제 후 그룹코드를 삭제하시기 바랍니다");
                }
            }
        }

        // 2. 추가된 행 처리
        List<CodeGroupVO> createdRows = modifyData.getCreatedRows();
        if (createdRows != null && !createdRows.isEmpty()) {
            for (CodeGroupVO codeGroupVO : createdRows) {
                codeGroupVO.setRgtr(SessionUtil.getUserId());
                // 사용 여부가 없으면 기본값 설정
                if (codeGroupVO.getUseYn() == null || codeGroupVO.getUseYn().isEmpty()) {
                    codeGroupVO.setUseYn("Y");
                }
                try {
                    // 코드 그룹 등록 시도
                    result += codeMapper.insertCodeGroup(codeGroupVO);
                } catch (DuplicateKeyException e) {
                    // 중복 에러 처리
                    // 동일한 그룹코드ID가 이미 존재하는 경우 중복 에러 발생
                    log.error("중복 에러 발생: {}", e.getMessage());
                    throw new MessageException("그룹코드ID가 중복되었습니다");
                }
            }
        }

        // 3. 수정된 행 처리
        List<CodeGroupVO> updatedRows = modifyData.getUpdatedRows();
        if (updatedRows != null && !updatedRows.isEmpty()) {
            for (CodeGroupVO codeGroupVO : updatedRows) {
                // 수정자가 없으면 기본값 설정
                if (codeGroupVO.getMdfr() == null || codeGroupVO.getMdfr().isEmpty()) {
                    codeGroupVO.setMdfr(SessionUtil.getUserId());
                }
                // 사용 여부가 없으면 기본값 설정
                if (codeGroupVO.getUseYn() == null || codeGroupVO.getUseYn().isEmpty()) {
                    codeGroupVO.setUseYn("Y");
                }
                try {
                    // 코드 그룹 수정 시도
                    result += codeMapper.updateCodeGroup(codeGroupVO);
                } catch (DuplicateKeyException e) {
                    // 중복 에러 처리
                    // 수정 시 다른 그룹코드와 ID가 중복되는 경우 에러 발생
                    log.error("중복 에러 발생: {}", e.getMessage());
                    throw new MessageException("그룹코드ID가 중복되었습니다");
                }
            }
        }

        return result;
    }

    /**
     * 코드 상세 정보의 중복 여부를 확인합니다.
     * 
     * @param vo 확인할 코드 상세 정보를 담은 VO 객체
     * @return 중복 여부 (true: 중복, false: 중복 아님)
     */
    @Override
    public boolean isCodeDetailDuplicated(CodeDetailVO vo) {
        int count = codeMapper.selectCodeDetailExistsCount(vo);
        return count > 0;
    }
}
