package app.mealsmadeeasy.api.recipe;

import app.mealsmadeeasy.api.user.User;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Slice;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

import java.util.List;
import java.util.Optional;

public interface RecipeRepository extends JpaRepository<Recipe, Integer> {

    Slice<Recipe> findAllByIsPublicIsTrue(Pageable pageable);

    List<Recipe> findAllByViewersContaining(User viewer);

    List<Recipe> findAllByOwner(User owner);

    @Query("SELECT r from Recipe r WHERE r.owner.username = ?1 AND r.slug = ?2")
    Optional<Recipe> findByOwnerUsernameAndSlug(String ownerUsername, String slug);

    @Query("SELECT r FROM Recipe r WHERE size(r.stars) >= ?1 AND (r.isPublic OR ?2 MEMBER OF r.viewers)")
    List<Recipe> findAllViewableByStarsGreaterThanEqual(long stars, User viewer);

    @Query("SELECT r FROM Recipe r WHERE r.id = ?1")
    @EntityGraph(attributePaths = { "viewers" })
    Optional<Recipe> findByIdWithViewers(long id);

    @Query("SELECT r FROM Recipe r WHERE r.id = ?1")
    @EntityGraph(attributePaths = { "stars" })
    Optional<Recipe> findByIdWithStars(long id);

    @Query("SELECT size(r.stars) FROM Recipe r WHERE r.id = ?1")
    int getStarCount(long recipeId);

    @Query("SELECT size(r.viewers) FROM Recipe r WHERE r.id = ?1")
    int getViewerCount(long recipeId);

    @Query("SELECT r FROM Recipe r WHERE r.isPublic OR r.owner = ?1 OR ?1 MEMBER OF r.viewers")
    Slice<Recipe> findAllViewableBy(User viewer, Pageable pageable);

    @Query(
            nativeQuery = true,
            value = """
            WITH distances AS (SELECT recipe_id, embedding <=> cast(?1 AS vector) AS distance FROM recipe_summary)
            SELECT r.* FROM distances d
            INNER JOIN recipe r ON r.id = d.recipe_id
            WHERE d.distance < ?2 AND (
                r.is_public = TRUE
                OR r.owner_id = ?3
                OR exists(SELECT 1 FROM recipe_viewer v WHERE v.recipe_id = r.id AND v.viewer_id = ?3)
            )
            ORDER BY d.distance;
            """
    )
    List<Recipe> searchByEmbeddingAndViewableBy(float[] queryEmbedding, float similarity, Integer viewerId);

    @Query(
            nativeQuery = true,
            value = """
            WITH distances AS (SELECT recipe_id, embedding <=> cast(?1 AS vector) AS distance FROM recipe_summary)
            SELECT r.* FROM distances d
            INNER JOIN recipe r ON r.id = d.recipe_id
            WHERE d.distance < ?2 AND r.is_public = TRUE
            ORDER BY d.distance;
            """
    )
    List<Recipe> searchByEmbeddingAndIsPublic(float[] queryEmbedding, float similarity);

    int countByIsPublicIsTrue();

    @Query("SELECT count(r) FROM Recipe r WHERE ?1 = r.owner OR ?1 MEMBER OF r.viewers OR r.isPublic IS TRUE")
    int countViewableBy(User viewer);

    void deleteRecipeByOwnerUsernameAndSlug(String username, String slug);

}
