package de.avatic.lcc.service.bulk;

import de.avatic.lcc.dto.bulk.BulkOperationState;
import de.avatic.lcc.dto.bulk.BulkProcessingType;
import de.avatic.lcc.model.db.error.SysError;
import de.avatic.lcc.model.db.error.SysErrorType;
import de.avatic.lcc.repositories.bulk.BulkOperationRepository;
import de.avatic.lcc.repositories.error.SysErrorRepository;
import de.avatic.lcc.service.transformer.error.SysErrorTransformer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.util.Arrays;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.TimeUnit;

@Service
public class BulkOperationExecutionService {

    private static final Logger logger = LoggerFactory.getLogger(BulkOperationExecutionService.class);
    private final BulkOperationRepository bulkOperationRepository;
    private final BulkExportService bulkExportService;
    private final BulkImportService bulkImportService;
    private final SysErrorRepository sysErrorRepository;
    private final SysErrorTransformer sysErrorTransformer;


    public BulkOperationExecutionService(BulkOperationRepository bulkOperationRepository, BulkExportService bulkExportService, BulkImportService bulkImportService, SysErrorRepository sysErrorRepository, SysErrorTransformer sysErrorTransformer) {
        this.bulkOperationRepository = bulkOperationRepository;
        this.bulkExportService = bulkExportService;
        this.bulkImportService = bulkImportService;
        this.sysErrorRepository = sysErrorRepository;
        this.sysErrorTransformer = sysErrorTransformer;

    }

    @Async("bulkProcessingExecutor")
    public CompletableFuture<Void> launchExecution(Integer id) {
        logger.info("Starting bulk operation execution for ID: {}", id);
        try {
            execution(id);
            logger.info("Bulk operation execution completed successfully for ID: {}", id);
            return CompletableFuture.completedFuture(null);
        } catch (Throwable e) {
            logger.error("Error during bulk operation execution for ID: {}", id, e);
            bulkOperationRepository.updateState(id, BulkOperationState.EXCEPTION);

            var error = new SysError();
            error.setType(SysErrorType.BULK);
            error.setCode(e.getClass().getSimpleName());
            error.setTitle("Bulk operation execution id" + id + " failed");
            error.setMessage(e.getMessage() == null ? "" : e.getMessage());
            error.setUserId(null);
            error.setBulkOperationId(id);
            error.setTrace(Arrays.stream(e.getStackTrace()).map(sysErrorTransformer::toSysErrorTraceItem).toList());

            sysErrorRepository.insert(error);

            return CompletableFuture.failedFuture(e);
        }
    }

    public void execution(Integer id) {
        logger.debug("Executing bulk operation for ID: {}", id);
        var operation = bulkOperationRepository.getOperationById(id);

        if (operation.isPresent()) {
            var op = operation.get();

            if (op.getProcessState() == BulkOperationState.SCHEDULED) {
                logger.info("Processing bulk operation ID: {}, type: {}, file type: {}", id, op.getProcessingType(), op.getFileType());
                bulkOperationRepository.updateState(id, BulkOperationState.PROCESSING);
                try {
                    if (op.getProcessingType() == BulkProcessingType.EXPORT) {
                        bulkExportService.processOperation(op);
                        op.setProcessState(BulkOperationState.COMPLETED);

                    } else {
                        bulkImportService.processOperation(op);
                        op.setProcessState(BulkOperationState.COMPLETED);

                    }
                } catch (Throwable e) {

                    logger.error("Error during bulk operation execution for ID: {}", id, e);

                    op.setProcessState(BulkOperationState.EXCEPTION);

                    var error = new SysError();
                    error.setType(SysErrorType.BULK);
                    error.setCode(e.getClass().getSimpleName());
                    error.setTitle("Bulk operation execution " + op.getProcessingType() + " of " + op.getFileType() + " failed");
                    error.setMessage(e.getMessage() == null ? "" : e.getMessage());
                    error.setUserId(op.getUserId());
                    error.setBulkOperationId(op.getId());
                    error.setTrace(Arrays.stream(e.getStackTrace()).map(sysErrorTransformer::toSysErrorTraceItem).toList());

                    sysErrorRepository.insert(error);
                }

                bulkOperationRepository.update(op);
            }
        }
    }

}
