// src/studio/generation.ts
var GENERATION_TYPES = ["image", "video", "avatar", "speech", "transcription"];
function isGenerationType(value) {
  return GENERATION_TYPES.includes(value);
}
var DESTINATIONS = [
  { id: "instagram", label: "Instagram", providerIds: ["instagram"], fields: "Caption, hashtags, crop" },
  { id: "tiktok", label: "TikTok", providerIds: ["tiktok"], fields: "Caption, audio note, vertical video" },
  { id: "youtube-shorts", label: "YouTube Shorts", providerIds: ["youtube"], fields: "Title, description, vertical video" },
  { id: "linkedin", label: "LinkedIn", providerIds: ["linkedin"], fields: "Caption, link, creator/company page" },
  { id: "x", label: "X", providerIds: ["twitter"], fields: "Short copy, mentions, thread option" }
];
var CADENCES = ["Manual approval", "Publish now", "Daily creative drop", "Weekly series"];
var MIN_IMAGE_COUNT = 1;
var MAX_IMAGE_COUNT = 4;
function relativeTime(date) {
  if (!date) return "";
  const now = Date.now();
  const diff = now - new Date(date).getTime();
  const minutes = Math.floor(diff / 6e4);
  if (minutes < 1) return "just now";
  if (minutes < 60) return `${minutes}m ago`;
  const hours = Math.floor(minutes / 60);
  if (hours < 24) return `${hours}h ago`;
  const days = Math.floor(hours / 24);
  if (days < 7) return `${days}d ago`;
  return new Date(date).toLocaleDateString("en-US", { month: "short", day: "numeric" });
}
function outputPathFor(type) {
  if (type === "image") return "generated/images";
  if (type === "video") return "generated/videos";
  if (type === "avatar") return "generated/avatars";
  if (type === "speech") return "generated/audio";
  return "generated/transcripts";
}
function generationVaultPath(generation) {
  const value = generation.metadata?.vaultPath;
  return typeof value === "string" && value.trim() ? value.trim() : null;
}
function buildPublishPackage({
  caption,
  postDescription,
  mentions,
  cadence,
  destinations
}) {
  const trimmedCaption = caption.trim();
  const trimmedDescription = postDescription.trim();
  const parsedMentions = mentions.split(",").map((item) => item.trim()).filter(Boolean);
  const selectedDestinations = destinations.filter(Boolean);
  const trimmedCadence = cadence.trim() || "Manual approval";
  const hasPublishContent = Boolean(
    trimmedCaption || trimmedDescription || parsedMentions.length > 0 || selectedDestinations.length > 0 || trimmedCadence !== "Manual approval"
  );
  if (!hasPublishContent) return null;
  return {
    caption: trimmedCaption,
    description: trimmedDescription,
    mentions: parsedMentions,
    destinations: selectedDestinations,
    cadence: trimmedCadence,
    workflowDraft: trimmedCadence !== "Manual approval",
    evalContract: {
      artifactType: "publish_package",
      deterministicChecks: ["has_asset", "has_destination", "has_caption_or_description", "has_cadence"]
    }
  };
}
function isPublishPackage(value) {
  if (!value || typeof value !== "object") return false;
  const publishPackage = value;
  const destinations = Array.isArray(publishPackage.destinations) ? publishPackage.destinations.filter(Boolean) : [];
  const mentions = Array.isArray(publishPackage.mentions) ? publishPackage.mentions.filter(Boolean) : [];
  return Boolean(
    destinations.length > 0 || mentions.length > 0 || typeof publishPackage.caption === "string" && publishPackage.caption.trim() || typeof publishPackage.description === "string" && publishPackage.description.trim() || typeof publishPackage.cadence === "string" && publishPackage.cadence.trim() && publishPackage.cadence.trim() !== "Manual approval"
  );
}
function isDestinationConnected(destination, connections) {
  return connections.some((connection) => connection.status === "connected" && (destination.providerIds.includes(connection.providerId) || destination.providerIds.includes(connection.connectorId)));
}
function selectedModelsWithDefaults(current, catalog) {
  const next = { ...current };
  for (const key of GENERATION_TYPES) {
    const models = catalog.models[key] ?? [];
    const currentOption = models.find((model) => model.id === next[key]);
    if (!next[key] || !currentOption || currentOption.status === "unavailable") {
      next[key] = preferredModelId(key, catalog) ?? "";
    }
  }
  return next;
}
function preferredModelId(type, catalog) {
  if (!catalog) return void 0;
  const models = catalog.models[type] ?? [];
  const preferred = catalog.defaults[type];
  return models.find((model) => model.id === preferred)?.id ?? models.find((model) => model.status !== "unavailable")?.id ?? models[0]?.id;
}
function modelMessage(model, loading, count) {
  if (loading) return "Loading media models...";
  if (count === 0) return "No models are available for this media type.";
  if (!model) return "Select a model.";
  if (model.status === "unavailable") return model.reason ?? "This model is not configured.";
  if (model.status === "limited") return model.reason ? `Limited: ${model.reason}` : "Limited availability.";
  return null;
}
function buildGenerationRequestBody(fields) {
  const body = {
    workspaceId: fields.workspaceId,
    clientRequestId: fields.clientRequestId,
    type: fields.type,
    model: fields.model,
    prompt: fields.prompt.trim(),
    negativePrompt: fields.negativePrompt.trim() || void 0,
    outputPath: fields.outputPath.trim() || void 0
  };
  if (fields.publishPackage) body.publishPackage = fields.publishPackage;
  if (fields.type === "image") Object.assign(body, {
    size: fields.image.size,
    quality: fields.image.quality,
    n: fields.image.count
  });
  if (fields.type === "video") {
    const duration = Number(fields.video.duration);
    Object.assign(body, {
      // omit (let the API default) rather than serialize NaN → null on bad input
      duration: Number.isFinite(duration) ? duration : void 0,
      resolution: fields.video.resolution,
      aspectRatio: fields.video.aspectRatio.trim() || void 0,
      referenceImageUrl: fields.video.referenceImageUrl.trim() || void 0
    });
  }
  if (fields.type === "speech") Object.assign(body, { voice: fields.speech.voice });
  if (fields.type === "avatar") Object.assign(body, {
    audioUrl: fields.avatar.audioUrl.trim(),
    imageUrl: fields.avatar.imageUrl.trim() || void 0,
    avatarId: fields.avatar.avatarId.trim() || void 0
  });
  if (fields.type === "transcription") {
    const temperature = Number(fields.transcription.temperature);
    Object.assign(body, {
      audioUrl: fields.transcription.audioUrl.trim(),
      language: fields.transcription.language.trim() || void 0,
      responseFormat: fields.transcription.responseFormat,
      // omit (let the API default) rather than serialize NaN → null on bad input
      temperature: Number.isFinite(temperature) ? temperature : void 0
    });
  }
  return body;
}
function generationStatus(generation) {
  const metadata = generation.metadata ?? {};
  const status = typeof metadata.generationStatus === "string" ? metadata.generationStatus : "";
  if (status === "pending" || status === "running" || status === "failed" || status === "succeeded") return status;
  return generation.result ? "succeeded" : "pending";
}
function generationError(generation) {
  const metadata = generation.metadata ?? {};
  if (typeof metadata.providerError === "string" && metadata.providerError.trim()) {
    return userSafeGenerationMessage(metadata.providerError);
  }
  if (typeof metadata.storageError === "string" && metadata.storageError.trim()) {
    return metadata.storageError;
  }
  return null;
}
function generationClientRequestId(generation) {
  const metadata = generation.metadata ?? {};
  return typeof metadata.clientRequestId === "string" && metadata.clientRequestId.trim() ? metadata.clientRequestId : null;
}
function generationBatchSlotKey(generation) {
  const metadata = generation.metadata ?? {};
  const batchId = typeof metadata.batchId === "string" && metadata.batchId.trim() ? metadata.batchId : null;
  return batchId && typeof metadata.outputIndex === "number" ? `${batchId}:${metadata.outputIndex}` : null;
}
function generationMergeKey(generation) {
  return generationBatchSlotKey(generation) ?? generationClientRequestId(generation);
}
function mergeLiveGeneration(current, generation) {
  const mergeKey = generationMergeKey(generation);
  const existingIndex = current.findIndex((item) => item.id === generation.id || mergeKey && generationMergeKey(item) === mergeKey);
  if (existingIndex === -1) return [generation, ...current];
  const next = [...current];
  next[existingIndex] = generation;
  return next;
}
function mergeLoaderAndLive(loader, live) {
  if (live.length === 0) return loader;
  const leading = live.map((generation) => {
    const mergeKey = generationMergeKey(generation);
    return mergeKey ? loader.find((gen) => generationMergeKey(gen) === mergeKey) ?? generation : loader.find((gen) => gen.id === generation.id) ?? generation;
  });
  const leadingIds = new Set(leading.map((gen) => gen.id));
  const leadingMergeKeys = new Set(leading.map((gen) => generationMergeKey(gen)).filter((id) => Boolean(id)));
  return [
    ...leading,
    ...loader.filter((gen) => !leadingIds.has(gen.id) && !leadingMergeKeys.has(generationMergeKey(gen) ?? ""))
  ];
}
function isLocalGeneration(generation) {
  return generation.id.startsWith("local-");
}
function generationOutputIndex(generation) {
  const value = generation.metadata?.outputIndex;
  return typeof value === "number" ? value : 0;
}
function latestBatchOf(generations) {
  const first = generations[0];
  if (!first) return [];
  const key = generationClientRequestId(first);
  const batch = key ? generations.filter((generation) => generationClientRequestId(generation) === key) : [first];
  return [...batch].sort((a, b) => generationOutputIndex(a) - generationOutputIndex(b));
}
function userSafeGenerationMessage(message) {
  if (!message) return "Generation failed";
  if (/Tangle API key is invalid or expired/i.test(message)) return message;
  if (/(api[_ -]?key|secret|token|credential|env|configured|configuration)/i.test(message)) {
    return "Generation failed";
  }
  return message;
}
function optimisticGeneration({
  type,
  prompt,
  model,
  clientRequestId,
  outputIndex,
  outputCount
}) {
  const batchId = outputIndex == null ? void 0 : clientRequestId;
  return {
    id: outputIndex == null ? `local-${clientRequestId}` : `local-${clientRequestId}-${outputIndex}`,
    type,
    prompt,
    result: null,
    model: model ?? null,
    cost: null,
    createdAt: /* @__PURE__ */ new Date(),
    metadata: {
      generationStatus: "pending",
      provider: type,
      clientRequestId,
      batchId,
      outputIndex,
      outputCount
    }
  };
}
function failedOptimisticGeneration(generation) {
  return {
    ...generation,
    metadata: {
      ...generation.metadata ?? {},
      generationStatus: "failed",
      providerError: "Generation failed"
    }
  };
}
function normalizeImageCount(value) {
  const numeric = typeof value === "number" ? value : Number(value);
  if (!Number.isFinite(numeric)) return MIN_IMAGE_COUNT;
  return Math.min(Math.max(Math.trunc(numeric), MIN_IMAGE_COUNT), MAX_IMAGE_COUNT);
}

export {
  GENERATION_TYPES,
  isGenerationType,
  DESTINATIONS,
  CADENCES,
  MIN_IMAGE_COUNT,
  MAX_IMAGE_COUNT,
  relativeTime,
  outputPathFor,
  generationVaultPath,
  buildPublishPackage,
  isPublishPackage,
  isDestinationConnected,
  selectedModelsWithDefaults,
  preferredModelId,
  modelMessage,
  buildGenerationRequestBody,
  generationStatus,
  generationError,
  generationMergeKey,
  mergeLiveGeneration,
  mergeLoaderAndLive,
  isLocalGeneration,
  latestBatchOf,
  userSafeGenerationMessage,
  optimisticGeneration,
  failedOptimisticGeneration,
  normalizeImageCount
};
//# sourceMappingURL=chunk-DTS5TZRN.js.map