/*
 * Copyright 2025 Red Hat, Inc. and/or its affiliates
 * and other contributors as indicated by the @author tags.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package org.keycloak.storage.configuration;

import java.util.Objects;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.function.Supplier;

import org.keycloak.provider.Provider;

/**
 * A {@link Provider} to store server configuration to be shared between the Keycloak instances.
 * <p>
 * This provider is a key-value store where both keys and values are {@link String}.
 */
public interface ServerConfigStorageProvider extends Provider {

    /**
     * Returns the value to which the specified {@code key}.
     *
     * @param key The {@code key} whose associated value is to be returned.
     * @return The value from the specified {@code key}.
     * @throws NullPointerException if the specified {@code key} is {@code null}.
     */
    Optional<String> find(String key);

    /**
     * Stores the specified {@code value} with the specified {@code key}.
     * <p>
     * If the {@code key} exists, its value is updated.
     *
     * @param key   The {@code key} with which the specified {@code value} is to be stored.
     * @param value The {@code value} to be associated with the specified {@code key}.
     * @throws NullPointerException if the specified {@code key} or {@code value} is {@code null}.
     */
    void store(String key, String value);

    /**
     * Removes the {@code value} specified by the {@code key}.
     *
     * @param key The {@code key} whose value is to be removed.
     * @throws NullPointerException if the specified {@code key} is {@code null}.
     */
    void remove(String key);

    /**
     * Returns the value to which the specified {@code key} or, if not found, stores the value returned by the
     * {@code valueGenerator}.
     *
     * @param key            The {@code key} whose associated value is to be returned or stored.
     * @param valueGenerator The {@link Supplier} to generate the value if it is not found.
     * @return The {value stored by the {@code key}, or the value generated by the {@link Supplier}.
     * @throws NullPointerException if the specified {@code key}, {@code valueGenerator} or {@link Supplier} return
     *                              value is {@code null}.
     */
    String loadOrCreate(String key, Supplier<String> valueGenerator);

    /**
     * Same as {@code loadOrCreate(key, () -> value)}.
     *
     * @see #loadOrCreate(String, Supplier)
     */
    default String loadOrCreate(String key, String value) {
        return loadOrCreate(key, () -> value);
    }

    /**
     * Same as {@code replace(key, Objects.requireNonNull(expected)::equals, () -> Objects.requireNonNull(newValue))}.
     *
     * @see #replace(String, Predicate, Supplier)
     */
    default boolean replace(String key, String expected, String newValue) {
        return replace(key, Objects.requireNonNull(expected)::equals, () -> Objects.requireNonNull(newValue));
    }

    /**
     * Replaces the value specified by {@code key} if the {@link Predicate} return a {@code true} value.
     *
     * @param key              The {@code key} whose associated value is to be replaced.
     * @param replacePredicate The {@link Predicate} to signal if the value should be replaced.
     * @param valueGenerator   The {@link Supplier} to generate the value if it is should be replaced.
     * @return {@code true} if the value is replaced, and {@code false} otherwise.
     */
    boolean replace(String key, Predicate<String> replacePredicate, Supplier<String> valueGenerator);

}
