package com.bio.bio_backend.domain.admin.common_code.service;

import com.bio.bio_backend.domain.admin.common_code.dto.CommonCodeDto;
import com.bio.bio_backend.domain.admin.common_code.dto.CommonGroupCodeDto;
import com.bio.bio_backend.domain.admin.common_code.dto.GetCommonCodeResponseDto;
import com.bio.bio_backend.domain.admin.common_code.entity.CommonCode;
import com.bio.bio_backend.domain.admin.common_code.entity.CommonGroupCode;
import com.bio.bio_backend.domain.admin.common_code.mapper.CommonCodeMapper;
import com.bio.bio_backend.domain.admin.common_code.mapper.CommonGroupCodeMapper;
import com.bio.bio_backend.domain.admin.common_code.repository.CommonCodeRepository;
import com.bio.bio_backend.domain.admin.common_code.repository.CommonGroupCodeRepository;
import com.bio.bio_backend.global.exception.ApiException;
import com.bio.bio_backend.global.constants.ApiResponseCode;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class CommonCodeServiceImpl implements CommonCodeService {
    
    private final CommonGroupCodeRepository commonGroupCodeRepository;
    private final CommonCodeRepository commonCodeRepository;
    private final CommonCodeMapper commonCodeMapper;
    private final CommonGroupCodeMapper commonGroupCodeMapper;
    
    // 그룹 코드 관련 메서드들
    @Override
    @Transactional
    public CommonGroupCodeDto createGroupCode(CommonGroupCodeDto groupCodeDto) {
        if (commonGroupCodeRepository.existsByCode(groupCodeDto.getCode())) {
            throw new ApiException(ApiResponseCode.COMMON_CODE_DUPLICATE);
        }
        
        CommonGroupCode groupCode = commonGroupCodeMapper.toCommonGroupCode(groupCodeDto);
        CommonGroupCode savedGroupCode = commonGroupCodeRepository.save(groupCode);
        return commonGroupCodeMapper.toCommonGroupCodeDto(savedGroupCode);
    }
    
    @Override
    @Transactional
    public void updateGroupCode(String code, CommonGroupCodeDto groupCodeDto) {
        CommonGroupCode existingGroupCode = commonGroupCodeRepository.findByCode(code)
            .orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_TARGET_NOT_FOUND));
        
        commonGroupCodeMapper.updateCommonGroupCodeFromDto(groupCodeDto, existingGroupCode);
        commonGroupCodeRepository.save(existingGroupCode);
    }
    
    @Override
    @Transactional
    public void deleteGroupCode(String code) {
        CommonGroupCode groupCode = commonGroupCodeRepository.findByCode(code)
            .orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_TARGET_NOT_FOUND));
        
        // 하위 공통 코드가 있는지 확인
        if (commonCodeRepository.existsByGroupCode(code)) {
            throw new ApiException(ApiResponseCode.COMMON_CODE_ERROR_001);
        }
        
        commonGroupCodeRepository.delete(groupCode);
    }
    
    @Override
    public CommonGroupCodeDto getGroupCode(String code) {
        CommonGroupCode groupCode = commonGroupCodeRepository.findByCode(code)
            .orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_NOT_FOUND));
        return commonGroupCodeMapper.toCommonGroupCodeDto(groupCode);
    }
    
    @Override
    public List<CommonGroupCodeDto> getAllGroupCodes() {
        List<CommonGroupCode> groupCodes = commonGroupCodeRepository.findAll();
        return commonGroupCodeMapper.toCommonGroupCodeDtoList(groupCodes);
    }
    
    @Override
    public List<CommonGroupCodeDto> getActiveGroupCodes() {
        List<CommonGroupCode> groupCodes = commonGroupCodeRepository.findByUseFlagOrderBySortOrderAsc(true);
        return commonGroupCodeMapper.toCommonGroupCodeDtoList(groupCodes);
    }
    
    // 공통 코드 관련 메서드들
    @Override
    @Transactional
    public CommonCodeDto createCode(CommonCodeDto commonCodeDto) {
        if (commonCodeRepository.existsByCode(commonCodeDto.getCode())) {
            throw new ApiException(ApiResponseCode.COMMON_CODE_DUPLICATE);
        }
        
        // 그룹 코드 존재 여부 확인
        if (!commonGroupCodeRepository.existsByCode(commonCodeDto.getGroupCode())) {
            throw new ApiException(ApiResponseCode.COMMON_CODE_ERROR_002);
        }
        
        CommonCode commonCode = commonCodeMapper.toCommonCode(commonCodeDto);
        CommonCode savedCommonCode = commonCodeRepository.save(commonCode);
        return commonCodeMapper.toCommonCodeDto(savedCommonCode);
    }
    
    @Override
    @Transactional
    public void updateCode(String code, CommonCodeDto commonCodeDto) {
        CommonCode existingCommonCode = commonCodeRepository.findByCode(code)
            .orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_CODE_ERROR_003));
        
        commonCodeMapper.updateCommonCodeFromDto(commonCodeDto, existingCommonCode);
        commonCodeRepository.save(existingCommonCode);
    }
    
    @Override
    @Transactional
    public void deleteCode(String code) {
        CommonCode commonCode = commonCodeRepository.findByCode(code)
            .orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_CODE_ERROR_003));
        
        // 하위 공통 코드가 있는지 확인
        if (commonCodeRepository.existsByParentCode(code)) {
            throw new ApiException(ApiResponseCode.COMMON_CODE_ERROR_001);
        }
        
        commonCodeRepository.delete(commonCode);
    }
    
    @Override
    public CommonCodeDto getCode(String code) {
        CommonCode commonCode = commonCodeRepository.findByCode(code)
            .orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_CODE_ERROR_003));
        return commonCodeMapper.toCommonCodeDto(commonCode);
    }
    
    @Override
    public GetCommonCodeResponseDto getCodeWithJoinInfo(String code) {
        return commonCodeRepository.findCodeWithJoinInfo(code)
            .orElseThrow(() -> new ApiException(ApiResponseCode.COMMON_CODE_ERROR_003));
    }
    
    @Override
    public List<CommonCodeDto> getActiveCodesByGroupCode(String groupCode) {
        List<CommonCode> commonCodes = commonCodeRepository.findByGroupCode(groupCode);
        return commonCodeMapper.toCommonCodeDtoList(commonCodes);
    }
    
    @Override
    public List<CommonCodeDto> getActiveCodesByParentCode(String parentCode) {
        List<CommonCode> commonCodes = commonCodeRepository.findByParentCode(parentCode);
        return commonCodeMapper.toCommonCodeDtoList(commonCodes);
    }
    
    @Override
    public List<CommonCodeDto> getAllCodes() {
        List<CommonCode> commonCodes = commonCodeRepository.findAll();
        return commonCodeMapper.toCommonCodeDtoList(commonCodes);
    }
}
