package de.gecheckt.pdf.umbenenner.application.validation.technicaltest;

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

/**
 * Führt einen bestätigten {@link CorrectionPlan} aus, indem er jeden enthaltenen
 * {@link CorrectionSuggestion}-Vorschlag über den {@link ResourceCreationPort} ausführt.
 * <p>
 * Der Service iteriert alle Vorschläge im Plan und gibt pro Vorschlag ein
 * {@link CorrectionOutcome} an den {@link ResourceCreationPort} weiter. Das Gesamtergebnis
 * wird als {@link CorrectionExecutionReport} zurückgegeben.
 *
 * <h2>Kein Frühabbruch</h2>
 * <p>
 * Wenn eine Korrektur scheitert, laufen alle weiteren Korrekturen trotzdem weiter.
 * Ein einzelnes {@link CorrectionOutcome.Failed} führt nicht zum Abbruch.
 *
 * <h2>Aufrufkonvention</h2>
 * <p>
 * Dieser Service darf nur nach ausdrücklicher Benutzerbestätigung des
 * {@link CorrectionPlan} aufgerufen werden. Es darf keine stille Ausführung im
 * Hintergrund geben. Da die Ausführung I/O-intensiv sein kann, sollte der Aufruf
 * auf einem Hintergrund-Worker-Thread erfolgen.
 *
 * <h2>Thread-Safety</h2>
 * <p>
 * Diese Klasse ist zustandslos und thread-safe, sofern der injizierte
 * {@link ResourceCreationPort} ebenfalls thread-safe ist.
 */
public class CorrectionExecutionService {

    private final ResourceCreationPort port;

    /**
     * Erstellt einen neuen Ausführungsservice.
     *
     * @param port der Port für schreibende Korrekturmaßnahmen; darf nicht {@code null} sein
     * @throws NullPointerException wenn {@code port} {@code null} ist
     */
    public CorrectionExecutionService(ResourceCreationPort port) {
        this.port = Objects.requireNonNull(port, "port must not be null");
    }

    /**
     * Führt alle Korrekturvorschläge im übergebenen Plan aus.
     * <p>
     * Iteriert die {@link CorrectionSuggestion}s des Plans und dispatcht jeden Vorschlag
     * an die passende Methode des {@link ResourceCreationPort}. Alle Ergebnisse werden
     * gesammelt und als {@link CorrectionExecutionReport} zurückgegeben. Ein Fehler bei
     * einem Vorschlag führt nicht zum Abbruch der Ausführung der nachfolgenden Vorschläge.
     * <p>
     * Wenn der Plan leer ist, wird ein leerer Bericht zurückgegeben.
     *
     * @param plan der zu ausführende Korrekturplan; darf nicht {@code null} sein
     * @return Bericht mit einem {@link CorrectionOutcome} pro Vorschlag; nie {@code null}
     * @throws NullPointerException wenn {@code plan} {@code null} ist
     */
    public CorrectionExecutionReport execute(CorrectionPlan plan) {
        Objects.requireNonNull(plan, "plan must not be null");
        List<CorrectionOutcome> outcomes = new ArrayList<>(plan.size());

        for (CorrectionSuggestion suggestion : plan.suggestions()) {
            CorrectionOutcome outcome = dispatch(suggestion);
            outcomes.add(outcome);
        }

        return new CorrectionExecutionReport(outcomes);
    }

    /**
     * Dispatcht einen einzelnen {@link CorrectionSuggestion} an die passende Methode
     * des {@link ResourceCreationPort}.
     *
     * @param suggestion der auszuführende Korrekturvorschlag; nie {@code null}
     * @return Ausführungsergebnis; nie {@code null}
     */
    private CorrectionOutcome dispatch(CorrectionSuggestion suggestion) {
        return switch (suggestion) {
            case CorrectionSuggestion.CreateDirectory cd -> port.createDirectory(cd);
            case CorrectionSuggestion.CreatePromptFile cp -> port.createPromptFile(cp);
            case CorrectionSuggestion.PrepareSqlitePath ps -> port.prepareSqlitePath(ps);
        };
    }
}
