package com.vladmihalcea.book.hpjp.hibernate.fetching;

import com.vladmihalcea.book.hpjp.util.AbstractPostgreSQLIntegrationTest;
import com.vladmihalcea.book.hpjp.util.providers.Database;
import org.hibernate.jpa.QueryHints;
import org.junit.Test;

import jakarta.persistence.*;
import java.time.LocalDate;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @author Vlad Mihalcea
 */
public class DistinctTest extends AbstractPostgreSQLIntegrationTest {

    @Override
    protected Class<?>[] entities() {
        return new Class<?>[]{
            Post.class,
            PostComment.class,
        };
    }

    @Override
    protected Database database() {
        return Database.POSTGRESQL;
    }

    @Override
    public void afterInit() {
        doInJPA(entityManager -> {
            entityManager.persist(
                new Post()
                    .setId(1L)
                    .setTitle("High-Performance Java Persistence eBook has been released!")
                    .setCreatedOn(LocalDate.of(2016, 8, 30))
                    .addComment(new PostComment("Excellent!"))
                    .addComment(new PostComment("Great!"))
            );

            entityManager.persist(
                new Post()
                    .setId(2L)
                    .setTitle("High-Performance Java Persistence paperback has been released!")
                    .setCreatedOn(LocalDate.of(2016, 10, 12))
            );

            entityManager.persist(
                new Post()
                    .setId(3L)
                    .setTitle("High-Performance Java Persistence Mach 1 video course has been released!")
                    .setCreatedOn(LocalDate.of(2018, 1, 30))
            );

            entityManager.persist(
                new Post()
                    .setId(4L)
                    .setTitle("High-Performance Java Persistence Mach 2 video course has been released!")
                    .setCreatedOn(LocalDate.of(2018, 5, 8))
            );
        });
    }

    @Test
    public void testWithDistinctScalarQuery() {
        doInJPA(entityManager -> {
            List<Integer> publicationYears = entityManager.createQuery("""
                select distinct year(p.createdOn)
                from Post p
                order by year(p.createdOn)
                """, Integer.class)
            .getResultList();

            LOGGER.info("Publication years: {}", publicationYears);
        });
    }

    @Test
    public void testWithoutDistinct() {
        doInJPA(entityManager -> {
            List<Post> posts = entityManager.createQuery("""
                select p
                from Post p
                left join fetch p.comments
                where p.title = :title
                """, Post.class)
            .setParameter("title", "High-Performance Java Persistence eBook has been released!")
            .getResultList();

            LOGGER.info("Fetched the following Post entity identifiers: {}", posts.stream().map(Post::getId).collect(Collectors.toList()));
        });
    }

    @Test
    public void testWithDistinct() {
        doInJPA(entityManager -> {
            List<Post> posts = entityManager.createQuery("""
                select distinct p
                from Post p
                left join fetch p.comments
                where p.title = :title
                """, Post.class)
            .setParameter("title", "High-Performance Java Persistence eBook has been released!")
            .getResultList();

            LOGGER.info("Fetched the following Post entity identifiers: {}", posts.stream().map(Post::getId).collect(Collectors.toList()));
        });
    }

    @Test
    public void testWithDistinctAndQueryHint() {
        doInJPA(entityManager -> {
            List<Post> posts = entityManager.createQuery("""
                select distinct p
                from Post p
                left join fetch p.comments
                where p.title = :title
                """, Post.class)
            .setParameter("title", "High-Performance Java Persistence eBook has been released!")
            .getResultList();

            LOGGER.info("Fetched the following Post entity identifiers: {}", posts.stream().map(Post::getId).collect(Collectors.toList()));
        });
    }

    @Entity(name = "Post")
    @Table(name = "post")
    public static class Post {

        @Id
        private Long id;

        private String title;

        @Column(name = "created_on")
        private LocalDate createdOn;

        @OneToMany(
            mappedBy = "post",
            cascade = CascadeType.ALL,
            orphanRemoval = true
        )
        private List<PostComment> comments = new ArrayList<>();

        public Long getId() {
            return id;
        }

        public Post setId(Long id) {
            this.id = id;
            return this;
        }

        public String getTitle() {
            return title;
        }

        public Post setTitle(String title) {
            this.title = title;
            return this;
        }

        public LocalDate getCreatedOn() {
            return createdOn;
        }

        public Post setCreatedOn(LocalDate createdOn) {
            this.createdOn = createdOn;
            return this;
        }

        public Post addComment(PostComment comment) {
            comments.add(comment);
            comment.setPost(this);
            return this;
        }
    }

    @Entity(name = "PostComment")
    @Table(name = "post_comment")
    public static class PostComment {

        @Id
        @GeneratedValue
        private Long id;

        @ManyToOne(fetch = FetchType.LAZY)
        private Post post;

        private String review;

        public PostComment() {}

        public PostComment(String review) {
            this.review = review;
        }

        public Long getId() {
            return id;
        }

        public PostComment setId(Long id) {
            this.id = id;
            return this;
        }

        public Post getPost() {
            return post;
        }

        public PostComment setPost(Post post) {
            this.post = post;
            return this;
        }

        public String getReview() {
            return review;
        }

        public PostComment setReview(String review) {
            this.review = review;
            return this;
        }
    }
}

@@ -389,16 +389,62 @@
 ctors;%0A%0A
+import static org.junit.Assert.assertEquals;%0A%0A
 /**%0A * @
@@ -3161,28 +3161,33 @@
 oid test
-WithDistinct
+AutoDeduplication
 () %7B%0A   
@@ -3600,751 +3600,99 @@
     
-LOGGER.info(%22Fetched the following Post entity identifiers: %7B%7D%22, posts.stream().map(Post::getId).collect(Collectors.toList()));%0A        %7D);%0A    %7D%0A%0A    @Test%0A    public void testWithDistinctAndQueryHint() %7B%0A        doInJPA(entityManager -%3E %7B%0A            List%3CPost%3E posts = entityManager.createQuery(%22%22%22%0A                select distinct p%0A                from Post p%0A                left join fetch p.comments%0A                where p.title = :title%0A                %22%22%22, Post.class)%0A            .setParameter(%22title%22, %22High-Performance Java Persistence eBook has been released!%22)%0A            .getResultList();%0A%0A            LOGGER.info(%22Fetched the following Post entity identifiers: %7B%7D%22, posts.stream().map(Post::getId).collect(Collectors.toList()
+assertEquals(1, posts.size());%0A            assertEquals(2, posts.get(0).getComments().size(
 ));%0A
@@ -4656,32 +4656,32 @@
 On = createdOn;%0A
-
             retu
@@ -4692,32 +4692,121 @@
 his;%0A        %7D%0A%0A
+        public List%3CPostComment%3E getComments() %7B%0A            return comments;%0A        %7D%0A%0A
         public P

Add auto-distinct test for Hibernate 6