/*
 * Hibernate, Relational Persistence for Idiomatic Java
 *
 * License: GNU Lesser General Public License (LGPL), version 2.1 or later.
 * See the lgpl.txt file in the root directory or <http://www.gnu.org/licenses/lgpl-2.1.html>.
 */
package org.hibernate.userguide.sql;

import java.sql.Statement;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.NamedNativeQueries;
import javax.persistence.NamedNativeQuery;
import javax.persistence.PrimaryKeyJoinColumn;
import javax.persistence.SecondaryTable;
import javax.persistence.Table;

import org.hibernate.Session;
import org.hibernate.annotations.Loader;
import org.hibernate.annotations.ResultCheckStyle;
import org.hibernate.annotations.SQLDelete;
import org.hibernate.annotations.SQLInsert;
import org.hibernate.jpa.test.BaseEntityManagerFunctionalTestCase;

import org.junit.Before;
import org.junit.Test;

import org.jboss.logging.Logger;

import static org.hibernate.userguide.util.TransactionUtil.doInJPA;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertNull;

/**
 * @author Vlad Mihalcea
 */
public class CustomSQLSecondaryTableTest extends BaseEntityManagerFunctionalTestCase {

	private static final Logger log = Logger.getLogger( CustomSQLSecondaryTableTest.class );

	@Override
	protected Class<?>[] getAnnotatedClasses() {
		return new Class<?>[] {
			Person.class
		};
	}

	@Before
	public void init() {
		doInJPA( this::entityManagerFactory, entityManager -> {
			Session session = entityManager.unwrap( Session.class );
			session.doWork( connection -> {
				try(Statement statement = connection.createStatement(); ) {
					statement.executeUpdate( "ALTER TABLE person ADD COLUMN valid boolean" );
					statement.executeUpdate( "ALTER TABLE person_details ADD COLUMN valid boolean" );
				}
			} );
		});
	}

	@Test
	public void test_sql_custom_crud() {

		Person _person = doInJPA( this::entityManagerFactory, entityManager -> {
			Person person = new Person();
			person.setName( "John Doe" );
			entityManager.persist( person );
			person.setImage( new byte[] {1, 2, 3} );
			return person;
		} );

		doInJPA( this::entityManagerFactory, entityManager -> {
			Long postId = _person.getId();
			Person person = entityManager.find( Person.class, postId );
			assertArrayEquals(new byte[] {1, 2, 3}, person.getImage());
			entityManager.remove( person );
		} );

		doInJPA( this::entityManagerFactory, entityManager -> {
			Long postId = _person.getId();
			Person person = entityManager.find( Person.class, postId );
			assertNull(person);
		} );
	}


	//tag::sql-custom-crud-secondary-table-example[]
	@Entity(name = "Person")
	@Table(name = "person")
	@SQLInsert(
		sql = "INSERT INTO person (name, id, valid) VALUES (?, ?, true) "
	)
	@SQLDelete(
		sql = "UPDATE person SET valid = false WHERE id = ? "
	)
	@SecondaryTable(name = "person_details",
		pkJoinColumns = @PrimaryKeyJoinColumn(name = "person_id"))
	@org.hibernate.annotations.Table(
		appliesTo = "person_details",
		sqlInsert = @SQLInsert(
			sql = "INSERT INTO person_details (image, person_id, valid) VALUES (?, ?, true) ",
			check = ResultCheckStyle.COUNT
		),
		sqlDelete = @SQLDelete(
			sql = "UPDATE person_details SET valid = false WHERE person_id = ? "
		)
	)
	@Loader(namedQuery = "find_valid_person")
	@NamedNativeQueries({
		@NamedNativeQuery(
			name = "find_valid_person",
			query = "select " +
					"    p.id, " +
					"    p.name, " +
					"    pd.image  " +
					"from person p  " +
					"left outer join person_details pd on p.id = pd.person_id  " +
					"where p.id = ? and p.valid = true and pd.valid = true",
			resultClass = Person.class
		)
	})
	public static class Person {

		@Id
		@GeneratedValue
		private Long id;

		private String name;

		@Column(name = "image", table = "person_details")
		private byte[] image;

		public Long getId() {
			return id;
		}

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

		public String getName() {
			return name;
		}

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

		public byte[] getImage() {
			return image;
		}

		public void setImage(byte[] image) {
			this.image = image;
		}
	}
	//end::sql-custom-crud-secondary-table-example[]

}
