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

import com.vladmihalcea.book.hpjp.hibernate.identifier.Identifiable;
import com.vladmihalcea.book.hpjp.util.AbstractTest;
import org.hibernate.Session;
import org.hibernate.annotations.CreationTimestamp;
import org.hibernate.annotations.GenericGenerator;
import org.junit.Test;

import javax.persistence.*;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;

public class CloneTest extends AbstractTest {

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

    private Post post;

    @Override
    protected void afterInit() {
        doInJPA(entityManager -> {
            Tag java = new Tag();
            java.setName("Java");

            entityManager.persist(java);

            Tag jdbc = new Tag();
            jdbc.setName("JDBC");

            entityManager.persist(jdbc);

            Tag jpa = new Tag();
            jpa.setName("JPA");

            entityManager.persist(jpa);

            Tag jooq = new Tag();
            jooq.setName("jOOQ");

            entityManager.persist(jooq);
        });

        doInJPA(entityManager -> {
            post = new Post();
            post.setTitle("High-Performance Java Persistence, 1st edition");

            PostDetails details = new PostDetails();
            details.setCreatedBy("Vlad Mihalcea");
            post.addDetails(details);

            post.getTags().add(entityManager.getReference(Tag.class, "Java"));
            post.getTags().add(entityManager.getReference(Tag.class, "JDBC"));
            post.getTags().add(entityManager.getReference(Tag.class, "JPA"));
            post.getTags().add(entityManager.getReference(Tag.class, "jOOQ"));

            PostComment comment1 = new PostComment();
            comment1.setReview("This book is a big one");
            post.addComment(comment1);

            PostComment comment2 = new PostComment();
            comment2.setReview("5 stars");
            post.addComment(comment2);

            entityManager.persist(post);
        });
    }

    @Test
    public void testClone() {
        Long postId = post.getId();
        doInJPA(entityManager -> {
            Post post = entityManager.find(Post.class, postId);

            Post postClone = new Post(post);
            entityManager.persist(postClone);
        });
    }

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

        @Id
        @GeneratedValue
        private Long id;

        private String title;

        public Post() {}

        public Post(Post post) {
            this.title = post.title;

            addDetails(new PostDetails(post.details));

            tags.addAll(post.getTags());
        }

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

        @OneToOne(cascade = CascadeType.ALL, mappedBy = "post",
                orphanRemoval = true, fetch = FetchType.LAZY)
        private PostDetails details;

        @ManyToMany
        @JoinTable(name = "post_tag",
                joinColumns = @JoinColumn(name = "post_id"),
                inverseJoinColumns = @JoinColumn(name = "tag_id")
        )
        private List<Tag> tags = new ArrayList<>();

        public Long getId() {
            return id;
        }

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

        public String getTitle() {
            return title;
        }

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

        public List<PostComment> getComments() {
            return comments;
        }

        public PostDetails getDetails() {
            return details;
        }

        public List<Tag> getTags() {
            return tags;
        }

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

        public void addDetails(PostDetails details) {
            this.details = details;
            details.setPost(this);
        }

        public void removeDetails() {
            this.details.setPost(null);
            this.details = null;
        }
    }

    @Entity(name = "PostDetails")
    @Table(name = "post_details")
    public static class PostDetails {

        @Id
        private Long id;

        @Column(name = "created_on")
        @CreationTimestamp
        private Date createdOn;

        @Column(name = "created_by")
        private String createdBy;

        public PostDetails() {
        }

        public PostDetails(PostDetails details) {
            this.createdBy = details.createdBy;
        }

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

        public Long getId() {
            return id;
        }

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

        public Post getPost() {
            return post;
        }

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

        public Date getCreatedOn() {
            return createdOn;
        }

        public void setCreatedOn(Date createdOn) {
            this.createdOn = createdOn;
        }

        public String getCreatedBy() {
            return createdBy;
        }

        public void setCreatedBy(String createdBy) {
            this.createdBy = createdBy;
        }
    }

    @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 void setId(Long id) {
            this.id = id;
        }

        public Post getPost() {
            return post;
        }

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

        public String getReview() {
            return review;
        }

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

    @Entity(name = "Tag")
    @Table(name = "tag")
    public static class Tag {

        @Id
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }

}

@@ -398,63 +398,9 @@
 til.
-ArrayList;%0Aimport java.util.Date;%0Aimport java.util.List
+*
 ;%0A%0Ap
@@ -3393,19 +3393,18 @@
 private 
-Lis
+Se
 t%3CTag%3E t
@@ -3409,32 +3409,30 @@
  tags = new 
-ArrayLis
+HashSe
 t%3C%3E();%0A%0A    
@@ -3910,19 +3910,18 @@
  public 
-Lis
+Se
 t%3CTag%3E g

Switch to Set instead of List