/*
 * Copyright © 2014-2017 EntIT Software LLC, a Micro Focus company (L.P.)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package io.cloudslang.engine.partitions.services;

import io.cloudslang.engine.data.SimpleHiloIdentifierGenerator;
import io.cloudslang.engine.partitions.entities.PartitionGroup;
import liquibase.integration.spring.SpringLiquibase;
import org.apache.commons.dbcp.BasicDataSource;
import org.hibernate.jpa.HibernatePersistenceProvider;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.FactoryBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.TransactionAwareDataSourceProxy;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.orm.jpa.JpaVendorAdapter;
import org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.test.annotation.DirtiesContext;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.transaction.TransactionConfiguration;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionTemplate;

import javax.persistence.EntityManagerFactory;
import javax.sql.DataSource;

import static org.fest.assertions.Assertions.assertThat;

/**
 * Date: 4/23/12
 */
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration
@Transactional
@TransactionConfiguration(defaultRollback = true)
public class PartitionTemplateWithEmfTest {
    private final static boolean SHOW_SQL = false;

    @Autowired
    private ApplicationContext applicationContext;

    @Autowired
    private PartitionService service;

    private static final String tableName = "my_table";

    @Test
    @DirtiesContext
    public void startFirstTime() {
        applicationContext.getBean(tableName, PartitionTemplate.class);
        PartitionGroup partitionGroup = service.readPartitionGroup(tableName);
        assertThat(partitionGroup).isNotNull();
        assertThat(partitionGroup.getName()).isEqualTo(tableName);
    }

    @Test
    @DirtiesContext
    public void startSecondTime() { // the partition group is already exist
        service.createPartitionGroup(tableName, Integer.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE);
        PartitionGroup partitionGroupOrig = service.readPartitionGroup(tableName);

        applicationContext.getBean(tableName, PartitionTemplate.class);
        PartitionGroup partitionGroup = service.readPartitionGroup(tableName);
        assertThat(partitionGroup).isEqualTo(partitionGroupOrig);
    }


    @Test
    @DirtiesContext
    public void iteratorTest() {
        service.createPartitionGroup(tableName, Integer.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE);
        PartitionGroup partitionGroupOrig = service.readPartitionGroup(tableName);

        applicationContext.getBean(tableName, PartitionTemplate.class);
        PartitionGroup partitionGroup = service.readPartitionGroup(tableName);
        assertThat(partitionGroup).isEqualTo(partitionGroupOrig);
    }

    @Test
    @DirtiesContext
    public void updatePartitionGroup() {
        service.createPartitionGroup(tableName, Integer.MAX_VALUE, Long.MAX_VALUE, Long.MAX_VALUE);

        final String newGroupName = "newName";
        final int newGroupSize = 10;
        final int newTimeThreshold = 20;
        final int newSizeThreshold = 30;

        service.updatePartitionGroup(newGroupName, newGroupSize, newTimeThreshold, newSizeThreshold);

        applicationContext.getBean(tableName, PartitionTemplate.class);
        PartitionGroup partitionGroup = service.readPartitionGroup(tableName);
        assertThat(partitionGroup.getName().equals(newGroupName));
        assertThat(partitionGroup.getGroupSize() == newGroupSize);
        assertThat(partitionGroup.getSizeThreshold() == newTimeThreshold);
        assertThat(partitionGroup.getSizeThreshold() == newSizeThreshold);
    }

    @Configuration
    @EnableJpaRepositories("io.cloudslang.engine.partitions.repositories")
    @EnableTransactionManagement
    static class Configurator {

        @Bean
        DataSource dataSource() {
            BasicDataSource ds = new BasicDataSource();
            ds.setDriverClassName("org.h2.Driver");
            ds.setUrl("jdbc:h2:mem:test");
            ds.setUsername("sa");
            ds.setPassword("sa");
            ds.setDefaultAutoCommit(false);
            return new TransactionAwareDataSourceProxy(ds);
        }

        @Bean
        SpringLiquibase liquibase(DataSource dataSource) {
            SpringLiquibase liquibase = new SpringLiquibase();
            liquibase.setDataSource(dataSource);
            liquibase.setChangeLog("classpath:/META-INF/database/test.changes.xml");
            SimpleHiloIdentifierGenerator.setDataSource(dataSource);
            return liquibase;
        }

        @Bean
        JpaVendorAdapter jpaVendorAdapter() {
            HibernateJpaVendorAdapter adapter = new HibernateJpaVendorAdapter();
            adapter.setShowSql(SHOW_SQL);
            adapter.setGenerateDdl(true);
            return adapter;
        }

        @Bean
        @DependsOn("liquibase")
        LocalContainerEntityManagerFactoryBean entityManagerFactory(JpaVendorAdapter jpaVendorAdapter) {
            LocalContainerEntityManagerFactoryBean fb = new LocalContainerEntityManagerFactoryBean();
            fb.setDataSource(dataSource());
            fb.setPersistenceProviderClass(HibernatePersistenceProvider.class);
            fb.setPackagesToScan("io.cloudslang.engine.partitions");
            fb.setJpaVendorAdapter(jpaVendorAdapter);
            return fb;
        }

        @Bean
        PartitionService service() {
            return new PartitionServiceImpl();
        }

        @Bean(name = tableName)
        @Lazy
        PartitionTemplate template() {
            return new PartitionTemplateImpl();
        }

        @Bean
        PlatformTransactionManager transactionManager(EntityManagerFactory emf) throws Exception {
            return new JpaTransactionManager(emf);
        }

        @Bean
        TransactionTemplate createTransactionTemplate(PlatformTransactionManager transactionManager) throws Exception {
            return new TransactionTemplate(transactionManager);
        }

        @Bean
        JdbcTemplate jdbcTemplate(DataSource dataSource) {
            return new JdbcTemplate(dataSource);
        }

        @Bean
        public PartitionUtils partitionUtils() {
            return new PartitionUtils();
        }
    }
}