package com.spring.infra.batch;

import java.util.List;

import org.springframework.batch.core.BatchStatus;
import org.springframework.batch.core.ExitStatus;
import org.springframework.batch.core.JobExecution;
import org.springframework.batch.core.JobExecutionListener;
import org.springframework.batch.core.StepExecution;
import org.springframework.batch.core.StepExecutionListener;
import org.springframework.dao.DataAccessException;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.stereotype.Component;

import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
public class BatchExceptionListener implements JobExecutionListener, StepExecutionListener {

    @Override
    public void beforeJob(@NonNull JobExecution jobExecution) {
        log.info("Job 시작: {}", jobExecution.getJobInstance().getJobName());
    }

    @Override
    public void afterJob(@NonNull JobExecution jobExecution) {
        if (jobExecution.getStatus() == BatchStatus.FAILED) {
            handleJobFailure(jobExecution);
        }
    }

    @Override
    public void beforeStep(@NonNull StepExecution stepExecution) {
        log.info("Step 시작: {}", stepExecution.getStepName());
    }

    @Override
    @Nullable
    public ExitStatus afterStep(@NonNull StepExecution stepExecution) {
        if (!stepExecution.getFailureExceptions().isEmpty()) {
            Throwable ex = stepExecution.getFailureExceptions().get(0);
            
            if (ex instanceof DataAccessException) {
                // SQL 예외 처리
                DataAccessException sqlEx = (DataAccessException) ex;
                log.error("SQL 오류 발생: {}", sqlEx.getMostSpecificCause().getMessage());
                return ExitStatus.FAILED;
            } else {
                // 기타 예외 처리
                log.error("예외 발생: ", ex);
                return ExitStatus.FAILED;
            }
        }
        return stepExecution.getExitStatus();
    }

    private void handleJobFailure(JobExecution jobExecution) {
        List<Throwable> exceptions = jobExecution.getAllFailureExceptions();
        for (Throwable ex : exceptions) {
            if (ex instanceof DataAccessException) {
                handleSqlException((DataAccessException) ex, jobExecution);
            } else {
                handleGeneralException(ex, jobExecution);
            }
        }
    }

    private void handleSqlException(DataAccessException ex, JobExecution jobExecution) {
        log.error("Job {} 실행 중 SQL 오류 발생: {}", 
            jobExecution.getJobInstance().getJobName(), 
            ex.getMostSpecificCause().getMessage());
    }

    private void handleGeneralException(Throwable ex, JobExecution jobExecution) {
        log.error("Job {} 실행 중 오류 발생: {}", 
            jobExecution.getJobInstance().getJobName(), 
            ex.getMessage());
    }
    
}
