package ru.ibs.socrates.confexporter.dao.repository;

import jakarta.persistence.criteria.Predicate;
import java.time.OffsetDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import ru.ibs.socrates.confexporter.dao.entity.MessageEntity;

/**
 * Репозиторий для работы с таблицей {@link MessageEntity} в PostgreSQL.
 * Поддерживает стандартные CRUD-операции и кастомные запросы.
 */
public interface MessageRepository extends JpaRepository<MessageEntity, UUID>,
    JpaSpecificationExecutor<MessageEntity> {

  /**
   * Получение 20 записей контента.
   * @return
   */
  List<MessageEntity> findFirst20By();

  /**
   * Находит контент по внешнему ID (content_id).
   *
   * @param externalMessagelId Внешний ID (формат UUID, не null)
   * @return {@link Optional} с записью или пустой результат
   */
  Optional<MessageEntity> findByExternalMessagelId(UUID externalMessagelId);
  
  /**
   * Найти все после даты.
   * 
   * @param timestamp
   * @return
   */
  List<MessageEntity> findAllByUpdatedAfter(OffsetDateTime timestamp);
  
  /**
   * Получение суммы размера текста для всех страниц проекта.
   * 
   * @param sourceId
   * @param limit
   * @return
   */
  @Query("""
      SELECT SUM(m.contentSize) 
      FROM MessageEntity m 
      WHERE m.id IN (
          SELECT m2.id FROM MessageEntity m2
          WHERE m2.project.sourceId = :sourceId
          ORDER BY m2.updated DESC
          LIMIT :limit
      )""")
  Optional<Long> sumContentSizeByProjectWithLimit(
      @Param("sourceId") UUID sourceId, @Param("limit") int limit);
  
  /**
   * Получение суммы размера текста для всех страниц проекта после даты.
   * 
   * @param sourceId
   * @param limit
   * @param dateFrom - MessageEntity.updated после dateFrom
   * @return
   */
  @Query("""
      SELECT SUM(m.contentSize) 
      FROM MessageEntity m 
      WHERE m.id IN (
          SELECT m2.id FROM MessageEntity m2
          WHERE m2.project.sourceId = :sourceId
              AND m2.updated > :dateFrom
          ORDER BY m2.updated DESC
          LIMIT :limit
      )""")
  Optional<Long> sumContentSizeByProjectAndAfterDateFromWithLimit(
      @Param("sourceId") UUID sourceId, OffsetDateTime dateFrom, @Param("limit") int limit);
  

  /**
   * Полчение MessageEntity по projectId и contentType с учетом паджинации.
   * 
   * @param sourceId
   * @param contentType
   * @param pageable
   * @return
   */
  default Page<MessageEntity> findTopByProjectAndContentType(
      @NonNull UUID sourceId,
      @Nullable String contentType,
      @Nullable OffsetDateTime dateFrom,
      Pageable pageable) {
    Specification<MessageEntity> specification = (root, criteriaQuery, criteriaBuilder) -> {
      List<Predicate> predicates = new ArrayList<>();
      predicates.add(root.get("project").get("sourceId").in(sourceId));
      Optional.ofNullable(contentType)
          .ifPresent(ps -> predicates.add(root.get("contentType").in(contentType)));
      Optional.ofNullable(dateFrom)
          .ifPresent(ps -> predicates.add(criteriaBuilder.greaterThanOrEqualTo(
              root.get("updated"), dateFrom)));
      return criteriaBuilder
          .and(predicates.toArray(new Predicate[0]));
    };
    return findAll(specification, pageable);
  }

}
