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

import com.kamco.cd.kamcoback.postgres.entity.QAnimalEntity;
import com.kamco.cd.kamcoback.postgres.entity.QZooEntity;
import com.kamco.cd.kamcoback.postgres.entity.ZooEntity;
import com.kamco.cd.kamcoback.zoo.dto.ZooDto;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.impl.JPAQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

@Repository
@RequiredArgsConstructor
public class ZooRepositoryImpl implements ZooRepositoryCustom {

  private final JPAQueryFactory queryFactory;
  private final QZooEntity qZoo = QZooEntity.zooEntity;
  private final QAnimalEntity qAnimal = QAnimalEntity.animalEntity;

  @Override
  public Page<ZooEntity> listZoo(ZooDto.SearchReq searchReq) {
    Pageable pageable = searchReq.toPageable();

    JPAQuery<ZooEntity> query =
        queryFactory
            .selectFrom(qZoo)
            .where(
                qZoo.isDeleted.eq(false),
                nameContains(searchReq.getName()),
                locationContains(searchReq.getLocation()));

    long total = query.fetchCount();

    List<ZooEntity> content =
        query
            .offset(pageable.getOffset())
            .limit(pageable.getPageSize())
            .orderBy(qZoo.createdDate.desc())
            .fetch();

    return new PageImpl<>(content, pageable, total);
  }

  @Override
  public Optional<ZooEntity> getZooByUuid(String uuid) {
    return Optional.ofNullable(
        queryFactory
            .selectFrom(qZoo)
            .where(qZoo.uuid.eq(UUID.fromString(uuid)), qZoo.isDeleted.eq(false))
            .fetchOne());
  }

  @Override
  public Optional<ZooEntity> getZooByUid(Long uid) {
    return Optional.ofNullable(
        queryFactory.selectFrom(qZoo).where(qZoo.uid.eq(uid), qZoo.isDeleted.eq(false)).fetchOne());
  }

  @Override
  public Long countActiveAnimals(Long zooId) {
    Long count =
        queryFactory
            .select(qAnimal.count())
            .from(qAnimal)
            .where(qAnimal.zoo.uid.eq(zooId), qAnimal.isDeleted.eq(false))
            .fetchOne();

    return count != null ? count : 0L;
  }

  @Override
  public Map<Long, Long> countActiveAnimalsByZooIds(List<Long> zooIds) {
    if (zooIds == null || zooIds.isEmpty()) {
      return new HashMap<>();
    }

    // QueryDSL group by로 한 번에 조회
    List<com.querydsl.core.Tuple> results =
        queryFactory
            .select(qAnimal.zoo.uid, qAnimal.count())
            .from(qAnimal)
            .where(qAnimal.zoo.uid.in(zooIds), qAnimal.isDeleted.eq(false))
            .groupBy(qAnimal.zoo.uid)
            .fetch();

    // Map으로 변환
    Map<Long, Long> countMap = new HashMap<>();
    for (com.querydsl.core.Tuple tuple : results) {
      Long zooId = tuple.get(qAnimal.zoo.uid);
      Long count = tuple.get(qAnimal.count());
      if (zooId != null && count != null) {
        countMap.put(zooId, count);
      }
    }

    // 조회되지 않은 Zoo는 0으로 설정
    for (Long zooId : zooIds) {
      countMap.putIfAbsent(zooId, 0L);
    }

    return countMap;
  }

  private BooleanExpression nameContains(String name) {
    return name != null && !name.isEmpty() ? qZoo.name.contains(name) : null;
  }

  private BooleanExpression locationContains(String location) {
    return location != null && !location.isEmpty() ? qZoo.location.contains(location) : null;
  }
}
