/**
 * Fleet artifacts — blob wiring (§13/§26).
 *
 * Large run outputs (logs, coverage, screenshots, build output) are written to
 * blob storage; only a POINTER (blob key + size/content-type/sha256) is persisted
 * in the `fleet_artifacts` Cosmos container. The bytes NEVER touch Cosmos — this
 * keeps documents well under the doc-size / RU ceilings no matter how large a log
 * gets. Read access is granted via a short-lived SAS URL minted on demand from the
 * stored key (the URL itself is never persisted).
 *
 * Blob key scheme (deterministic, product- and job-scoped):
 *   fleet/<productId>/<jobId>/<artifactId>-<kind>
 */

import { createHash, randomUUID } from 'node:crypto';
import { getBucket, generateSasUrl } from '../../lib/blob.js';
import * as repo from './repository.js';
import { FleetArtifactDocSchema, type FleetArtifactDoc, type FleetArtifactKind } from './types.js';

/** Container holding all fleet run-output blobs. */
export const FLEET_ARTIFACTS_CONTAINER = 'fleet-artifacts';

/** SAS read-URL lifetime. Short-lived — callers re-issue via getArtifactDownload. */
export const ARTIFACT_SAS_TTL_MINUTES = 15;

/** A persisted artifact pointer paired with a freshly-minted SAS read URL. */
export interface ArtifactWithDownload {
  artifact: FleetArtifactDoc;
  downloadUrl: string;
}

export interface UploadArtifactArgs {
  productId: string;
  jobId: string;
  kind: FleetArtifactKind;
  bytes: Buffer;
  contentType: string;
  runId?: string;
}

/** Deterministic blob key for an artifact. */
export function artifactBlobKey(
  productId: string,
  jobId: string,
  artifactId: string,
  kind: FleetArtifactKind
): string {
  return `fleet/${productId}/${jobId}/${artifactId}-${kind}`;
}

/**
 * Upload artifact bytes to blob storage and persist the Cosmos pointer.
 * Returns the pointer doc plus a short-lived SAS read URL. The bytes live only in
 * blob — the returned doc carries no inline payload.
 */
export async function uploadArtifact(args: UploadArtifactArgs): Promise<ArtifactWithDownload> {
  const id = `art_${randomUUID()}`;
  const blobKey = artifactBlobKey(args.productId, args.jobId, id, args.kind);

  const bucket = await getBucket(FLEET_ARTIFACTS_CONTAINER);
  const meta = await bucket.upload(blobKey, args.bytes, { contentType: args.contentType });

  const sha256 = createHash('sha256').update(args.bytes).digest('hex');

  const doc: FleetArtifactDoc = FleetArtifactDocSchema.parse({
    id,
    productId: args.productId,
    jobId: args.jobId,
    runId: args.runId,
    kind: args.kind,
    blobKey,
    contentType: args.contentType,
    sizeBytes: meta.size ?? args.bytes.length,
    sha256,
    createdAt: new Date().toISOString(),
  });

  const artifact = await repo.createArtifact(doc);
  const downloadUrl = await issueDownloadUrl(blobKey);
  return { artifact, downloadUrl };
}

/**
 * Re-issue a fresh short-lived SAS read URL for an existing artifact, scoped to
 * its owning product. Returns null when the artifact does not exist for that product.
 */
export async function getArtifactDownload(
  id: string,
  productId: string
): Promise<ArtifactWithDownload | null> {
  const artifact = await repo.getArtifact(id, productId);
  if (!artifact) return null;
  const downloadUrl = await issueDownloadUrl(artifact.blobKey);
  return { artifact, downloadUrl };
}

/**
 * Delete an artifact's pointer (and its backing blob), scoped to its owning
 * product. Returns false when nothing matched.
 */
export async function deleteArtifact(id: string, productId: string): Promise<boolean> {
  const artifact = await repo.getArtifact(id, productId);
  if (!artifact) return false;
  const bucket = await getBucket(FLEET_ARTIFACTS_CONTAINER);
  await bucket.delete(artifact.blobKey);
  await repo.deleteArtifact(id, productId);
  return true;
}

function issueDownloadUrl(blobKey: string): Promise<string> {
  return generateSasUrl(FLEET_ARTIFACTS_CONTAINER, blobKey, 'r', ARTIFACT_SAS_TTL_MINUTES);
}
