package com.r35157.libs.solana;

import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Objects;

/**
 * Outcome observed for a Solana transaction at a requested commitment level.
 *
 * <p>A timed-out outcome remains unknown. It does not prove that the
 * transaction failed, was rejected, expired, or will never land, and callers
 * must not automatically resubmit an equivalent transaction.</p>
 *
 * @param status definitive success, definitive on-chain failure, or timeout
 * @param slot transaction slot for a definitive outcome, otherwise
 *             {@code null}
 * @param failureDetails compact JSON containing Solana's complete on-chain
 *                       {@code err} value for a failed outcome, otherwise
 *                       {@code null}
 */
public record SolanaTransactionOutcome(
        @NotNull Status status,
        @Nullable ΩSolanaSlotΩ slot,
        @Nullable String failureDetails
) {
    /**
     * Validates the field invariants associated with the outcome status.
     */
    public SolanaTransactionOutcome {
        Objects.requireNonNull(status, "status");

        switch (status) {
            case SUCCEEDED -> {
                if (slot == null) {
                    throw new IllegalArgumentException(
                            "A successful transaction outcome requires a slot"
                    );
                }
                if (failureDetails != null) {
                    throw new IllegalArgumentException(
                            "A successful transaction outcome must not contain "
                                    + "failure details"
                    );
                }
            }
            case FAILED -> {
                if (slot == null) {
                    throw new IllegalArgumentException(
                            "A failed transaction outcome requires a slot"
                    );
                }
                if (failureDetails == null || failureDetails.isBlank()) {
                    throw new IllegalArgumentException(
                            "A failed transaction outcome requires failure "
                                    + "details"
                    );
                }
            }
            case TIMED_OUT -> {
                if (slot != null || failureDetails != null) {
                    throw new IllegalArgumentException(
                            "A timed-out transaction outcome must not contain "
                                    + "a slot or failure details"
                    );
                }
            }
        }
    }

    /**
     * Classification of a transaction observation.
     */
    public enum Status {
        /**
         * The transaction reached the requested commitment without an
         * on-chain execution error.
         */
        SUCCEEDED,

        /**
         * The transaction reached the requested commitment with a non-null
         * Solana on-chain {@code err} value.
         */
        FAILED,

        /**
         * The requested definitive outcome remained unknown at the deadline.
         */
        TIMED_OUT
    }
}
