package jpa.teste;

import com.mycompany.jarmusic.Album;
import com.mycompany.jarmusic.Artista;
import com.mycompany.jarmusic.Musica;
import com.mycompany.jarmusic.Playlist;
import com.mycompany.jarmusic.Usuario;
import java.util.List;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;
import javax.persistence.Query;
import javax.persistence.TypedQuery;
import javax.validation.ConstraintViolation;
import javax.validation.ConstraintViolationException;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
import static org.junit.Assert.*;
import org.junit.FixMethodOrder;
import org.junit.Rule;
import org.junit.rules.TestWatcher;
import org.junit.runner.Description;
import org.junit.runners.MethodSorters;

@FixMethodOrder(MethodSorters.NAME_ASCENDING)
@SuppressWarnings("JPQLValidation")
public class JpqlTest {

    private static Logger logger = Logger.getGlobal();
    private EntityManager em;
    private EntityTransaction et;
    private static EntityManagerFactory emf;

    @BeforeClass
    public static void setUpClass() {
        logger.setLevel(Level.SEVERE);
        emf = Persistence.createEntityManagerFactory("jarmusic");
        DbUnitUtil.inserirDados();
    }

    @AfterClass
    public static void tearDownClass() {
        emf.close();
    }

    @Before
    public void setUp() {
        em = emf.createEntityManager();
        et = em.getTransaction();
        et.begin();
    }

    @After
    public void tearDown() {
        et.commit();
        em.close();
    }

    @Test
    public void t01_inserirUsuario() {
        logger.info("Executando t01: persistir Usuario");
        Usuario usuario = new Usuario();
        usuario.setNome("Guthemberg");
        usuario.setEmail("guthemberg@gmail.com");
        usuario.setSenha("123456as");
        em.persist(usuario);
        em.flush();
        assertNotNull(usuario.getId());
        logger.log(Level.INFO, "Usuario {0} incluído com sucesso.", usuario);
    }

    @Test
    public void t02_criarUsuario() {
        Usuario usuario = new Usuario();
        usuario.setNome("Joao");
        usuario.setEmail("joao@gmail.com");
        usuario.setSenha("m1nhAs3nh4");
        em.persist(usuario);
        em.flush();
        assertNotNull(usuario.getId());
    }

    @Test
    public void t03_criarUsuarioInvalido() {
        Usuario usuario = null;
        try {
            usuario = new Usuario();
            usuario.setNome("joao");//errado
            usuario.setEmail("joao@gmail.com");
            usuario.setSenha("123");//errado
            em.persist(usuario);
            em.flush();
            assertTrue(false);
        } catch (ConstraintViolationException ex) {
            Logger.getGlobal().info(ex.getMessage());

            Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();

            if (logger.isLoggable(Level.INFO)) {
                for (ConstraintViolation violation : constraintViolations) {
                    Logger.getGlobal().log(Level.INFO, "{0}.{1}: {2}", new Object[]{violation.getRootBeanClass(), violation.getPropertyPath(), violation.getMessage()});
                }
            }

            assertEquals(2, constraintViolations.size());
            assertNull(usuario.getId());
        }
    }

    @Test
    public void t04_atualizarUsuarioInvalido() {
        Logger.getGlobal().log(Level.INFO, "t03_criarUsuarioInvalido");
        TypedQuery<Usuario> query = em.createQuery("SELECT u FROM Usuario u WHERE u.nome like :nome", Usuario.class);
        query.setParameter("nome", "Joao");
        Usuario usuario = query.getSingleResult();
        usuario.setSenha("testando1234");

        try {
            em.flush();
            assertTrue(false);
        } catch (ConstraintViolationException ex) {
            Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();

            if (logger.isLoggable(Level.INFO)) {
                for (ConstraintViolation violation : constraintViolations) {
                    Logger.getGlobal().log(Level.INFO, "{0}.{1}: {2}", new Object[]{violation.getRootBeanClass(), violation.getPropertyPath(), violation.getMessage()});
                }
            }

            assertEquals(1, constraintViolations.size());
        }
    }

    @Test
    public void t05_buscarUsuarioPorNome() {

        logger.info("Executando t05: SELECT u FROM Usuario u WHERE u.nome LIKE :nome ORDER BY c.id");
        TypedQuery<Usuario> query = em.createQuery(
                "SELECT u FROM Usuario u WHERE u.nome LIKE :nome ORDER BY u.id",
                Usuario.class);
        query.setParameter("nome", "guthemberg");
        List<Usuario> usuarios = query.getResultList();

        for (Usuario usuario : usuarios) {
            assertTrue(usuario.getNome().startsWith("guthemberg"));
        }

        assertEquals(1, usuarios.size());
    }

    @Test
    public void t06_selecionarAlbumPopulado() {
        logger.info("Executando t03: SELECT a FROM tb_album a\n"
                + "where exists( select 1 from tb_album_musica am where\n"
                + " am.ID_ALBUM = a.ID_ALBUM)");
        TypedQuery<Album> query = em.createQuery(
                "SELECT a FROM jarmusic.tb_album a where exists( select 1 from tb_album_musica am where am.ID_ALBUM = a.ID_ALBUM)",
                Album.class);
        List<Album> albuns = query.getResultList();

        assertEquals(1, albuns.size());
    }

    @Test
    public void t07_atualizarUsuarioMerge() {
        logger.info("Executando t04: atualizar Usuario com Merge");
        TypedQuery<Usuario> query = em.createNamedQuery("Usuario.PorNome", Usuario.class);
        query.setParameter("nome", "Guto");
        Usuario usuario = query.getSingleResult();
        assertNotNull(usuario);
        em.clear();
        usuario.setNome("Leo do paranaue");
        em.merge(usuario);
        em.flush();
        em.clear();
        usuario = query.getSingleResult();
        assertEquals(1, query.getResultList().size());
        assertEquals("Leo do paranaue", usuario.getNome());
    }

    @Test
    public void t08_removerUsuario() {
        logger.info("Executando t05: remover Usuario");
        TypedQuery<Usuario> query = em.createNamedQuery("Usuario.PorNome", Usuario.class);
        query.setParameter("nome", "Leo do paranaue");
        Usuario usuario = query.getSingleResult();
        assertNotNull(usuario);
        em.remove(usuario);
        em.flush();
        assertEquals(0, query.getResultList().size());
    }

    @Test
    public void t09_atualizarUsuario() {
        logger.info("Executando t06: atualizar Usuario");
        TypedQuery<Usuario> query = em.createNamedQuery("Usuario.PorNome", Usuario.class);
        query.setParameter("nome", "Zuzu");
        Usuario usuario = query.getSingleResult();
        assertNotNull(usuario);
        usuario.setNome("Mark Zuke");
        em.flush();
    }

    @Test
    public void t10_persistirUsuario() {
        logger.info("Executando t10: persistir Categoria");
        Usuario usuario = new Usuario();
        usuario.setNome("Pablo");
        usuario.setEmail("pablo@gmail.com");
        usuario.setSenha("alguma12");
        em.persist(usuario);
        em.flush();
        assertNotNull(usuario.getId());
        logger.log(Level.INFO, "Usuario {0} incluído com sucesso.", usuario);
    }

    @Test
    public void t11_buscarUsuarioInserido07() {

        logger.info("Executando t08: SELECT u FROM Usuario u WHERE u.nome LIKE :nome ORDER BY c.id");
        TypedQuery<Usuario> query = em.createQuery(
                "SELECT u FROM Usuario u WHERE u.nome LIKE :nome ORDER BY u.id",
                Usuario.class);
        query.setParameter("nome", "Pablo");
        List<Usuario> usuarios = query.getResultList();

        for (Usuario usuario : usuarios) {
            assertTrue(usuario.getNome().startsWith("Pablo"));
        }
        assertEquals(1, usuarios.size());
    }

    @Test
    public void t12_usuarioPorNomeEspecifico() {
        logger.info("Executando t01: Usuario.PorNome");
        TypedQuery<Usuario> query = em.createNamedQuery("Usuario.PorNome", Usuario.class);
        query.setParameter("nome", "Joao");
        List<Usuario> usuarios = query.getResultList();

        for (Usuario usuario : usuarios) {
            assertTrue(usuario.getNome().startsWith("Joao"));
        }

        assertEquals(1, usuarios.size());
    }

    @Test
    public void t13_musicasMais3segundos() {
        logger.info("Executando t13: SELECT c FROM Musica c WHERE c.duracao > 3");
        TypedQuery<Musica> query = em.createQuery("SELECT c FROM Musica c WHERE c.duracao > 3", Musica.class);
        List<Musica> musicas = query.getResultList();
        assertEquals(4, musicas.size());
    }

    @Test
    public void t14_delete() {
        logger.info("Executando t15: DELETE Musica AS o WHERE o.id = ?1");
        Long id = (long) 6;
        Query query = em.createQuery("DELETE Musica AS o WHERE o.ID_MUSICA = ?1");
        query.setParameter(2, id);
        query.executeUpdate();
        Musica musica = em.find(Musica.class, id);
        assertNull(musica);
        logger.log(Level.INFO, "Musica {0} removida com sucesso.", id);
    }

    @Test
    public void t15_persistirUsuario() {
        logger.info("Executando t10: persistir Categoria");
        Usuario usuario = new Usuario();
        usuario.setNome("Pablo");
        usuario.setEmail("pablo@gmail.com");
        usuario.setSenha("alguma123");
        em.persist(usuario);
        em.flush();
        assertNotNull(usuario.getId());
        logger.log(Level.INFO, "Usuario {0} incluído com sucesso.", usuario);
    }

    @Test
    public void t16_atualizarUsuario2() {
        logger.info("Executando t06: atualizar Usuario");
        TypedQuery<Usuario> query = em.createNamedQuery("Usuario.PorNome", Usuario.class);
        query.setParameter("nome", "Zuzu");
        Usuario usuario = query.getSingleResult();
        assertNotNull(usuario);
        usuario.setNome("Mark Zuke");
        em.flush();
        assertEquals(1, query.getResultList().size());
    }

    @Test
    public void t17_atualizarUsuarioMerge() {
        logger.info("Executando t17: atualizar Usuario com Merge");
        TypedQuery<Usuario> query = em.createNamedQuery("Usuario.PorNome", Usuario.class);
        query.setParameter("nome", "Guto");
        Usuario usuario = query.getSingleResult();
        assertNotNull(usuario);
        em.clear();
        usuario.setNome("Leozem");
        em.merge(usuario);
        em.flush();
        assertEquals(1, query.getResultList().size());
    }

    @Test
    public void t18_removerUsuario() {
        logger.info("Executando t07: remover Usuario");
        TypedQuery<Usuario> query = em.createNamedQuery("Usuario.PorNome", Usuario.class);
        query.setParameter("nome", "Leo");
        Usuario usuario = query.getSingleResult();
        assertNotNull(usuario);
        em.remove(usuario);
        em.flush();
        assertEquals(0, query.getResultList().size());
    }

}
