package de.gecheckt.pdf.umbenenner.adapter.in.gui;

import java.util.Objects;
import java.util.Optional;
import java.util.Set;

import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiBatchRunLaunchOutcome;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiBatchRunLauncher;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiHistoricalDocumentContextPort;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiManualFileCopyPort;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiManualFileRenamePort;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiMiniRunLauncher;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiResetDocumentStatusPort;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.history.GuiDeleteDocumentHistoryPort;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.history.GuiHistoryDetailsPort;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.history.GuiHistoryOverviewPort;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.history.GuiHistoryResetDocumentStatusPort;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiConfigurationEditorState;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.editor.GuiConfigurationEditorStateFactory;
import de.gecheckt.pdf.umbenenner.adapter.in.gui.modelprices.GuiModelPriceManagementPort;
import de.gecheckt.pdf.umbenenner.application.port.in.ResetDocumentStatusResult;
import de.gecheckt.pdf.umbenenner.application.port.in.SchedulerControlUseCase;
import de.gecheckt.pdf.umbenenner.application.port.out.ConfigurationFileLockPort;
import de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog.AiModelCatalogPort;
import de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog.EffectiveApiKeyDescriptor;
import de.gecheckt.pdf.umbenenner.application.validation.editor.ApiKeyResolutionPort;
import de.gecheckt.pdf.umbenenner.application.validation.technicaltest.CorrectionExecutionService;
import de.gecheckt.pdf.umbenenner.application.validation.technicaltest.PathCheckPort;
import de.gecheckt.pdf.umbenenner.application.validation.technicaltest.ProviderTechnicalTestService;
import de.gecheckt.pdf.umbenenner.application.validation.technicaltest.ResourceCreationPort;
import de.gecheckt.pdf.umbenenner.application.validation.technicaltest.TechnicalTestOrchestrator;
import de.gecheckt.pdf.umbenenner.domain.model.DocumentFingerprint;

/**
 * Immutable startup data for the GUI adapter.
 * <p>
 * Carries the initial editor state, the optional startup notice, the file-loading callback,
 * the file-writing callback that the workspace uses for native save actions, the
 * {@link AiModelCatalogPort} used to retrieve available AI model lists on demand, the
 * {@link ApiKeyResolutionPort} used by the editor validation to determine the effective
 * API key provenance from environment variables, the {@link ProviderTechnicalTestService}
 * used to execute provider-specific technical checks, the {@link PathCheckPort}
 * used to verify filesystem path accessibility for configuration values, the
 * {@link TechnicalTestOrchestrator} used by the "Technische Tests ausführen" action, the
 * {@link CorrectionExecutionService} used to execute corrective actions after a
 * technical test run has been confirmed by the user, the {@link GuiBatchRunLauncher} used
 * to execute regular batch runs, the {@link GuiMiniRunLauncher} used to execute targeted
 * mini-runs for selected documents, the {@link GuiResetDocumentStatusPort} used to
 * reset the persistence status of selected documents, and the
 * {@link GuiManualFileRenamePort} used to manually rename a target file from the GUI,
 * the {@link GuiManualFileCopyPort} used to manually copy a source file to the target
 * folder for documents that have not yet been successfully processed, and
 * the {@link GuiHistoricalDocumentContextPort} used to retrieve the historical processing
 * context for documents that were skipped in the current run, the resolved application
 * version string that the status bar displays at the bottom of the main window, and the
 * optional {@link SchedulerControlUseCase} for controlling the automatic scheduler.
 * <p>
 * The optional {@code applicationContextError} carries a human-readable German error
 * message when the bootstrap-side application run context could not be initialised at
 * startup (e.g., invalid or incomplete configuration). An empty value signals that the
 * run context was built successfully and batch runs can be launched immediately.
 * <p>
 * The optional {@code schedulerControlUseCase} is present when the automatic scheduler
 * was successfully wired at startup. An empty value means scheduler control is not
 * available in this startup context (e.g., no valid configuration was loaded at startup).
 * <p>
 * The optional {@code configurationFileLockPort} is present when the GUI can acquire an
 * OS-level exclusive lock on the configuration file before a manual batch run. When present,
 * it is acquired by the {@link de.gecheckt.pdf.umbenenner.adapter.in.gui.batchrun.GuiBatchRunCoordinator}
 * on the worker thread before each run and released in a finally block. An empty value means
 * no locking is performed (e.g., no valid configuration was loaded at startup, or locking is
 * not required in this context).
 * <p>
 * The {@code applicationContextInitializer} is invoked on a background thread each time the
 * workspace loads a configuration file (auto-load at startup and manual open). Bootstrap
 * provides an implementation that builds the application run context and wires the scheduler.
 * <p>
 * All ports and services are supplied by Bootstrap so that the GUI adapter does not need to
 * know about provider-specific HTTP details or adapter wiring.
 */
public record GuiStartupContext(
        GuiConfigurationEditorState initialState,
        Optional<String> startupNotice,
        GuiConfigurationFileLoader configurationFileLoader,
        GuiConfigurationFileWriter configurationFileWriter,
        AiModelCatalogPort modelCatalogPort,
        ApiKeyResolutionPort apiKeyResolutionPort,
        ProviderTechnicalTestService providerTechnicalTestService,
        PathCheckPort pathCheckPort,
        TechnicalTestOrchestrator technicalTestOrchestrator,
        CorrectionExecutionService correctionExecutionService,
        GuiBatchRunLauncher batchRunLauncher,
        GuiMiniRunLauncher miniRunLauncher,
        GuiResetDocumentStatusPort resetDocumentStatusPort,
        GuiManualFileRenamePort manualFileRenamePort,
        GuiManualFileCopyPort manualFileCopyPort,
        GuiHistoricalDocumentContextPort historicalDocumentContextPort,
        String applicationVersion,
        GuiPromptEditorPort promptEditorPort,
        GuiHistoryOverviewPort historyOverviewPort,
        GuiHistoryDetailsPort historyDetailsPort,
        GuiHistoryResetDocumentStatusPort historyResetDocumentStatusPort,
        GuiDeleteDocumentHistoryPort deleteDocumentHistoryPort,
        GuiPromptEditorPortFactory promptEditorPortFactory,
        GuiCreateNewDatabasePort createNewDatabasePort,
        Optional<String> applicationContextError,
        Optional<SchedulerControlUseCase> schedulerControlUseCase,
        Optional<ConfigurationFileLockPort> configurationFileLockPort,
        GuiApplicationContextInitializer applicationContextInitializer,
        Optional<GuiModelPriceManagementPort> modelPriceManagementPort) {
    private static final String NO_PROMPT_PORT_MSG = "Kein Prompt-Editor-Port in diesem Startkontext verfügbar.";
    private static final String NO_PORT_MSG = "Kein Port in diesem Startkontext.";


    /**
     * Creates a fully wired startup context.
     *
     * @param initialState                initial editor state; must not be {@code null}
     * @param startupNotice               optional startup notice; {@code null} becomes empty
     * @param configurationFileLoader     file-loading callback; must not be {@code null}
     * @param configurationFileWriter     file-writing callback; must not be {@code null}
     * @param modelCatalogPort            port for retrieving AI model lists; must not be {@code null}
     * @param apiKeyResolutionPort        port for resolving API key provenance; must not be {@code null}
     * @param providerTechnicalTestService service for provider-specific technical checks; must not be {@code null}
     * @param pathCheckPort               port for filesystem path accessibility checks; must not be {@code null}
     * @param technicalTestOrchestrator   orchestrator for the full technical test run; must not be {@code null}
     * @param correctionExecutionService  service for executing confirmed corrective actions; must not be {@code null}
     * @param batchRunLauncher            bridge that executes a regular batch run; must not be {@code null}
     * @param miniRunLauncher             bridge that executes a targeted mini-run for selected
     *                                    documents; must not be {@code null}
     * @param resetDocumentStatusPort     bridge that resets the persistence status of selected
     *                                    documents; must not be {@code null}
     * @param manualFileRenamePort        bridge that renames a target file manually from the GUI;
     *                                    must not be {@code null}
     * @param manualFileCopyPort          bridge that copies a source file to the target folder for
     *                                    documents that have not yet been successfully processed;
     *                                    must not be {@code null}
     * @param historicalDocumentContextPort bridge that resolves the historical processing context
     *                                      for skipped documents; must not be {@code null}
     * @param applicationVersion            resolved application version string shown in the status
     *                                      bar; {@code null} defaults to {@code "dev"}
     * @param promptEditorPort              bridge zum Prompt-Editor-Use-Case; darf nicht
     *                                      {@code null} sein
     * @param promptEditorPortFactory       Fabrik für Prompt-Editor-Ports bei Konfigurationswechsel;
     *                                      darf nicht {@code null} sein
     * @param applicationContextError       optional error message when the application run context
     *                                      could not be initialised at startup; {@code null} becomes empty
     */
    public GuiStartupContext {
        initialState = Objects.requireNonNull(initialState, "initialState must not be null");
        startupNotice = Objects.requireNonNullElse(startupNotice, Optional.empty());
        applicationContextError = Objects.requireNonNullElse(applicationContextError, Optional.empty());
        configurationFileLoader = Objects.requireNonNull(configurationFileLoader,
                "configurationFileLoader must not be null");
        configurationFileWriter = Objects.requireNonNull(configurationFileWriter,
                "configurationFileWriter must not be null");
        modelCatalogPort = Objects.requireNonNull(modelCatalogPort,
                "modelCatalogPort must not be null");
        apiKeyResolutionPort = Objects.requireNonNull(apiKeyResolutionPort,
                "apiKeyResolutionPort must not be null");
        providerTechnicalTestService = Objects.requireNonNull(providerTechnicalTestService,
                "providerTechnicalTestService must not be null");
        pathCheckPort = Objects.requireNonNull(pathCheckPort,
                "pathCheckPort must not be null");
        technicalTestOrchestrator = Objects.requireNonNull(technicalTestOrchestrator,
                "technicalTestOrchestrator must not be null");
        correctionExecutionService = Objects.requireNonNull(correctionExecutionService,
                "correctionExecutionService must not be null");
        batchRunLauncher = Objects.requireNonNull(batchRunLauncher,
                "batchRunLauncher must not be null");
        miniRunLauncher = Objects.requireNonNull(miniRunLauncher,
                "miniRunLauncher must not be null");
        resetDocumentStatusPort = Objects.requireNonNull(resetDocumentStatusPort,
                "resetDocumentStatusPort must not be null");
        manualFileRenamePort = Objects.requireNonNull(manualFileRenamePort,
                "manualFileRenamePort must not be null");
        manualFileCopyPort = Objects.requireNonNull(manualFileCopyPort,
                "manualFileCopyPort must not be null");
        historicalDocumentContextPort = Objects.requireNonNull(historicalDocumentContextPort,
                "historicalDocumentContextPort must not be null");
        // Null-Fallback für Testumgebungen ohne gepacktes JAR
        applicationVersion = applicationVersion == null ? "dev" : applicationVersion;
        promptEditorPort = Objects.requireNonNull(promptEditorPort, "promptEditorPort must not be null");
        historyOverviewPort = Objects.requireNonNull(historyOverviewPort,
                "historyOverviewPort must not be null");
        historyDetailsPort = Objects.requireNonNull(historyDetailsPort,
                "historyDetailsPort must not be null");
        historyResetDocumentStatusPort = Objects.requireNonNull(historyResetDocumentStatusPort,
                "historyResetDocumentStatusPort must not be null");
        deleteDocumentHistoryPort = Objects.requireNonNull(deleteDocumentHistoryPort,
                "deleteDocumentHistoryPort must not be null");
        promptEditorPortFactory = Objects.requireNonNull(promptEditorPortFactory,
                "promptEditorPortFactory must not be null");
        createNewDatabasePort = Objects.requireNonNull(createNewDatabasePort,
                "createNewDatabasePort must not be null");
        schedulerControlUseCase = Objects.requireNonNullElse(schedulerControlUseCase, Optional.empty());
        configurationFileLockPort = Objects.requireNonNullElse(configurationFileLockPort, Optional.empty());
        applicationContextInitializer = applicationContextInitializer == null
                ? GuiApplicationContextInitializer.noOp() : applicationContextInitializer;
        modelPriceManagementPort = Objects.requireNonNullElse(modelPriceManagementPort, Optional.empty());
    }

    /**
     * Backward-compatible constructor that fills {@code schedulerControlUseCase} with
     * {@link Optional#empty()}.
     * <p>
     * Preserves existing callers that were written before the scheduler was added.
     *
     * @param initialState                  initial editor state; must not be {@code null}
     * @param startupNotice                 optional startup notice; {@code null} becomes empty
     * @param configurationFileLoader       file-loading callback; must not be {@code null}
     * @param configurationFileWriter       file-writing callback; must not be {@code null}
     * @param modelCatalogPort              port for retrieving AI model lists; must not be {@code null}
     * @param apiKeyResolutionPort          port for resolving API key provenance; must not be {@code null}
     * @param providerTechnicalTestService  service for provider-specific technical checks; must not be {@code null}
     * @param pathCheckPort                 port for filesystem path accessibility checks; must not be {@code null}
     * @param technicalTestOrchestrator     orchestrator for the full technical test run; must not be {@code null}
     * @param correctionExecutionService    service for executing confirmed corrective actions; must not be {@code null}
     * @param batchRunLauncher              bridge that executes a regular batch run; must not be {@code null}
     * @param miniRunLauncher               bridge that executes a targeted mini-run; must not be {@code null}
     * @param resetDocumentStatusPort       bridge that resets document status; must not be {@code null}
     * @param manualFileRenamePort          bridge that renames a target file; must not be {@code null}
     * @param manualFileCopyPort            bridge that copies a source file; must not be {@code null}
     * @param historicalDocumentContextPort bridge for historical processing context; must not be {@code null}
     * @param applicationVersion            resolved application version string; {@code null} defaults to {@code "dev"}
     * @param promptEditorPort              bridge zum Prompt-Editor-Use-Case; must not be {@code null}
     * @param historyOverviewPort           bridge for history overview; must not be {@code null}
     * @param historyDetailsPort            bridge for history details; must not be {@code null}
     * @param historyResetDocumentStatusPort bridge for history reset; must not be {@code null}
     * @param deleteDocumentHistoryPort     bridge for history deletion; must not be {@code null}
     * @param promptEditorPortFactory       factory for prompt editor ports; must not be {@code null}
     * @param createNewDatabasePort         bridge for new database creation; must not be {@code null}
     * @param applicationContextError       optional error from context init; {@code null} becomes empty
     */
    public GuiStartupContext(
            GuiConfigurationEditorState initialState,
            Optional<String> startupNotice,
            GuiConfigurationFileLoader configurationFileLoader,
            GuiConfigurationFileWriter configurationFileWriter,
            AiModelCatalogPort modelCatalogPort,
            ApiKeyResolutionPort apiKeyResolutionPort,
            ProviderTechnicalTestService providerTechnicalTestService,
            PathCheckPort pathCheckPort,
            TechnicalTestOrchestrator technicalTestOrchestrator,
            CorrectionExecutionService correctionExecutionService,
            GuiBatchRunLauncher batchRunLauncher,
            GuiMiniRunLauncher miniRunLauncher,
            GuiResetDocumentStatusPort resetDocumentStatusPort,
            GuiManualFileRenamePort manualFileRenamePort,
            GuiManualFileCopyPort manualFileCopyPort,
            GuiHistoricalDocumentContextPort historicalDocumentContextPort,
            String applicationVersion,
            GuiPromptEditorPort promptEditorPort,
            GuiHistoryOverviewPort historyOverviewPort,
            GuiHistoryDetailsPort historyDetailsPort,
            GuiHistoryResetDocumentStatusPort historyResetDocumentStatusPort,
            GuiDeleteDocumentHistoryPort deleteDocumentHistoryPort,
            GuiPromptEditorPortFactory promptEditorPortFactory,
            GuiCreateNewDatabasePort createNewDatabasePort,
            Optional<String> applicationContextError) {
        this(initialState, startupNotice, configurationFileLoader, configurationFileWriter,
                modelCatalogPort, apiKeyResolutionPort, providerTechnicalTestService, pathCheckPort,
                technicalTestOrchestrator, correctionExecutionService, batchRunLauncher,
                miniRunLauncher, resetDocumentStatusPort, manualFileRenamePort, manualFileCopyPort,
                historicalDocumentContextPort, applicationVersion, promptEditorPort,
                historyOverviewPort, historyDetailsPort, historyResetDocumentStatusPort,
                deleteDocumentHistoryPort, promptEditorPortFactory, createNewDatabasePort,
                applicationContextError, Optional.empty(), Optional.empty(),
                GuiApplicationContextInitializer.noOp(), Optional.empty());
    }

    /**
     * Backward-compatible constructor that fills {@code configurationFileLockPort} with
     * {@link Optional#empty()}.
     * <p>
     * Preserves existing callers that were written before the configuration file lock port
     * was added.
     *
     * @param initialState                  initial editor state; must not be {@code null}
     * @param startupNotice                 optional startup notice; {@code null} becomes empty
     * @param configurationFileLoader       file-loading callback; must not be {@code null}
     * @param configurationFileWriter       file-writing callback; must not be {@code null}
     * @param modelCatalogPort              port for retrieving AI model lists; must not be {@code null}
     * @param apiKeyResolutionPort          port for resolving API key provenance; must not be {@code null}
     * @param providerTechnicalTestService  service for provider-specific technical checks; must not be {@code null}
     * @param pathCheckPort                 port for filesystem path accessibility checks; must not be {@code null}
     * @param technicalTestOrchestrator     orchestrator for the full technical test run; must not be {@code null}
     * @param correctionExecutionService    service for executing confirmed corrective actions; must not be {@code null}
     * @param batchRunLauncher              bridge that executes a regular batch run; must not be {@code null}
     * @param miniRunLauncher               bridge that executes a targeted mini-run; must not be {@code null}
     * @param resetDocumentStatusPort       bridge that resets document status; must not be {@code null}
     * @param manualFileRenamePort          bridge that renames a target file; must not be {@code null}
     * @param manualFileCopyPort            bridge that copies a source file; must not be {@code null}
     * @param historicalDocumentContextPort bridge for historical processing context; must not be {@code null}
     * @param applicationVersion            resolved application version string; {@code null} defaults to {@code "dev"}
     * @param promptEditorPort              bridge zum Prompt-Editor-Use-Case; must not be {@code null}
     * @param historyOverviewPort           bridge for history overview; must not be {@code null}
     * @param historyDetailsPort            bridge for history details; must not be {@code null}
     * @param historyResetDocumentStatusPort bridge for history reset; must not be {@code null}
     * @param deleteDocumentHistoryPort     bridge for history deletion; must not be {@code null}
     * @param promptEditorPortFactory       factory for prompt editor ports; must not be {@code null}
     * @param createNewDatabasePort         bridge for new database creation; must not be {@code null}
     * @param applicationContextError       optional error from context init; {@code null} becomes empty
     * @param schedulerControlUseCase       optional scheduler control use case; {@code null} becomes empty
     */
    public GuiStartupContext(
            GuiConfigurationEditorState initialState,
            Optional<String> startupNotice,
            GuiConfigurationFileLoader configurationFileLoader,
            GuiConfigurationFileWriter configurationFileWriter,
            AiModelCatalogPort modelCatalogPort,
            ApiKeyResolutionPort apiKeyResolutionPort,
            ProviderTechnicalTestService providerTechnicalTestService,
            PathCheckPort pathCheckPort,
            TechnicalTestOrchestrator technicalTestOrchestrator,
            CorrectionExecutionService correctionExecutionService,
            GuiBatchRunLauncher batchRunLauncher,
            GuiMiniRunLauncher miniRunLauncher,
            GuiResetDocumentStatusPort resetDocumentStatusPort,
            GuiManualFileRenamePort manualFileRenamePort,
            GuiManualFileCopyPort manualFileCopyPort,
            GuiHistoricalDocumentContextPort historicalDocumentContextPort,
            String applicationVersion,
            GuiPromptEditorPort promptEditorPort,
            GuiHistoryOverviewPort historyOverviewPort,
            GuiHistoryDetailsPort historyDetailsPort,
            GuiHistoryResetDocumentStatusPort historyResetDocumentStatusPort,
            GuiDeleteDocumentHistoryPort deleteDocumentHistoryPort,
            GuiPromptEditorPortFactory promptEditorPortFactory,
            GuiCreateNewDatabasePort createNewDatabasePort,
            Optional<String> applicationContextError,
            Optional<SchedulerControlUseCase> schedulerControlUseCase) {
        this(initialState, startupNotice, configurationFileLoader, configurationFileWriter,
                modelCatalogPort, apiKeyResolutionPort, providerTechnicalTestService, pathCheckPort,
                technicalTestOrchestrator, correctionExecutionService, batchRunLauncher,
                miniRunLauncher, resetDocumentStatusPort, manualFileRenamePort, manualFileCopyPort,
                historicalDocumentContextPort, applicationVersion, promptEditorPort,
                historyOverviewPort, historyDetailsPort, historyResetDocumentStatusPort,
                deleteDocumentHistoryPort, promptEditorPortFactory, createNewDatabasePort,
                applicationContextError, schedulerControlUseCase, Optional.empty(),
                GuiApplicationContextInitializer.noOp(), Optional.empty());
    }

    /**
     * Backward-compatible constructor that fills the manual-rename port with a no-op
     * implementation.
     *
     * @param initialState                initial editor state; must not be {@code null}
     * @param startupNotice               optional startup notice; {@code null} becomes empty
     * @param configurationFileLoader     file-loading callback; must not be {@code null}
     * @param configurationFileWriter     file-writing callback; must not be {@code null}
     * @param modelCatalogPort            port for retrieving AI model lists; must not be {@code null}
     * @param apiKeyResolutionPort        port for resolving API key provenance; must not be {@code null}
     * @param providerTechnicalTestService service for provider-specific technical checks; must not be {@code null}
     * @param pathCheckPort               port for filesystem path accessibility checks; must not be {@code null}
     * @param technicalTestOrchestrator   orchestrator for the full technical test run; must not be {@code null}
     * @param correctionExecutionService  service for executing confirmed corrective actions; must not be {@code null}
     * @param batchRunLauncher            bridge that executes a regular batch run; must not be {@code null}
     * @param miniRunLauncher             bridge that executes a targeted mini-run for selected
     *                                    documents; must not be {@code null}
     * @param resetDocumentStatusPort     bridge that resets the persistence status of selected
     *                                    documents; must not be {@code null}
     */
    public GuiStartupContext(
            GuiConfigurationEditorState initialState,
            Optional<String> startupNotice,
            GuiConfigurationFileLoader configurationFileLoader,
            GuiConfigurationFileWriter configurationFileWriter,
            AiModelCatalogPort modelCatalogPort,
            ApiKeyResolutionPort apiKeyResolutionPort,
            ProviderTechnicalTestService providerTechnicalTestService,
            PathCheckPort pathCheckPort,
            TechnicalTestOrchestrator technicalTestOrchestrator,
            CorrectionExecutionService correctionExecutionService,
            GuiBatchRunLauncher batchRunLauncher,
            GuiMiniRunLauncher miniRunLauncher,
            GuiResetDocumentStatusPort resetDocumentStatusPort) {
        this(initialState, startupNotice, configurationFileLoader, configurationFileWriter,
                modelCatalogPort, apiKeyResolutionPort, providerTechnicalTestService, pathCheckPort,
                technicalTestOrchestrator, correctionExecutionService, batchRunLauncher,
                miniRunLauncher, resetDocumentStatusPort, rejectingManualFileRenamePort(),
                rejectingManualFileCopyPort(),
                noOpHistoricalDocumentContextPort(), "dev", noOpPromptEditorPort(),
                noOpHistoryOverviewPort(), noOpHistoryDetailsPort(),
                noOpHistoryResetPort(), noOpDeleteHistoryPort(), noOpPromptEditorPortFactory(),
                rejectingCreateNewDatabasePort(), Optional.empty());
    }

    /**
     * Backward-compatible constructor that fills the mini-run launcher, reset port and
     * manual-rename port with no-op implementations.
     *
     * @param initialState                initial editor state; must not be {@code null}
     * @param startupNotice               optional startup notice; {@code null} becomes empty
     * @param configurationFileLoader     file-loading callback; must not be {@code null}
     * @param configurationFileWriter     file-writing callback; must not be {@code null}
     * @param modelCatalogPort            port for retrieving AI model lists; must not be {@code null}
     * @param apiKeyResolutionPort        port for resolving API key provenance; must not be {@code null}
     * @param providerTechnicalTestService service for provider-specific technical checks; must not be {@code null}
     * @param pathCheckPort               port for filesystem path accessibility checks; must not be {@code null}
     * @param technicalTestOrchestrator   orchestrator for the full technical test run; must not be {@code null}
     * @param correctionExecutionService  service for executing confirmed corrective actions; must not be {@code null}
     * @param batchRunLauncher            bridge that executes a regular batch run; must not be {@code null}
     */
    public GuiStartupContext(
            GuiConfigurationEditorState initialState,
            Optional<String> startupNotice,
            GuiConfigurationFileLoader configurationFileLoader,
            GuiConfigurationFileWriter configurationFileWriter,
            AiModelCatalogPort modelCatalogPort,
            ApiKeyResolutionPort apiKeyResolutionPort,
            ProviderTechnicalTestService providerTechnicalTestService,
            PathCheckPort pathCheckPort,
            TechnicalTestOrchestrator technicalTestOrchestrator,
            CorrectionExecutionService correctionExecutionService,
            GuiBatchRunLauncher batchRunLauncher) {
        this(initialState, startupNotice, configurationFileLoader, configurationFileWriter,
                modelCatalogPort, apiKeyResolutionPort, providerTechnicalTestService, pathCheckPort,
                technicalTestOrchestrator, correctionExecutionService, batchRunLauncher,
                rejectingMiniRunLauncher(), rejectingResetPort(), rejectingManualFileRenamePort(),
                rejectingManualFileCopyPort(),
                noOpHistoricalDocumentContextPort(), "dev", noOpPromptEditorPort(),
                noOpHistoryOverviewPort(), noOpHistoryDetailsPort(),
                noOpHistoryResetPort(), noOpDeleteHistoryPort(), noOpPromptEditorPortFactory(),
                rejectingCreateNewDatabasePort(), Optional.empty());
    }

    /**
     * Backward-compatible constructor that fills the processing-run launcher, mini-run
     * launcher, reset port and manual-rename port with no-op implementations.
     * <p>
     * Preserves existing callers that were written before the processing-run tab was added.
     *
     * @param initialState                initial editor state; must not be {@code null}
     * @param startupNotice               optional startup notice; {@code null} becomes empty
     * @param configurationFileLoader     file-loading callback; must not be {@code null}
     * @param configurationFileWriter     file-writing callback; must not be {@code null}
     * @param modelCatalogPort            port for retrieving AI model lists; must not be {@code null}
     * @param apiKeyResolutionPort        port for resolving API key provenance; must not be {@code null}
     * @param providerTechnicalTestService service for provider-specific technical checks; must not be {@code null}
     * @param pathCheckPort               port for filesystem path accessibility checks; must not be {@code null}
     * @param technicalTestOrchestrator   orchestrator for the full technical test run; must not be {@code null}
     * @param correctionExecutionService  service for executing confirmed corrective actions; must not be {@code null}
     */
    public GuiStartupContext(
            GuiConfigurationEditorState initialState,
            Optional<String> startupNotice,
            GuiConfigurationFileLoader configurationFileLoader,
            GuiConfigurationFileWriter configurationFileWriter,
            AiModelCatalogPort modelCatalogPort,
            ApiKeyResolutionPort apiKeyResolutionPort,
            ProviderTechnicalTestService providerTechnicalTestService,
            PathCheckPort pathCheckPort,
            TechnicalTestOrchestrator technicalTestOrchestrator,
            CorrectionExecutionService correctionExecutionService) {
        this(initialState, startupNotice, configurationFileLoader, configurationFileWriter,
                modelCatalogPort, apiKeyResolutionPort, providerTechnicalTestService, pathCheckPort,
                technicalTestOrchestrator, correctionExecutionService,
                rejectingBatchRunLauncher(), rejectingMiniRunLauncher(), rejectingResetPort(),
                rejectingManualFileRenamePort(), rejectingManualFileCopyPort(),
                noOpHistoricalDocumentContextPort(), "dev", noOpPromptEditorPort(),
                noOpHistoryOverviewPort(), noOpHistoryDetailsPort(),
                noOpHistoryResetPort(), noOpDeleteHistoryPort(), noOpPromptEditorPortFactory(),
                rejectingCreateNewDatabasePort(), Optional.empty());
    }

    private static GuiBatchRunLauncher rejectingBatchRunLauncher() {
        return (configPath, observer, token) -> GuiBatchRunLaunchOutcome.rejected(
                "Kein Verarbeitungslauf-Launcher in diesem Startkontext verfügbar.");
    }

    private static GuiMiniRunLauncher rejectingMiniRunLauncher() {
        return (configPath, filter, observer, token) -> GuiBatchRunLaunchOutcome.rejected(
                "Kein Mini-Run-Launcher in diesem Startkontext verfügbar.");
    }

    private static GuiResetDocumentStatusPort rejectingResetPort() {
        return (configPath, fingerprints) -> {
            java.util.Map<DocumentFingerprint, String> failures = new java.util.HashMap<>();
            for (DocumentFingerprint fp : fingerprints) {
                failures.put(fp, "Kein Reset-Port in diesem Startkontext verfügbar.");
            }
            return new ResetDocumentStatusResult(fingerprints.size(), Set.of(), failures);
        };
    }

    private static GuiManualFileRenamePort rejectingManualFileRenamePort() {
        return (configPath, request) -> new de.gecheckt.pdf.umbenenner.application.port.in
                .ManualFileRenameFileSystemFailure(
                        "Kein Umbenennungs-Port in diesem Startkontext verfügbar.");
    }

    private static GuiManualFileCopyPort rejectingManualFileCopyPort() {
        return (configPath, request) -> new de.gecheckt.pdf.umbenenner.application.port.in
                .ManualFileCopyFileSystemFailure(
                        "Kein Kopier-Port in diesem Startkontext verfügbar.");
    }

    private static GuiHistoricalDocumentContextPort noOpHistoricalDocumentContextPort() {
        return (configPath, fingerprint) -> java.util.Optional.empty();
    }

    /**
     * Creates a blank startup context with no-op implementations for all ports and services.
     * <p>
     * This is safe for environments where no Bootstrap wiring is present, such as isolated
     * GUI tests.
     *
     * @param startupNotice optional startup notice; {@code null} becomes empty
     * @return a startup context for the unloaded editor start
     */
    public static GuiStartupContext blank(Optional<String> startupNotice) {
        de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog.AiModelCatalogPort noOpCatalogPort =
                request -> new de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog
                        .ModelCatalogResult.IncompleteConfiguration(
                                request.providerIdentifier(),
                                "Kein Modellkatalog in diesem Startkontext verfügbar.");
        ApiKeyResolutionPort noOpApiKeyPort = (family, propertyValue) -> EffectiveApiKeyDescriptor.absent();
        ProviderTechnicalTestService noOpTestService =
                new ProviderTechnicalTestService(noOpCatalogPort, noOpApiKeyPort);
        PathCheckPort noOpPathCheckPort = new PathCheckPort() {
            @Override
            public boolean isDirectoryReadable(String path) { return false; }
            @Override
            public boolean isDirectoryWritableOrCreatable(String path) { return false; }
            @Override
            public boolean isFileReadable(String path) { return false; }
            @Override
            public boolean isSqlitePathUsable(String path) { return false; }
        };
        TechnicalTestOrchestrator noOpOrchestrator = new TechnicalTestOrchestrator(
                new de.gecheckt.pdf.umbenenner.application.validation.editor.EditorConfigurationValidator(),
                noOpPathCheckPort,
                noOpTestService,
                () -> java.util.Optional.empty());
        ResourceCreationPort noOpResourceCreationPort = new ResourceCreationPort() {
            @Override
            public de.gecheckt.pdf.umbenenner.application.validation.technicaltest.CorrectionOutcome
                    createDirectory(de.gecheckt.pdf.umbenenner.application.validation.technicaltest
                            .CorrectionSuggestion.CreateDirectory suggestion) {
                return new de.gecheckt.pdf.umbenenner.application.validation.technicaltest
                        .CorrectionOutcome.NotAttempted(suggestion, NO_PORT_MSG);
            }
            @Override
            public de.gecheckt.pdf.umbenenner.application.validation.technicaltest.CorrectionOutcome
                    createPromptFile(de.gecheckt.pdf.umbenenner.application.validation.technicaltest
                            .CorrectionSuggestion.CreatePromptFile suggestion) {
                return new de.gecheckt.pdf.umbenenner.application.validation.technicaltest
                        .CorrectionOutcome.NotAttempted(suggestion, NO_PORT_MSG);
            }
            @Override
            public de.gecheckt.pdf.umbenenner.application.validation.technicaltest.CorrectionOutcome
                    prepareSqlitePath(de.gecheckt.pdf.umbenenner.application.validation.technicaltest
                            .CorrectionSuggestion.PrepareSqlitePath suggestion) {
                return new de.gecheckt.pdf.umbenenner.application.validation.technicaltest
                        .CorrectionOutcome.NotAttempted(suggestion, NO_PORT_MSG);
            }
        };
        CorrectionExecutionService noOpCorrectionService = new CorrectionExecutionService(noOpResourceCreationPort);
        GuiBatchRunLauncher noOpBatchRunLauncher = (configPath, observer, token) ->
                GuiBatchRunLaunchOutcome.rejected(
                        "Kein Verarbeitungslauf-Launcher in diesem Startkontext verfügbar.");
        return new GuiStartupContext(
                GuiConfigurationEditorStateFactory.createBlankStartState(),
                startupNotice,
                configFilePath -> GuiConfigurationEditorStateFactory.createBlankStartState(),
                (values, path) -> GuiConfigurationSaveResult.saved(path),
                noOpCatalogPort,
                noOpApiKeyPort,
                noOpTestService,
                noOpPathCheckPort,
                noOpOrchestrator,
                noOpCorrectionService,
                noOpBatchRunLauncher,
                rejectingMiniRunLauncher(),
                rejectingResetPort(),
                rejectingManualFileRenamePort(),
                rejectingManualFileCopyPort(),
                noOpHistoricalDocumentContextPort(),
                "dev",
                noOpPromptEditorPort(),
                noOpHistoryOverviewPort(),
                noOpHistoryDetailsPort(),
                noOpHistoryResetPort(),
                noOpDeleteHistoryPort(),
                noOpPromptEditorPortFactory(),
                rejectingCreateNewDatabasePort(),
                Optional.empty());
    }

    /**
     * Liefert einen ablehnenden {@link GuiCreateNewDatabasePort}, der jede Anlage
     * sofort als Fehler zurückgibt. Wird verwendet, wenn kein Bootstrap-seitig
     * verdrahteter Port vorliegt (z. B. in Tests oder vor dem Laden einer
     * Konfiguration).
     *
     * @return ein ablehnender Port; nie {@code null}
     */
    private static GuiCreateNewDatabasePort rejectingCreateNewDatabasePort() {
        return (configFilePath, targetFile) -> new de.gecheckt.pdf.umbenenner.application.port.in
                .CreateNewDatabaseUseCase.CreateNewDatabaseResult.CreationFailed(
                        de.gecheckt.pdf.umbenenner.application.port.in
                                .CreateNewDatabaseUseCase.CreateNewDatabaseResult.Phase.PATH_RESOLUTION,
                        "Kein DB-Anlage-Port in diesem Startkontext verfügbar.",
                        null);
    }

    private static GuiPromptEditorPortFactory noOpPromptEditorPortFactory() {
        return path -> noOpPromptEditorPort();
    }

    private static GuiPromptEditorPort noOpPromptEditorPort() {
        return new GuiPromptEditorPort() {
            @Override
            public de.gecheckt.pdf.umbenenner.application.port.out.PromptLoadingResult loadCurrentPrompt() {
                return new de.gecheckt.pdf.umbenenner.application.port.out.PromptLoadingFailure(
                        "NO_OP", NO_PROMPT_PORT_MSG);
            }

            @Override
            public de.gecheckt.pdf.umbenenner.application.port.out.PromptSaveResult save(String content) {
                return new de.gecheckt.pdf.umbenenner.application.port.out.PromptSaveResult.WriteFailed(
                        NO_PROMPT_PORT_MSG, null);
            }

            @Override
            public de.gecheckt.pdf.umbenenner.application.validation.technicaltest.CorrectionOutcome
                    createDefaultPromptIfMissing(
                            de.gecheckt.pdf.umbenenner.application.validation.technicaltest
                                    .CorrectionSuggestion.CreatePromptFile suggestion) {
                return new de.gecheckt.pdf.umbenenner.application.validation.technicaltest
                        .CorrectionOutcome.NotAttempted(
                                suggestion, NO_PROMPT_PORT_MSG);
            }
        };
    }

    private static de.gecheckt.pdf.umbenenner.adapter.in.gui.history.GuiHistoryOverviewPort
            noOpHistoryOverviewPort() {
        return (configFilePath, query) -> new de.gecheckt.pdf.umbenenner.application.usecase
                .DefaultHistoryOverviewUseCase.HistoryOverviewResult(java.util.List.of(), false);
    }

    private static de.gecheckt.pdf.umbenenner.adapter.in.gui.history.GuiHistoryDetailsPort
            noOpHistoryDetailsPort() {
        return (configFilePath, fingerprint) -> java.util.Optional.empty();
    }

    private static de.gecheckt.pdf.umbenenner.adapter.in.gui.history.GuiHistoryResetDocumentStatusPort
            noOpHistoryResetPort() {
        return (configFilePath, fingerprint) -> { /* kein Reset in diesem Startkontext */ };
    }

    private static de.gecheckt.pdf.umbenenner.adapter.in.gui.history.GuiDeleteDocumentHistoryPort
            noOpDeleteHistoryPort() {
        return (configFilePath, fingerprint) -> { /* kein Löschen in diesem Startkontext */ };
    }
}
