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

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

/**
 * Root editor state for the GUI configuration editor.
 * <p>
 * The state keeps the loaded file snapshot, the baseline values and the current editable values
 * separate so later GUI layers can compare the current editor content against the original
 * representation without relying on a manually maintained dirty flag.
 *
 * @param loadedFileSnapshot loaded file representation, or empty when the editor was created
 *                           from the standard template
 * @param baselineValues values representing the baseline the editor compares against
 * @param values current editable configuration values
 */
public record GuiConfigurationEditorState(
        Optional<GuiConfigurationFileSnapshot> loadedFileSnapshot,
        GuiConfigurationValues baselineValues,
        GuiConfigurationValues values,
        Optional<String> pendingMigrationMessage) {

    /**
     * Creates a new editor state.
     *
     * @param loadedFileSnapshot loaded file representation; {@code null} becomes empty
     * @param baselineValues baseline values; must not be {@code null}
     * @param values current editable configuration values; must not be {@code null}
     */
    public GuiConfigurationEditorState {
        loadedFileSnapshot = Objects.requireNonNullElse(loadedFileSnapshot, Optional.empty());
        baselineValues = Objects.requireNonNull(baselineValues, "baselineValues must not be null");
        values = Objects.requireNonNull(values, "values must not be null");
        pendingMigrationMessage = Objects.requireNonNullElse(pendingMigrationMessage, Optional.empty());
    }

    /**
     * Returns whether the editor currently contains unsaved changes.
     *
     * @return {@code true} when the current values differ from the baseline, otherwise {@code false}
     */
    public boolean isDirty() {
        return !baselineValues.equals(values);
    }

    /**
     * Returns the derived change state for the current editor content.
     *
     * @return {@link GuiChangeState#DIRTY} when the editor is dirty, otherwise {@link GuiChangeState#CLEAN}
     */
    public GuiChangeState changeState() {
        return isDirty() ? GuiChangeState.DIRTY : GuiChangeState.CLEAN;
    }

    /**
     * Returns whether the editor currently has a loaded file snapshot.
     *
     * @return {@code true} when the editor was loaded from disk, otherwise {@code false}
     */
    public boolean hasLoadedFileSnapshot() {
        return loadedFileSnapshot.isPresent();
    }

    /**
     * Returns whether the editor represents a newly created configuration.
     *
     * @return {@code true} when no file snapshot is loaded, otherwise {@code false}
     */
    public boolean isNewConfiguration() {
        return loadedFileSnapshot.isEmpty();
    }

    /**
     * Returns the configuration-path text shown in the GUI header.
     * <p>
     * When no file snapshot is loaded, the header must remain empty so the GUI does not
     * suggest a file is already associated with the editor state.
     *
     * @return the loaded configuration path as text, or an empty string when no file is loaded
     */
    public String configurationPathText() {
        return loadedFileSnapshot.map(snapshot -> snapshot.filePath().toString()).orElse("");
    }

    /**
     * Returns whether the editor holds a migration note for later display.
     *
     * @return {@code true} when a migration note is stored, otherwise {@code false}
     */
    public boolean hasPendingMigrationMessage() {
        return pendingMigrationMessage.isPresent();
    }

    /**
     * Returns the stored migration note text.
     *
     * @return the migration note text, or an empty string when no note is stored
     */
    public String pendingMigrationMessageText() {
        return pendingMigrationMessage.orElse("");
    }

    /**
     * Returns a copy with different current editable values.
     *
     * @param values the new editable values; must not be {@code null}
     * @return a new editor state containing the supplied values
     */
    public GuiConfigurationEditorState withValues(GuiConfigurationValues values) {
        return new GuiConfigurationEditorState(loadedFileSnapshot, baselineValues, values, pendingMigrationMessage);
    }

    /**
     * Returns a copy with a different baseline.
     *
     * @param baselineValues the new baseline values; must not be {@code null}
     * @return a new editor state using the supplied baseline
     */
    public GuiConfigurationEditorState withBaselineValues(GuiConfigurationValues baselineValues) {
        return new GuiConfigurationEditorState(loadedFileSnapshot, baselineValues, values, pendingMigrationMessage);
    }

    /**
     * Returns a copy with a loaded file snapshot.
     *
     * @param snapshot the loaded file snapshot; must not be {@code null}
     * @return a new editor state linked to the supplied snapshot
     */
    public GuiConfigurationEditorState withLoadedFileSnapshot(GuiConfigurationFileSnapshot snapshot) {
        return new GuiConfigurationEditorState(Optional.of(snapshot), baselineValues, values, pendingMigrationMessage);
    }

    /**
     * Returns a copy without any loaded file snapshot.
     *
     * @return a new editor state without a loaded file snapshot
     */
    public GuiConfigurationEditorState withoutLoadedFileSnapshot() {
        return new GuiConfigurationEditorState(Optional.empty(), baselineValues, values, pendingMigrationMessage);
    }

    /**
     * Returns a copy with a migration note that is kept for later display.
     *
     * @param migrationMessage the migration note; must not be {@code null}
     * @return a new editor state carrying the supplied migration note
     */
    public GuiConfigurationEditorState withPendingMigrationMessage(String migrationMessage) {
        return new GuiConfigurationEditorState(loadedFileSnapshot, baselineValues, values,
                Optional.of(migrationMessage));
    }

    /**
     * Returns a clean copy of this editor state by resetting the current values to the baseline.
     *
     * @return a clean editor state
     */
    public GuiConfigurationEditorState markClean() {
        return baselineValues.equals(values)
                ? this
                : new GuiConfigurationEditorState(loadedFileSnapshot, baselineValues, baselineValues,
                        pendingMigrationMessage);
    }
}
