package com.kamco.cd.kamcoback.postgres.core;

import com.kamco.cd.kamcoback.common.service.BaseCoreService;
import com.kamco.cd.kamcoback.postgres.entity.ZooEntity;
import com.kamco.cd.kamcoback.postgres.repository.ZooRepository;
import com.kamco.cd.kamcoback.zoo.dto.ZooDto;
import jakarta.persistence.EntityNotFoundException;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class ZooCoreService implements BaseCoreService<ZooDto.Detail, Long, ZooDto.SearchReq> {

  private final ZooRepository zooRepository;

  @Transactional(readOnly = true)
  public ZooDto.Detail getDataByUuid(String uuid) {
    ZooEntity zoo =
        zooRepository
            .getZooByUuid(uuid)
            .orElseThrow(() -> new EntityNotFoundException("Zoo not found with uuid: " + uuid));
    return toDetailDto(zoo);
  }

  // AddReq를 받는 추가 메서드
  @Transactional
  public ZooDto.Detail create(ZooDto.AddReq req) {
    ZooEntity entity = new ZooEntity(req.getName(), req.getLocation(), req.getDescription());
    ZooEntity saved = zooRepository.save(entity);
    return toDetailDto(saved);
  }

  @Override
  @Transactional
  public void remove(Long id) {
    ZooEntity zoo =
        zooRepository
            .getZooByUid(id)
            .orElseThrow(() -> new EntityNotFoundException("Zoo not found with id: " + id));
    zoo.deleted();
  }

  @Override
  public ZooDto.Detail getOneById(Long id) {
    ZooEntity zoo =
        zooRepository
            .getZooByUid(id)
            .orElseThrow(() -> new EntityNotFoundException("Zoo not found with id: " + id));
    return toDetailDto(zoo);
  }

  @Override
  public Page<ZooDto.Detail> search(ZooDto.SearchReq searchReq) {
    Page<ZooEntity> zooEntities = zooRepository.listZoo(searchReq);

    // N+1 문제 해결: 한 번의 쿼리로 모든 Zoo의 animal count 조회
    List<Long> zooIds =
        zooEntities.getContent().stream().map(ZooEntity::getUid).collect(Collectors.toList());

    Map<Long, Long> animalCountMap = zooRepository.countActiveAnimalsByZooIds(zooIds);

    // DTO 변환
    List<ZooDto.Detail> details =
        zooEntities.getContent().stream()
            .map(zoo -> toDetailDtoWithCountMap(zoo, animalCountMap))
            .collect(Collectors.toList());

    return new PageImpl<>(details, zooEntities.getPageable(), zooEntities.getTotalElements());
  }

  // Entity -> Detail DTO 변환 (동물 개수 포함) - 단건 조회용
  private ZooDto.Detail toDetailDto(ZooEntity zoo) {
    Long activeAnimalCount = zooRepository.countActiveAnimals(zoo.getUid());
    return new ZooDto.Detail(
        zoo.getUid(),
        zoo.getUuid().toString(),
        zoo.getName(),
        zoo.getLocation(),
        zoo.getDescription(),
        zoo.getCreatedDate(),
        zoo.getModifiedDate(),
        activeAnimalCount);
  }

  // Entity -> Detail DTO 변환 (동물 개수 포함) - 배치 조회용 (N+1 해결)
  private ZooDto.Detail toDetailDtoWithCountMap(ZooEntity zoo, Map<Long, Long> countMap) {
    Long activeAnimalCount = countMap.getOrDefault(zoo.getUid(), 0L);
    return new ZooDto.Detail(
        zoo.getUid(),
        zoo.getUuid().toString(),
        zoo.getName(),
        zoo.getLocation(),
        zoo.getDescription(),
        zoo.getCreatedDate(),
        zoo.getModifiedDate(),
        activeAnimalCount);
  }
}
