import { ContainerClient } from '@azure/storage-blob';
import { BlobStore, ArtifactSink } from '@nanoesis/engine';
export { contentTypeFor } from '@nanoesis/engine';

/**
 * The narrow blob-container seam the adapters depend on. It is the *only* surface the
 * Azure SDK is wrapped behind ({@link AzureBlobContainer}), which keeps the SDK out of
 * the adapters' logic and lets every path-mapping rule be unit-tested against the
 * in-memory container below, no real or emulated blob store in the default suite
 * (CLAUDE.md §5). Names are flat blob names (forward slashes, no leading slash).
 */
interface BlobContainer {
    /**
     * Every blob name beginning with `prefix` ("" lists the whole container), **sorted
     * ascending** as part of the port contract (CLAUDE §2: determinism). Callers feed the
     * result into Map/Set-backed downstream walks, so any non-deterministic implementation
     * order would leak into compiled output. Implementations own the sort — Azure's
     * native listing order is unspecified and must be sorted at the boundary.
     */
    list(prefix: string): Promise<readonly string[]>;
    /** A blob's bytes, or null if it does not exist. */
    read(name: string): Promise<Uint8Array | null>;
    /** Create or overwrite a blob with the given bytes and content type. */
    write(name: string, data: Uint8Array, contentType: string): Promise<void>;
    /** Delete a blob; a missing blob is a no-op (idempotent). */
    remove(name: string): Promise<void>;
}
/**
 * An in-memory {@link BlobContainer} backed by a name→bytes map. The test double for
 * the blob adapters, the LSP guarantee that the real Azure container behaves the same.
 */
declare class InMemoryBlobContainer implements BlobContainer {
    private readonly blobs;
    constructor(seed?: Readonly<Record<string, Uint8Array | string>>);
    list(prefix: string): Promise<readonly string[]>;
    read(name: string): Promise<Uint8Array | null>;
    write(name: string, data: Uint8Array): Promise<void>;
    remove(name: string): Promise<void>;
    /** Test helper: the current blob names. */
    get names(): readonly string[];
}

/**
 * The real {@link BlobContainer}, wrapping `@azure/storage-blob`. This is the *only*
 * file in the adapter that touches the SDK (CLAUDE.md §3, concretes injected via a
 * narrow seam); everything else is pure logic tested against {@link InMemoryBlobContainer}.
 * It talks to a real Azure account or to **Azurite** locally, unchanged, the
 * `UseDevelopmentStorage=true` connection string points at the emulator.
 */
declare class AzureBlobContainer implements BlobContainer {
    private readonly client;
    constructor(client: ContainerClient);
    /** Build from a connection string (e.g. `UseDevelopmentStorage=true` for Azurite). */
    static fromConnectionString(connectionString: string, containerName: string): AzureBlobContainer;
    /** Create the container if it does not exist (idempotent host start-up step). */
    ensureContainer(): Promise<void>;
    list(prefix: string): Promise<readonly string[]>;
    read(name: string): Promise<Uint8Array | null>;
    write(name: string, data: Uint8Array, contentType: string): Promise<void>;
    remove(name: string): Promise<void>;
}

/**
 * The engine's {@link BlobStore} (get/put/delete, DESIGN §11c) over a {@link BlobContainer}.
 * This is the adopter's storage primitive the index-backed working store (and the publish
 * sink) run on, and it works over the real {@link AzureBlobContainer} in production or the
 * in-memory container in tests (LSP). Keys are POSIX-style; `put` stamps a content type
 * from the key's extension so a published blob serves correctly as a static-website file.
 */
declare class BlobContainerStore implements BlobStore {
    private readonly container;
    constructor(container: BlobContainer);
    get(key: string): Promise<Uint8Array | undefined>;
    put(key: string, bytes: Uint8Array): Promise<void>;
    delete(key: string): Promise<void>;
}

/**
 * The **published store** over an Azure Blob container (DESIGN §11, §11b, §11c): the
 * publish pipeline's {@link ArtifactSink}. Publish only writes now, clearing the target
 * is the adopter's job (DESIGN §11c), so {@link wipe} (delete every blob in the container)
 * is the host's own pre-clear that it calls before a wipe-and-reupload republish. Each
 * artifact is uploaded with a content type inferred from its path so the container can be
 * served as a static website (Azurite's `$web`/static-site endpoint locally).
 */
declare class BlobArtifactSink implements ArtifactSink {
    private readonly container;
    constructor(container: BlobContainer);
    wipe(): Promise<void>;
    write(path: string, contents: string | Uint8Array): Promise<void>;
}

/**
 * Pure path/name helper for the blob adapters. Blob storage is a *flat* namespace, a blob
 * is just a name like "content/blog/post.json", so the only mapping the SDK glue needs is
 * to normalise a site-relative path into a blob name. Enumeration lives in the engine's
 * content index (DESIGN §11d), not here. Keeping this pure means it is unit-tested without
 * a real (or emulated) blob store; the SDK glue stays thin (CLAUDE.md §5).
 */
/** Normalise a site path to a blob name: forward slashes, no leading/trailing slash. */
declare function normalizeBlobName(path: string): string;

export { AzureBlobContainer, BlobArtifactSink, type BlobContainer, BlobContainerStore, InMemoryBlobContainer, normalizeBlobName };
