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

import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Objects;

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

/**
 * Editable GUI values for the complete configuration document.
 * <p>
 * This object combines all known general configuration values and the provider-specific blocks
 * in a single model that can be rendered and edited by the GUI.
 *
 * @param sourceFolder source-folder path as editable text
 * @param targetFolder target-folder path as editable text
 * @param sqliteFile SQLite file path as editable text
 * @param promptTemplateFile prompt-template path as editable text
 * @param runtimeLockFile optional runtime lock file path as editable text
 * @param logDirectory optional log directory as editable text
 * @param logLevel log level as editable text
 * @param maxRetriesTransient transient retry limit as editable text
 * @param maxPages page limit as editable text
 * @param maxTextCharacters text limit as editable text
 * @param maxTitleLength maximum base-title length as editable text
 * @param logAiSensitive raw value of {@code log.ai.sensitive} as editable text
 * @param activeProviderFamily raw value of {@code ai.provider.active} as editable text
 * @param providerConfigurations provider-specific editor state keyed by provider family
 */
public record GuiConfigurationValues(
        String sourceFolder,
        String targetFolder,
        String sqliteFile,
        String promptTemplateFile,
        String runtimeLockFile,
        String logDirectory,
        String logLevel,
        String maxRetriesTransient,
        String maxPages,
        String maxTextCharacters,
        String maxTitleLength,
        String logAiSensitive,
        String activeProviderFamily,
        Map<AiProviderFamily, GuiProviderConfigurationState> providerConfigurations) {

    /**
     * Creates a new editable configuration state.
     *
     * @param sourceFolder source-folder path; {@code null} becomes an empty string
     * @param targetFolder target-folder path; {@code null} becomes an empty string
     * @param sqliteFile SQLite file path; {@code null} becomes an empty string
     * @param promptTemplateFile prompt-template path; {@code null} becomes an empty string
     * @param runtimeLockFile runtime lock file path; {@code null} becomes an empty string
     * @param logDirectory log directory; {@code null} becomes an empty string
     * @param logLevel log level; {@code null} becomes an empty string
     * @param maxRetriesTransient transient retry limit; {@code null} becomes an empty string
     * @param maxPages page limit; {@code null} becomes an empty string
     * @param maxTextCharacters text limit; {@code null} becomes an empty string
     * @param maxTitleLength maximum base-title length; {@code null} becomes an empty string
     * @param logAiSensitive raw {@code log.ai.sensitive} value; {@code null} becomes an empty string
     * @param activeProviderFamily raw {@code ai.provider.active} value; {@code null} becomes an empty string
     * @param providerConfigurations provider-specific state map; must not be {@code null}
     */
    public GuiConfigurationValues {
        sourceFolder = normalizeText(sourceFolder);
        targetFolder = normalizeText(targetFolder);
        sqliteFile = normalizeText(sqliteFile);
        promptTemplateFile = normalizeText(promptTemplateFile);
        runtimeLockFile = normalizeText(runtimeLockFile);
        logDirectory = normalizeText(logDirectory);
        logLevel = normalizeText(logLevel);
        maxRetriesTransient = normalizeText(maxRetriesTransient);
        maxPages = normalizeText(maxPages);
        maxTextCharacters = normalizeText(maxTextCharacters);
        maxTitleLength = normalizeText(maxTitleLength);
        logAiSensitive = normalizeText(logAiSensitive);
        activeProviderFamily = normalizeText(activeProviderFamily);

        LinkedHashMap<AiProviderFamily, GuiProviderConfigurationState> copy = new LinkedHashMap<>();
        Objects.requireNonNull(providerConfigurations, "providerConfigurations must not be null")
                .forEach(copy::put);
        providerConfigurations = Collections.unmodifiableMap(copy);
    }

    /**
     * Returns the editable state for the requested provider family.
     *
     * @param providerFamily the provider family to look up; must not be {@code null}
     * @return the matching provider state, or {@code null} when not present
     */
    public GuiProviderConfigurationState providerConfiguration(AiProviderFamily providerFamily) {
        return providerConfigurations.get(providerFamily);
    }

    /**
     * Returns a copy with a different active provider-family identifier.
     *
     * @param providerFamily raw provider-family identifier; {@code null} becomes an empty string
     * @return a new configuration values object with the requested active provider value
     */
    public GuiConfigurationValues withActiveProviderFamily(String providerFamily) {
        return new GuiConfigurationValues(sourceFolder, targetFolder, sqliteFile, promptTemplateFile,
                runtimeLockFile, logDirectory, logLevel, maxRetriesTransient, maxPages, maxTextCharacters,
                maxTitleLength, logAiSensitive, providerFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different source-folder path.
     *
     * @param value new value; {@code null} becomes an empty string
     * @return a new configuration values object with the requested source folder
     */
    public GuiConfigurationValues withSourceFolder(String value) {
        return new GuiConfigurationValues(value, targetFolder, sqliteFile, promptTemplateFile,
                runtimeLockFile, logDirectory, logLevel, maxRetriesTransient, maxPages, maxTextCharacters,
                maxTitleLength, logAiSensitive, activeProviderFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different target-folder path.
     *
     * @param value new value; {@code null} becomes an empty string
     * @return a new configuration values object with the requested target folder
     */
    public GuiConfigurationValues withTargetFolder(String value) {
        return new GuiConfigurationValues(sourceFolder, value, sqliteFile, promptTemplateFile,
                runtimeLockFile, logDirectory, logLevel, maxRetriesTransient, maxPages, maxTextCharacters,
                maxTitleLength, logAiSensitive, activeProviderFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different SQLite file path.
     *
     * @param value new value; {@code null} becomes an empty string
     * @return a new configuration values object with the requested SQLite file path
     */
    public GuiConfigurationValues withSqliteFile(String value) {
        return new GuiConfigurationValues(sourceFolder, targetFolder, value, promptTemplateFile,
                runtimeLockFile, logDirectory, logLevel, maxRetriesTransient, maxPages, maxTextCharacters,
                maxTitleLength, logAiSensitive, activeProviderFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different prompt-template file path.
     *
     * @param value new value; {@code null} becomes an empty string
     * @return a new configuration values object with the requested prompt-template file path
     */
    public GuiConfigurationValues withPromptTemplateFile(String value) {
        return new GuiConfigurationValues(sourceFolder, targetFolder, sqliteFile, value,
                runtimeLockFile, logDirectory, logLevel, maxRetriesTransient, maxPages, maxTextCharacters,
                maxTitleLength, logAiSensitive, activeProviderFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different runtime lock file path.
     *
     * @param value new value; {@code null} becomes an empty string
     * @return a new configuration values object with the requested runtime lock file path
     */
    public GuiConfigurationValues withRuntimeLockFile(String value) {
        return new GuiConfigurationValues(sourceFolder, targetFolder, sqliteFile, promptTemplateFile,
                value, logDirectory, logLevel, maxRetriesTransient, maxPages, maxTextCharacters,
                maxTitleLength, logAiSensitive, activeProviderFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different log directory path.
     *
     * @param value new value; {@code null} becomes an empty string
     * @return a new configuration values object with the requested log directory
     */
    public GuiConfigurationValues withLogDirectory(String value) {
        return new GuiConfigurationValues(sourceFolder, targetFolder, sqliteFile, promptTemplateFile,
                runtimeLockFile, value, logLevel, maxRetriesTransient, maxPages, maxTextCharacters,
                maxTitleLength, logAiSensitive, activeProviderFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different log level.
     *
     * @param value new value; {@code null} becomes an empty string
     * @return a new configuration values object with the requested log level
     */
    public GuiConfigurationValues withLogLevel(String value) {
        return new GuiConfigurationValues(sourceFolder, targetFolder, sqliteFile, promptTemplateFile,
                runtimeLockFile, logDirectory, value, maxRetriesTransient, maxPages, maxTextCharacters,
                maxTitleLength, logAiSensitive, activeProviderFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different transient-retry limit.
     *
     * @param value new value; {@code null} becomes an empty string
     * @return a new configuration values object with the requested retry limit
     */
    public GuiConfigurationValues withMaxRetriesTransient(String value) {
        return new GuiConfigurationValues(sourceFolder, targetFolder, sqliteFile, promptTemplateFile,
                runtimeLockFile, logDirectory, logLevel, value, maxPages, maxTextCharacters,
                maxTitleLength, logAiSensitive, activeProviderFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different page limit.
     *
     * @param value new value; {@code null} becomes an empty string
     * @return a new configuration values object with the requested page limit
     */
    public GuiConfigurationValues withMaxPages(String value) {
        return new GuiConfigurationValues(sourceFolder, targetFolder, sqliteFile, promptTemplateFile,
                runtimeLockFile, logDirectory, logLevel, maxRetriesTransient, value, maxTextCharacters,
                maxTitleLength, logAiSensitive, activeProviderFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different text-character limit.
     *
     * @param value new value; {@code null} becomes an empty string
     * @return a new configuration values object with the requested character limit
     */
    public GuiConfigurationValues withMaxTextCharacters(String value) {
        return new GuiConfigurationValues(sourceFolder, targetFolder, sqliteFile, promptTemplateFile,
                runtimeLockFile, logDirectory, logLevel, maxRetriesTransient, maxPages, value,
                maxTitleLength, logAiSensitive, activeProviderFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different maximum base-title length value.
     *
     * @param value new value; {@code null} becomes an empty string
     * @return a new configuration values object with the requested title-length value
     */
    public GuiConfigurationValues withMaxTitleLength(String value) {
        return new GuiConfigurationValues(sourceFolder, targetFolder, sqliteFile, promptTemplateFile,
                runtimeLockFile, logDirectory, logLevel, maxRetriesTransient, maxPages, maxTextCharacters,
                value, logAiSensitive, activeProviderFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different {@code log.ai.sensitive} value.
     *
     * @param value new raw boolean value; {@code null} becomes an empty string
     * @return a new configuration values object with the requested sensitive-log setting
     */
    public GuiConfigurationValues withLogAiSensitive(String value) {
        return new GuiConfigurationValues(sourceFolder, targetFolder, sqliteFile, promptTemplateFile,
                runtimeLockFile, logDirectory, logLevel, maxRetriesTransient, maxPages, maxTextCharacters,
                maxTitleLength, value, activeProviderFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different provider-configurations map.
     *
     * @param providerConfigurations new provider map; must not be {@code null}
     * @return a new configuration values object with the requested provider configurations
     */
    public GuiConfigurationValues withProviderConfigurations(
            Map<AiProviderFamily, GuiProviderConfigurationState> providerConfigurations) {
        return new GuiConfigurationValues(sourceFolder, targetFolder, sqliteFile, promptTemplateFile,
                runtimeLockFile, logDirectory, logLevel, maxRetriesTransient, maxPages, maxTextCharacters,
                maxTitleLength, logAiSensitive, activeProviderFamily, providerConfigurations);
    }

    /**
     * Returns a copy with a different configuration for one provider family.
     * <p>
     * All other provider-family entries are preserved unchanged.
     *
     * @param family the provider family to update; must not be {@code null}
     * @param state  the new provider configuration state; must not be {@code null}
     * @return a new configuration values object with the updated provider configuration
     */
    public GuiConfigurationValues withProviderConfiguration(AiProviderFamily family,
                                                            GuiProviderConfigurationState state) {
        Map<AiProviderFamily, GuiProviderConfigurationState> updated =
                new java.util.LinkedHashMap<>(providerConfigurations);
        updated.put(family, state);
        return withProviderConfigurations(updated);
    }

    private static String normalizeText(String value) {
        return value == null ? "" : value;
    }
}
