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

import java.util.Objects;

import de.gecheckt.pdf.umbenenner.application.config.provider.AiProviderFamily;

/**
 * Represents which provider section is currently visible in the GUI and preserves
 * the editable configuration state of the section that is currently hidden.
 * <p>
 * The GUI shows exactly one provider section at a time. When the user switches the
 * active provider, the previously visible section must not lose its field values.
 * This record captures the current display context as an immutable snapshot so that
 * view-model code can reason about visibility and data preservation without touching
 * JavaFX nodes directly.
 * <p>
 * Instances of this record contain no JavaFX references and are safe to create and
 * inspect from any thread, including unit-test threads.
 *
 * @param visibleProvider          the provider family whose configuration section is
 *                                 currently rendered; never {@code null}
 * @param visibleProviderState     the editable configuration state currently displayed;
 *                                 never {@code null}
 * @param hiddenProviderState      the editable configuration state of the provider that
 *                                 is not shown, preserved here so it is not lost on switch;
 *                                 never {@code null}
 * @param hiddenProvider           the provider family whose section is currently hidden;
 *                                 never {@code null}
 */
public record GuiVisibleProviderSection(
        AiProviderFamily visibleProvider,
        GuiProviderConfigurationState visibleProviderState,
        AiProviderFamily hiddenProvider,
        GuiProviderConfigurationState hiddenProviderState) {

    /**
     * Creates a new visible-provider section snapshot.
     *
     * @param visibleProvider      provider whose section is shown; must not be {@code null}
     * @param visibleProviderState configuration state of the visible provider; must not be {@code null}
     * @param hiddenProvider       provider whose section is hidden; must not be {@code null}
     * @param hiddenProviderState  configuration state of the hidden provider; must not be {@code null}
     * @throws NullPointerException     if any parameter is {@code null}
     * @throws IllegalArgumentException if {@code visibleProvider} and {@code hiddenProvider} are equal
     */
    public GuiVisibleProviderSection {
        Objects.requireNonNull(visibleProvider, "visibleProvider must not be null");
        Objects.requireNonNull(visibleProviderState, "visibleProviderState must not be null");
        Objects.requireNonNull(hiddenProvider, "hiddenProvider must not be null");
        Objects.requireNonNull(hiddenProviderState, "hiddenProviderState must not be null");
        if (visibleProvider == hiddenProvider) {
            throw new IllegalArgumentException(
                    "visibleProvider and hiddenProvider must be different, but both are: " + visibleProvider);
        }
    }

    /**
     * Returns the configuration state for the requested provider family.
     *
     * @param family the provider family to retrieve the state for; must not be {@code null}
     * @return the state for the requested provider
     * @throws IllegalArgumentException if the requested family is neither the visible nor the hidden provider
     */
    public GuiProviderConfigurationState stateFor(AiProviderFamily family) {
        Objects.requireNonNull(family, "family must not be null");
        if (family == visibleProvider) {
            return visibleProviderState;
        }
        if (family == hiddenProvider) {
            return hiddenProviderState;
        }
        throw new IllegalArgumentException("Unknown provider family: " + family);
    }

    /**
     * Returns a copy with the visible and hidden providers swapped, preserving both states.
     * <p>
     * The previously hidden provider becomes visible and the previously visible provider
     * moves to hidden. No field values are lost during the switch.
     *
     * @return a new section snapshot with providers and their states swapped
     */
    public GuiVisibleProviderSection switchProvider() {
        return new GuiVisibleProviderSection(hiddenProvider, hiddenProviderState,
                visibleProvider, visibleProviderState);
    }

    /**
     * Returns a copy with a different configuration state for the visible provider.
     *
     * @param newState the updated configuration state; must not be {@code null}
     * @return a new section snapshot with the visible provider's state replaced
     */
    public GuiVisibleProviderSection withVisibleProviderState(GuiProviderConfigurationState newState) {
        Objects.requireNonNull(newState, "newState must not be null");
        return new GuiVisibleProviderSection(visibleProvider, newState, hiddenProvider, hiddenProviderState);
    }
}
