package org.raddatz.familienarchiv.document;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.raddatz.familienarchiv.exception.DomainException;
import org.raddatz.familienarchiv.exception.ErrorCode;
import org.raddatz.familienarchiv.document.Document;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;

/**
 * Sequentially regenerates thumbnails for documents that have a file attached but no
 * thumbnail yet. Runs on the {@code thumbnailExecutor} pool — single-threaded iteration
 * is intentional: PDFBox + ImageIO are memory-heavy and we cap peak usage by processing
 * documents one at a time. Only one backfill can run at a time; concurrent starts are
 * rejected with {@link ErrorCode#THUMBNAIL_BACKFILL_ALREADY_RUNNING}.
 */
@Service
@RequiredArgsConstructor
@Slf4j
public class ThumbnailBackfillService {

    public enum State { IDLE, RUNNING, DONE, FAILED }

    public record BackfillStatus(
            State state,
            String message,
            int total,
            int processed,
            int skipped,
            int failed,
            LocalDateTime startedAt
    ) {}

    private final DocumentService documentService;
    private final ThumbnailService thumbnailService;

    private volatile BackfillStatus currentStatus = new BackfillStatus(
            State.IDLE, "Kein Backfill gestartet.", 0, 0, 0, 0, null);

    public BackfillStatus getStatus() {
        return currentStatus;
    }

    @Async("thumbnailExecutor")
    public void runBackfillAsync() {
        if (currentStatus.state() == State.RUNNING) {
            throw DomainException.conflict(ErrorCode.THUMBNAIL_BACKFILL_ALREADY_RUNNING,
                    "Thumbnail-Backfill läuft bereits");
        }

        LocalDateTime startedAt = LocalDateTime.now();
        List<Document> docs;
        try {
            docs = documentService.findForThumbnailBackfill();
        } catch (Exception e) {
            currentStatus = new BackfillStatus(State.FAILED,
                    "Backfill fehlgeschlagen: " + e.getMessage(),
                    0, 0, 0, 0, startedAt);
            log.warn("Thumbnail backfill aborted before starting: {}", e.getMessage());
            return;
        }

        int total = docs.size();
        currentStatus = new BackfillStatus(State.RUNNING,
                "Backfill läuft…", total, 0, 0, 0, startedAt);
        log.info("Thumbnail backfill started: total={}", total);

        int processed = 0;
        int skipped = 0;
        int failed = 0;
        for (Document doc : docs) {
            ThumbnailService.Outcome outcome;
            try {
                outcome = thumbnailService.generate(doc);
            } catch (Exception e) {
                log.warn("Thumbnail generation failed for doc={} reason={}",
                        doc.getId(), e.getMessage());
                outcome = ThumbnailService.Outcome.FAILED;
            }
            switch (outcome) {
                case SUCCESS -> processed++;
                case SKIPPED -> skipped++;
                case FAILED -> failed++;
            }
            currentStatus = new BackfillStatus(State.RUNNING,
                    "Backfill läuft…", total, processed, skipped, failed, startedAt);
        }

        long durationMs = Duration.between(startedAt, LocalDateTime.now()).toMillis();
        log.info("Thumbnail backfill complete: total={} processed={} skipped={} failed={} durationMs={}",
                total, processed, skipped, failed, durationMs);

        currentStatus = new BackfillStatus(State.DONE,
                String.format("Fertig: %d erzeugt, %d übersprungen, %d fehlgeschlagen.",
                        processed, skipped, failed),
                total, processed, skipped, failed, startedAt);
    }
}
