package com.spring.domain.post.batch;

import java.util.ArrayList;
import java.util.List;

import javax.persistence.EntityManagerFactory;

import org.springframework.batch.core.Job;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.Step;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.job.builder.JobBuilder;
import org.springframework.batch.core.job.flow.FlowExecutionStatus;
import org.springframework.batch.core.job.flow.JobExecutionDecider;
import org.springframework.batch.core.launch.support.RunIdIncrementer;
import org.springframework.batch.core.step.builder.StepBuilder;
import org.springframework.batch.core.step.tasklet.Tasklet;
import org.springframework.batch.item.ItemProcessor;
import org.springframework.batch.item.ItemWriter;
import org.springframework.batch.item.database.JpaPagingItemReader;
import org.springframework.batch.item.database.builder.JpaPagingItemReaderBuilder;
import org.springframework.batch.repeat.RepeatStatus;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;
import org.springframework.transaction.PlatformTransactionManager;

import com.spring.domain.post.entity.Post;
import com.spring.domain.post.entity.PostBackUp;
import com.spring.domain.post.repository.PostBackUpRepository;
import com.spring.domain.post.repository.PostRepository;
import com.spring.infra.batch.AbstractBatchChunk;
import com.spring.infra.batch.BatchJobInfo;
import com.spring.infra.db.orm.jpa.SecondaryJpaConfig;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@BatchJobInfo(
    group = "${batch-info.post-create-batch.group}",
    jobName = "${batch-info.post-create-batch.job-name}",
    cronExpression = "${batch-info.post-create-batch.cron-expression}",
    description = "${batch-info.post-create-batch.description}"
)
@RequiredArgsConstructor
public class PostCreateBatchChunk extends AbstractBatchChunk {

    @Autowired
    @Override
    public void setTransactionManager(@Qualifier(SecondaryJpaConfig.TRANSACTION_MANAGER) PlatformTransactionManager transactionManager) {
        super.setTransactionManager(transactionManager);
    }

    @Qualifier(SecondaryJpaConfig.ENTITY_MANAGER_FACTORY)
    private final EntityManagerFactory entityManagerFactory;

    private final PostRepository postRepository;
    private final PostBackUpRepository postBackUpRepository;

    private List<Post> list = new ArrayList<>();

    @Override
    public Job createJob() {
        return new JobBuilder(batchJobInfoData.getJobName())
                .repository(jobRepository)
                .incrementer(new RunIdIncrementer())
                .start(readListStep())
                .next(decider())
                .from(decider()).on("PROCESS").to(processStep())
                .from(decider()).on("TERMINATE").to(terminateStep())
                .end()
                .build();
    }

    private JobExecutionDecider decider() {
        return (JobExecution jobExecution, StepExecution stepExecution) ->
                !list.isEmpty() ? new FlowExecutionStatus("PROCESS") : new FlowExecutionStatus("TERMINATE");
    }

    private Step readListStep() {
        return new StepBuilder("readListStep")
                .repository(jobRepository)
                .transactionManager(transactionManager)
                .tasklet(readListTasklet())
                .build();
    }

    private Tasklet readListTasklet() {
        return (contribution, chunkContext) -> {
            list = postRepository.findAll();
            return RepeatStatus.FINISHED;
        };
    }

    private Step processStep() {
        return new StepBuilder("processStep")
                .repository(jobRepository)
                .transactionManager(transactionManager)
                .<Post, PostBackUp>chunk(5)
                .reader(testReader())
                .processor(testProcessor())
                .writer(testWriter())
                .build();
    }

    private JpaPagingItemReader<Post> testReader() {
        return new JpaPagingItemReaderBuilder<Post>()
                .name("testReader")
                .entityManagerFactory(entityManagerFactory)
                .pageSize(5)
                .queryString("select p from Post p")
                .build();
    }

    private ItemProcessor<Post, PostBackUp> testProcessor() {
        return post -> 
            PostBackUp.builder().postId(post.getPostId()).content(post.getContent()).title(post.getTitle()).build();
    }

    private ItemWriter<PostBackUp> testWriter() {
        return postBackUpRepository::saveAll;
    }

    private Step terminateStep() {
        return new StepBuilder("terminateStep")
                .repository(jobRepository)
                .transactionManager(transactionManager)
                .tasklet(terminateTasklet())
                .build();
    }

    private Tasklet terminateTasklet() {
        return (contribution, chunkContext) -> {
            log.error("List Read Error : List is null");
            return RepeatStatus.FINISHED;
        };
    }

}
