package cn.yunzhixue.ability.center.examsprint.application.report;

import cn.yunzhixue.ability.center.examsprint.contracts.report.ExamSprintReportGenerationStatus;
import cn.yunzhixue.ability.center.examsprint.domain.report.ExamSprintReport;
import cn.yunzhixue.ability.center.examsprint.domain.report.ExamSprintReportPdfGenerator;
import cn.yunzhixue.ability.center.examsprint.domain.report.ExamSprintReportRenderer;
import cn.yunzhixue.ability.center.examsprint.domain.report.ExamSprintReportRepository;
import cn.yunzhixue.ability.center.examsprint.domain.report.ExamSprintReportStorage;
import org.springframework.stereotype.Service;

import java.time.Clock;
import java.time.Instant;
import java.util.List;

@Service
public class ExamSprintReportGenerationWorker {

    private final ExamSprintReportRepository repository;
    private final List<ExamSprintReportRenderer> renderers;
    private final ExamSprintReportPdfGenerator pdfGenerator;
    private final ExamSprintReportStorage storage;
    private final Clock clock;

    public ExamSprintReportGenerationWorker(
            ExamSprintReportRepository repository,
            List<ExamSprintReportRenderer> renderers,
            ExamSprintReportPdfGenerator pdfGenerator,
            ExamSprintReportStorage storage,
            Clock clock) {
        this.repository = repository;
        this.renderers = renderers;
        this.pdfGenerator = pdfGenerator;
        this.storage = storage;
        this.clock = clock;
    }

    public void process(String reportId) {
        Instant startedAt = clock.instant();
        ExamSprintReport report = repository.findById(reportId).orElse(null);
        if (report == null
                || report.generationStatus() != ExamSprintReportGenerationStatus.PENDING
                || report.isExpiredAt(startedAt)) {
            return;
        }

        repository.save(report.processing(startedAt));

        try {
            String html = rendererFor(report).render(report.payload(), startedAt);
            byte[] pdfBytes = pdfGenerator.generate(html);
            String typeSegment = report.reportType().name().toLowerCase();
            String fileName = "exam-sprint-" + typeSegment + "-report-" + report.reportId() + ".pdf";
            ExamSprintReportStorage.StoredExamSprintReportFile storedFile =
                    storage.upload(report.reportId(), report.reportType(), fileName, pdfBytes, report.expiresAt());
            repository.save(repository.findById(reportId)
                    .orElseThrow()
                    .success(clock.instant(), storedFile.storageObjectKey(), storedFile.fileName()));
        } catch (Exception exception) {
            repository.findById(reportId)
                    .ifPresent(current -> repository.save(current.failed(clock.instant(), failureReasonOf(exception))));
        }
    }

    private ExamSprintReportRenderer rendererFor(ExamSprintReport report) {
        return renderers.stream()
                .filter(renderer -> renderer.supports(report.reportType()))
                .findFirst()
                .orElseThrow(() -> new IllegalStateException("No renderer for report type " + report.reportType()));
    }

    private String failureReasonOf(Exception exception) {
        String message = exception.getMessage();
        return message == null || message.isBlank() ? exception.getClass().getSimpleName() : message;
    }
}
