package com.zioinfo.hrm.uiws.system.service;

import com.zioinfo.hrm.uiws.common.UiwsApiException;
import com.zioinfo.hrm.uiws.common.UiwsErrorCode;
import com.zioinfo.hrm.uiws.system.dto.CodeGrpDetailDto;
import com.zioinfo.hrm.uiws.system.dto.CodeGrpDto;
import com.zioinfo.hrm.uiws.system.dto.CodeGrpSaveDto;
import com.zioinfo.hrm.uiws.system.dto.CodeValueDto;
import com.zioinfo.hrm.uiws.system.dto.PageResponse;
import com.zioinfo.hrm.uiws.system.mapper.CodeMapper;
import com.zioinfo.hrm.uiws.system.model.SysCode;
import com.zioinfo.hrm.uiws.system.model.SysCodeGrp;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

/**
 * 2.7 코드(codes). 그룹 + 값 복합 관리. 그룹 수정/등록 시 값 목록 전체 치환.
 * 사용중(use_yn='Y') 코드값이 있으면 그룹 삭제 차단.
 */
@Service
@RequiredArgsConstructor
public class CodeService {

    private final CodeMapper codeMapper;

    @Transactional(readOnly = true)
    public PageResponse<CodeGrpDto> listGroups(String keyword, int page, int size) {
        List<CodeGrpDto> content = codeMapper.searchGroups(keyword, page * size, size).stream()
                .map(g -> new CodeGrpDto(g.getGrpCd(), g.getGrpNm(), g.getUseYn())).toList();
        return PageResponse.of(content, codeMapper.countGroups(keyword), page, size);
    }

    @Transactional(readOnly = true)
    public CodeGrpDetailDto getGroup(String grpCd) {
        return toDetail(findGrp(grpCd));
    }

    @Transactional(readOnly = true)
    public List<CodeValueDto> getValues(String grpCd) {
        return codeMapper.findActiveValuesByGrp(grpCd).stream().map(this::toValue).toList();
    }

    @Transactional
    public CodeGrpDetailDto create(CodeGrpSaveDto dto) {
        if (codeMapper.groupExists(dto.grpCd())) {
            throw new UiwsApiException(UiwsErrorCode.DUPLICATE_KEY, "이미 존재하는 코드그룹입니다.");
        }
        SysCodeGrp grp = new SysCodeGrp();
        grp.setGrpCd(dto.grpCd());
        grp.setGrpNm(dto.grpNm());
        grp.setUseYn(dto.useYn());
        grp.setCreatedBy(SysActor.id());
        codeMapper.insertGroup(grp);
        replaceValues(dto.grpCd(), dto.values());
        return toDetail(findGrp(dto.grpCd()));
    }

    @Transactional
    public CodeGrpDetailDto update(String grpCd, CodeGrpSaveDto dto) {
        SysCodeGrp grp = findGrp(grpCd);
        grp.setGrpNm(dto.grpNm());
        grp.setUseYn(dto.useYn());
        grp.setUpdatedBy(SysActor.id());
        codeMapper.updateGroup(grp);
        codeMapper.deleteValuesByGrp(grpCd);
        replaceValues(grpCd, dto.values());
        return toDetail(findGrp(grpCd));
    }

    @Transactional
    public void delete(String grpCd) {
        findGrp(grpCd);
        boolean inUse = codeMapper.findValuesByGrp(grpCd).stream().anyMatch(c -> "Y".equals(c.getUseYn()));
        if (inUse) {
            throw new UiwsApiException(UiwsErrorCode.RESOURCE_IN_USE,
                    "사용중인 코드값이 존재하여 코드그룹을 삭제할 수 없습니다.");
        }
        codeMapper.deleteValuesByGrp(grpCd);
        codeMapper.deleteGroup(grpCd);
    }

    private void replaceValues(String grpCd, List<CodeValueDto> values) {
        if (values == null) {
            return;
        }
        String actor = SysActor.id();
        for (CodeValueDto v : values) {
            SysCode code = new SysCode();
            code.setGrpCd(grpCd);
            code.setCodeVal(v.codeVal());
            code.setCodeNm(v.codeNm());
            code.setSortOrd(v.sortOrd() == null ? 0 : v.sortOrd());
            code.setUseYn(v.useYn() == null ? "Y" : v.useYn());
            code.setCreatedBy(actor);
            codeMapper.insertValue(code);
        }
    }

    private CodeGrpDetailDto toDetail(SysCodeGrp grp) {
        List<CodeValueDto> values = codeMapper.findValuesByGrp(grp.getGrpCd()).stream().map(this::toValue).toList();
        return new CodeGrpDetailDto(grp.getGrpCd(), grp.getGrpNm(), grp.getUseYn(), values);
    }

    private CodeValueDto toValue(SysCode c) {
        return new CodeValueDto(c.getGrpCd(), c.getCodeVal(), c.getCodeNm(),
                c.getSortOrd() == null ? 0 : c.getSortOrd(), c.getUseYn());
    }

    private SysCodeGrp findGrp(String grpCd) {
        SysCodeGrp g = codeMapper.findGroupById(grpCd);
        if (g == null) {
            throw new UiwsApiException(UiwsErrorCode.CODE_GRP_NOT_FOUND);
        }
        return g;
    }
}
