package de.gecheckt.pdf.umbenenner.application.port.out.modelcatalog;

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

/**
 * Sealed result type for a model catalogue retrieval operation performed via
 * {@link AiModelCatalogPort}.
 * <p>
 * Each permitted sub-type represents one distinct outcome:
 * <ul>
 *   <li>{@link Success} – the provider returned a non-empty list of model identifiers.</li>
 *   <li>{@link EmptyList} – the provider responded successfully but returned no models.</li>
 *   <li>{@link IncompleteConfiguration} – the request could not be sent because a required
 *       configuration value (e.g., API key, base URL) was missing.</li>
 *   <li>{@link TechnicalFailure} – the HTTP call, authentication, or response parsing failed.</li>
 * </ul>
 * <p>
 * The GUI adapter uses this result directly; no separate GUI-layer translation type is needed
 * because the structure matches the GUI's display needs without containing any JavaFX reference.
 * This design decision is documented here to avoid introducing a redundant mapping layer.
 * <p>
 * Callers are expected to use pattern-matching {@code switch} expressions over all permitted types
 * to ensure exhaustive handling as new sub-types might be added in future expansions.
 */
public sealed interface ModelCatalogResult
        permits ModelCatalogResult.Success,
                ModelCatalogResult.EmptyList,
                ModelCatalogResult.IncompleteConfiguration,
                ModelCatalogResult.TechnicalFailure {
    String PROVIDER_ID_NOT_NULL = "providerIdentifier must not be null";


    /**
     * The provider returned a non-empty list of available model identifiers.
     * <p>
     * The list is guaranteed to contain at least one entry. Callers may safely use
     * the first element as a default selection.
     *
     * @param providerIdentifier identifier of the provider that returned the list; never {@code null}
     * @param models             non-empty, ordered list of model identifier strings; never {@code null}
     * @param loadedAt           timestamp when the list was successfully retrieved; never {@code null}
     */
    record Success(
            String providerIdentifier,
            List<String> models,
            Instant loadedAt) implements ModelCatalogResult {

        /**
         * Creates a successful model catalogue result.
         *
         * @param providerIdentifier identifier of the provider; must not be {@code null}
         * @param models             list of model identifiers; must not be {@code null} or empty
         * @param loadedAt           retrieval timestamp; must not be {@code null}
         * @throws NullPointerException     if any parameter is {@code null}
         * @throws IllegalArgumentException if {@code models} is empty
         */
        public Success {
            Objects.requireNonNull(providerIdentifier, PROVIDER_ID_NOT_NULL);
            Objects.requireNonNull(models, "models must not be null");
            Objects.requireNonNull(loadedAt, "loadedAt must not be null");
            if (models.isEmpty()) {
                throw new IllegalArgumentException(
                        "models must not be empty; use EmptyList for an empty response");
            }
            models = List.copyOf(models);
        }
    }

    /**
     * The provider responded successfully but returned no model identifiers.
     * <p>
     * This case is distinct from {@link TechnicalFailure}: the HTTP exchange succeeded and the
     * response was parseable, but the list of models was empty. The GUI should fall back to
     * manual text input.
     *
     * @param providerIdentifier identifier of the provider; never {@code null}
     * @param loadedAt           timestamp of the (technically successful) response; never {@code null}
     */
    record EmptyList(
            String providerIdentifier,
            Instant loadedAt) implements ModelCatalogResult {

        /**
         * Creates an empty-list result.
         *
         * @param providerIdentifier identifier of the provider; must not be {@code null}
         * @param loadedAt           retrieval timestamp; must not be {@code null}
         * @throws NullPointerException if any parameter is {@code null}
         */
        public EmptyList {
            Objects.requireNonNull(providerIdentifier, PROVIDER_ID_NOT_NULL);
            Objects.requireNonNull(loadedAt, "loadedAt must not be null");
        }
    }

    /**
     * The model catalogue request could not be sent because a required configuration value
     * was absent.
     * <p>
     * Typical causes: missing API key, missing base URL for a provider family that requires one.
     * The adapter must not throw an exception for this case; it must return this result type
     * instead so the GUI can display a user-friendly hint without crashing.
     *
     * @param providerIdentifier identifier of the provider for which configuration is incomplete;
     *                           never {@code null}
     * @param missingReason      human-readable description of which configuration value is missing;
     *                           never {@code null}
     */
    record IncompleteConfiguration(
            String providerIdentifier,
            String missingReason) implements ModelCatalogResult {

        /**
         * Creates an incomplete-configuration result.
         *
         * @param providerIdentifier identifier of the provider; must not be {@code null}
         * @param missingReason      description of the missing value; must not be {@code null}
         * @throws NullPointerException if any parameter is {@code null}
         */
        public IncompleteConfiguration {
            Objects.requireNonNull(providerIdentifier, PROVIDER_ID_NOT_NULL);
            Objects.requireNonNull(missingReason, "missingReason must not be null");
        }
    }

    /**
     * A technical error occurred during the model catalogue retrieval.
     * <p>
     * Covers HTTP errors, authentication failures, network timeouts, and response parsing
     * failures. The adapter classifies the error into a short category string (e.g.,
     * {@code "HTTP_ERROR"}, {@code "AUTH_FAILURE"}, {@code "TIMEOUT"}, {@code "PARSE_ERROR"})
     * and provides a human-readable detail message.
     * <p>
     * This result does not trigger a retry; the GUI offers an explicit
     * "reload models" action that the user can invoke after fixing the underlying issue.
     *
     * @param providerIdentifier identifier of the provider that was contacted; never {@code null}
     * @param errorCategory      short, stable category string for programmatic discrimination;
     *                           never {@code null}
     * @param errorDetail        human-readable error description; never {@code null}
     */
    record TechnicalFailure(
            String providerIdentifier,
            String errorCategory,
            String errorDetail) implements ModelCatalogResult {

        /**
         * Creates a technical-failure result.
         *
         * @param providerIdentifier identifier of the provider; must not be {@code null}
         * @param errorCategory      short category string; must not be {@code null}
         * @param errorDetail        human-readable detail; must not be {@code null}
         * @throws NullPointerException if any parameter is {@code null}
         */
        public TechnicalFailure {
            Objects.requireNonNull(providerIdentifier, PROVIDER_ID_NOT_NULL);
            Objects.requireNonNull(errorCategory, "errorCategory must not be null");
            Objects.requireNonNull(errorDetail, "errorDetail must not be null");
        }
    }
}
