/*
 * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
 * or more contributor license agreements. Licensed under the "Elastic License
 * 2.0", the "GNU Affero General Public License v3.0 only", and the "Server Side
 * Public License v 1"; you may not use this file except in compliance with, at
 * your election, the "Elastic License 2.0", the "GNU Affero General Public
 * License v3.0 only", or the "Server Side Public License, v 1".
 */

package org.elasticsearch.action;

import org.elasticsearch.action.support.IndicesOptions;
import org.elasticsearch.core.Nullable;
import org.elasticsearch.index.shard.ShardId;

import java.util.Collection;

/**
 * Needs to be implemented by all {@link org.elasticsearch.action.ActionRequest} subclasses that relate to
 * one or more indices. Allows to retrieve which indices the action relates to.
 * In case of internal requests originated during the distributed execution of an external request,
 * they will still return the indices that the original request related to.
 */
public interface IndicesRequest {

    /**
     * Returns the array of indices that the action relates to
     */
    String[] indices();

    /**
     * Returns the indices options used to resolve indices. They tell for instance whether a single index is
     * accepted, whether an empty array will be converted to _all, and how wildcards will be expanded if needed.
     */
    IndicesOptions indicesOptions();

    /**
     * Determines whether the request should be applied to data streams. When {@code false}, none of the names or
     * wildcard expressions in {@link #indices} should be applied to or expanded to any data streams. All layers
     * involved in the request's fulfillment including security, name resolution, etc., should respect this flag.
     */
    default boolean includeDataStreams() {
        return false;
    }

    /**
     * Interface for indicating potential work related to cross-project authentication and authorization.
     */
    interface CrossProjectCandidate {

        /**
         * Determines whether the request type can support cross-project processing. Cross-project processing entails
         * 1. UIAM authentication and authorization projects resolution.
         * 2. If applicable, cross-project flat-world index resolution and error handling
         * Note: this method only determines in the request _supports_ cross-project. Whether cross-project processing
         * is actually performed depends on other factors such as:
         * - Whether CPS is enabled which impacts both 1 and 2.
         * - Whether {@link IndicesOptions} supports it when the request is an {@link IndicesRequest}. This only impacts 2.
         * See also {@link org.elasticsearch.search.crossproject.CrossProjectModeDecider}.
         */
        default boolean allowsCrossProject() {
            return false;
        }
    }

    interface Replaceable extends IndicesRequest, CrossProjectCandidate {
        /**
         * Sets the indices that the action relates to.
         */
        IndicesRequest indices(String... indices);

        /**
         * Record the results of index resolution. See {@link ResolvedIndexExpressions} for details.
         * Note: this method does not replace {@link #indices(String...)}. {@link #indices(String...)} must still be called to update
         * the actual list of indices the request relates to.
         * Note: the field is transient and not serialized.
         */
        default void setResolvedIndexExpressions(ResolvedIndexExpressions expressions) {}

        /**
         * Returns the results of index resolution, if recorded via
         * {@link #setResolvedIndexExpressions(ResolvedIndexExpressions)}. Null if not recorded.
         */
        @Nullable
        default ResolvedIndexExpressions getResolvedIndexExpressions() {
            return null;
        }

        /**
         * Determines whether the request can contain indices on a remote cluster.
         * <p>
         * NOTE in theory this method can belong to the {@link IndicesRequest} interface because whether a request
         * allowing remote indices has no inherent relationship to whether it is {@link Replaceable} or not.
         * However, we don't have an existing request that is non-replaceable but allows remote indices.
         * In addition, authorization code currently relies on the fact that non-replaceable requests do not allow
         * remote indices.
         * That said, it is possible to remove this constraint should the needs arise in the future. We just need
         * proceed with extra caution.
         */
        default boolean allowsRemoteIndices() {
            return false;
        }

        @Nullable // if no routing is specified
        default String getProjectRouting() {
            return null;
        }
    }

    /**
     * For use cases where a Request instance cannot implement Replaceable due to not supporting wildcards
     * and only supporting a single index at a time, this is an alternative interface that the
     * security layer checks against to determine if remote indices are allowed for that Request type.
     *
     * This may change with https://github.com/elastic/elasticsearch/issues/105598
     */
    interface SingleIndexNoWildcards extends IndicesRequest, CrossProjectCandidate {
        default boolean allowsRemoteIndices() {
            return true;
        }

        /**
         * Determines whether the request type allows cross-project processing. Cross-project processing entails cross-project search
         * index resolution and error handling. Note: this method only determines in the request _supports_ cross-project.
         * Whether cross-project processing is actually performed is determined by {@link IndicesOptions}.
         */
        default boolean allowsCrossProject() {
            return true;
        }

        /**
         * Marks request local. Local requests should be processed on the same cluster, even if they have cluster-alias prefix.
         */
        void markOriginOnly();
    }

    /**
     * This subtype of request is for requests which may travel to remote clusters. These requests may need to provide additional
     * information to the system on top of the indices the action relates to in order to be handled correctly in all cases.
     */
    interface RemoteClusterShardRequest extends IndicesRequest {
        /**
         * Returns the shards this action is targeting directly, which may not obviously align with the indices returned by
         * {@code indices()}. This is mostly used by requests which fan out to a number of shards for the those fan-out requests.
         *
         * A default is intentionally not provided for this method. It is critical that this method be implemented correctly for all
         * remote cluster requests,
         */
        Collection<ShardId> shards();
    }
}
