/*
 * SPDX-License-Identifier: MIT
 */
package org.ta4j.core.analysis;

import java.time.Instant;
import java.util.Objects;

/**
 * Options controlling how windowed criterion analysis is resolved.
 *
 * <p>
 * The default context is conservative and deterministic:
 * </p>
 * <ul>
 * <li>{@link MissingHistoryPolicy#STRICT}</li>
 * <li>{@link PositionInclusionPolicy#EXIT_IN_WINDOW}</li>
 * <li>{@link OpenPositionHandling#IGNORE}</li>
 * <li>no explicit anchor/as-of instant</li>
 * </ul>
 *
 * @param missingHistoryPolicy    behavior when requested history is unavailable
 * @param positionInclusionPolicy policy used to include closed positions in the
 *                                window
 * @param openPositionHandling    handling strategy for open position at window
 *                                end; reuses the existing shared enum
 *                                {@link OpenPositionHandling} where
 *                                {@link OpenPositionHandling#IGNORE} excludes
 *                                open positions and
 *                                {@link OpenPositionHandling#MARK_TO_MARKET}
 *                                synthesizes window-end valuation
 * @param asOf                    optional anchor/as-of instant; when null, the
 *                                series end is used for lookback windows
 * @since 0.22.4
 */
public record AnalysisContext(MissingHistoryPolicy missingHistoryPolicy,
        PositionInclusionPolicy positionInclusionPolicy, OpenPositionHandling openPositionHandling, Instant asOf) {

    /**
     * Policy for unavailable historical bars.
     *
     * @since 0.22.4
     */
    public enum MissingHistoryPolicy {
        /**
         * Fail fast when requested window references unavailable series history.
         */
        STRICT,
        /**
         * Intersect the requested window with available series history.
         */
        CLAMP
    }

    /**
     * Policy controlling which closed positions are included in a window.
     *
     * @since 0.22.4
     */
    public enum PositionInclusionPolicy {
        /**
         * Include a closed position when its exit index is inside the window.
         */
        EXIT_IN_WINDOW,
        /**
         * Include a closed position only when both entry and exit are in the window.
         */
        FULLY_CONTAINED
    }

    /**
     * Creates a default context.
     *
     * @since 0.22.4
     */
    public AnalysisContext() {
        this(MissingHistoryPolicy.STRICT, PositionInclusionPolicy.EXIT_IN_WINDOW, OpenPositionHandling.IGNORE, null);
    }

    /**
     * Validates the context.
     */
    public AnalysisContext {
        Objects.requireNonNull(missingHistoryPolicy, "missingHistoryPolicy");
        Objects.requireNonNull(positionInclusionPolicy, "positionInclusionPolicy");
        Objects.requireNonNull(openPositionHandling, "openPositionHandling");
    }

    /**
     * Creates a default context.
     *
     * @return default window analysis context
     * @since 0.22.4
     */
    public static AnalysisContext defaults() {
        return new AnalysisContext();
    }

    /**
     * Returns a copy with a different missing-history policy.
     *
     * @param policy the policy to use
     * @return updated context
     * @since 0.22.4
     */
    public AnalysisContext withMissingHistoryPolicy(MissingHistoryPolicy policy) {
        return new AnalysisContext(Objects.requireNonNull(policy, "policy"), positionInclusionPolicy,
                openPositionHandling, asOf);
    }

    /**
     * Returns a copy with a different position-inclusion policy.
     *
     * @param policy the policy to use
     * @return updated context
     * @since 0.22.4
     */
    public AnalysisContext withPositionInclusionPolicy(PositionInclusionPolicy policy) {
        return new AnalysisContext(missingHistoryPolicy, Objects.requireNonNull(policy, "policy"), openPositionHandling,
                asOf);
    }

    /**
     * Returns a copy with a different open-position handling.
     *
     * @param handling the handling to use
     * @return updated context
     * @since 0.22.4
     */
    public AnalysisContext withOpenPositionHandling(OpenPositionHandling handling) {
        return new AnalysisContext(missingHistoryPolicy, positionInclusionPolicy,
                Objects.requireNonNull(handling, "handling"), asOf);
    }

    /**
     * Returns a copy with a different anchor/as-of instant.
     *
     * @param asOf the anchor instant used by lookback windows, or null to use
     *             series end
     * @return updated context
     * @since 0.22.4
     */
    public AnalysisContext withAsOf(Instant asOf) {
        return new AnalysisContext(missingHistoryPolicy, positionInclusionPolicy, openPositionHandling, asOf);
    }
}
