package io.cameleer.server.core.storage;

import java.io.IOException;
import java.io.InputStream;

/**
 * Storage abstraction for deployable JAR artifacts. Implementations decide where
 * the bytes live (filesystem today; OCI registry / S3 later — see Zot follow-up
 * issue #158). Callers never construct filesystem paths themselves.
 *
 * <p>The interface is intentionally narrow: enough to upload, fetch, and delete.
 * {@link #locator} returns a stable identifier written into
 * {@code app_versions.jar_path} so existing rows still resolve.
 */
public interface ArtifactStore {

    /** Persist {@code bytes} of {@code size} bytes under {@code coords}. Idempotent
     * on identical content; overwrites on same coords with different content.
     * Returns the locator (same as {@link #locator}) for convenience. */
    String put(ArtifactCoordinates coords, InputStream bytes, long size) throws IOException;

    /** Open the artifact for reading. Caller closes. */
    InputStream get(ArtifactCoordinates coords) throws IOException;

    /** Size of the stored artifact in bytes. Authoritative — read from the backend
     * at serve time so a stale {@code AppVersion.jarSizeBytes} cannot silently
     * truncate downloads. */
    long size(ArtifactCoordinates coords) throws IOException;

    /** True if an artifact is currently stored under {@code coords}. */
    boolean exists(ArtifactCoordinates coords);

    /** Remove the artifact and any backend-specific scaffolding (empty parent dirs, etc.).
     * Silently no-ops if the artifact is already absent. */
    void delete(ArtifactCoordinates coords) throws IOException;

    /** Stable identifier for this storage location — written into
     * {@code app_versions.jar_path} so existing rows still resolve. For the
     * filesystem store this is the absolute path; for OCI it's the ref. */
    String locator(ArtifactCoordinates coords);
}
