import { promises as fs } from "fs";
import { findOneWithDecryption } from "@open-mercato/shared/lib/encryption/find";
const DEFAULT_MAX_INLINE_BYTES = 4 * 1024 * 1024;
const DEFAULT_MAX_TEXT_CHARS = 64 * 1024;
function classifyMediaType(mimeType) {
  const normalized = (mimeType ?? "").toLowerCase().trim();
  if (normalized.startsWith("image/")) return "image";
  if (normalized === "application/pdf") return "pdf";
  return "file";
}
function isTextLikeMime(mimeType) {
  const normalized = (mimeType ?? "").toLowerCase().trim();
  if (!normalized) return false;
  if (normalized.startsWith("text/")) return true;
  if (normalized === "application/json") return true;
  if (normalized === "application/xml") return true;
  if (normalized === "application/x-yaml" || normalized === "text/yaml") return true;
  if (normalized === "application/csv") return true;
  return false;
}
function truncateText(value, maxChars) {
  if (value.length <= maxChars) return value;
  return `${value.slice(0, Math.max(0, maxChars - 16))}
[... truncated]`;
}
function resolveEm(container) {
  if (!container) return null;
  try {
    const candidate = container.resolve("em");
    return candidate ?? null;
  } catch {
    return null;
  }
}
function resolveSigner(container) {
  if (!container) return null;
  try {
    const candidate = container.resolve("attachmentSigner");
    if (candidate && typeof candidate.sign === "function") {
      return candidate;
    }
  } catch {
    return null;
  }
  return null;
}
async function loadAttachmentRow(em, attachmentId, authContext) {
  const { Attachment } = await import("@open-mercato/core/modules/attachments/data/entities");
  const record = await findOneWithDecryption(
    em,
    Attachment,
    { id: attachmentId },
    void 0,
    {
      tenantId: authContext.tenantId,
      organizationId: authContext.organizationId
    }
  );
  if (!record) return null;
  const row = record;
  return {
    id: row.id,
    entityId: row.entityId,
    fileName: row.fileName,
    mimeType: row.mimeType,
    fileSize: row.fileSize,
    storagePath: row.storagePath,
    storageDriver: row.storageDriver,
    partitionCode: row.partitionCode,
    tenantId: row.tenantId ?? null,
    organizationId: row.organizationId ?? null,
    content: row.content ?? null
  };
}
function rowBelongsToCaller(row, authContext) {
  if (authContext.isSuperAdmin) return true;
  if (row.tenantId && row.tenantId !== authContext.tenantId) return false;
  if (row.organizationId && row.organizationId !== authContext.organizationId) return false;
  return true;
}
async function readAttachmentBytes(row) {
  const { resolveAttachmentAbsolutePath } = await import("@open-mercato/core/modules/attachments/lib/storage");
  const absolutePath = resolveAttachmentAbsolutePath(
    row.partitionCode,
    row.storagePath,
    row.storageDriver
  );
  try {
    const buffer = await fs.readFile(absolutePath);
    return new Uint8Array(buffer);
  } catch (error) {
    console.warn(
      `[AI Agents] Failed to read attachment ${row.id} from storage; falling back to metadata-only:`,
      error
    );
    return null;
  }
}
async function classifyAndBuildPart(row, mediaClass, maxInlineBytes, maxTextChars, signer, authContext) {
  const base = {
    attachmentId: row.id,
    fileName: row.fileName,
    mediaType: row.mimeType || "application/octet-stream"
  };
  if (mediaClass === "file" && isTextLikeMime(row.mimeType) && typeof row.content === "string" && row.content.length > 0) {
    return {
      ...base,
      source: "text",
      textContent: truncateText(row.content, maxTextChars)
    };
  }
  if (mediaClass === "image" || mediaClass === "pdf") {
    if (row.fileSize > 0 && row.fileSize <= maxInlineBytes) {
      const bytes = await readAttachmentBytes(row);
      if (bytes) {
        return {
          ...base,
          source: "bytes",
          data: bytes
        };
      }
    }
    if (signer) {
      try {
        const url = await signer.sign({
          attachmentId: row.id,
          fileName: row.fileName,
          mediaType: row.mimeType,
          tenantId: authContext.tenantId,
          organizationId: authContext.organizationId
        });
        if (typeof url === "string" && url.length > 0) {
          return {
            ...base,
            source: "signed-url",
            url
          };
        }
      } catch (error) {
        console.warn(
          `[AI Agents] attachmentSigner failed for ${row.id}; falling back to metadata-only:`,
          error
        );
      }
    }
    return { ...base, source: "metadata-only" };
  }
  return { ...base, source: "metadata-only" };
}
async function resolveAttachmentParts(input) {
  const ids = Array.from(input.attachmentIds ?? []);
  if (ids.length === 0) return [];
  const em = resolveEm(input.container);
  if (!em) {
    console.warn(
      "[AI Agents] resolveAttachmentParts called without a DI container exposing `em`; skipping attachment resolution."
    );
    return [];
  }
  const maxInlineBytes = input.maxInlineBytes ?? DEFAULT_MAX_INLINE_BYTES;
  const maxTextChars = input.maxTextChars ?? DEFAULT_MAX_TEXT_CHARS;
  const signer = resolveSigner(input.container);
  const acceptedSet = input.acceptedMediaTypes ? new Set(input.acceptedMediaTypes) : null;
  const parts = [];
  for (const id of ids) {
    if (typeof id !== "string" || id.length === 0) continue;
    let row;
    try {
      row = await loadAttachmentRow(em, id, input.authContext);
    } catch (error) {
      console.warn(
        `[AI Agents] Failed to load attachment ${id}; skipping:`,
        error
      );
      continue;
    }
    if (!row) {
      console.warn(`[AI Agents] Attachment ${id} not found; skipping.`);
      continue;
    }
    if (!rowBelongsToCaller(row, input.authContext)) {
      console.warn(
        `[AI Agents] Attachment ${id} is out of scope for caller (tenant=${input.authContext.tenantId}, org=${input.authContext.organizationId}); skipping.`
      );
      continue;
    }
    const mediaClass = classifyMediaType(row.mimeType);
    if (acceptedSet && !acceptedSet.has(mediaClass)) {
      console.warn(
        `[AI Agents] Attachment ${id} (${row.mimeType}) is not in agent acceptedMediaTypes=${[...acceptedSet].join(",")}; skipping.`
      );
      continue;
    }
    try {
      const part = await classifyAndBuildPart(
        row,
        mediaClass,
        maxInlineBytes,
        maxTextChars,
        signer,
        input.authContext
      );
      parts.push(part);
    } catch (error) {
      console.warn(
        `[AI Agents] Failed to build attachment part for ${id}; skipping:`,
        error
      );
    }
  }
  return parts;
}
async function resolveAttachmentPartsForAgent(input) {
  if (!input.attachmentIds || input.attachmentIds.length === 0) return [];
  return resolveAttachmentParts({
    attachmentIds: input.attachmentIds,
    authContext: input.authContext,
    acceptedMediaTypes: input.agent.acceptedMediaTypes,
    container: input.container
  });
}
function attachmentPartsToUiFileParts(parts) {
  const output = [];
  for (const part of parts) {
    if (part.source === "bytes" && part.data) {
      const base64 = toBase64(part.data);
      if (base64) {
        output.push({
          type: "file",
          mediaType: part.mediaType,
          filename: part.fileName,
          url: `data:${part.mediaType};base64,${base64}`
        });
      }
      continue;
    }
    if (part.source === "signed-url" && typeof part.url === "string" && part.url.length > 0) {
      output.push({
        type: "file",
        mediaType: part.mediaType,
        filename: part.fileName,
        url: part.url
      });
    }
  }
  return output;
}
function summarizeAttachmentPartsForPrompt(parts) {
  if (parts.length === 0) return null;
  const lines = ["[ATTACHMENTS]"];
  for (const part of parts) {
    const header = `- ${part.fileName} (${part.mediaType}, source=${part.source})`;
    if (part.source === "text" && typeof part.textContent === "string" && part.textContent.length > 0) {
      lines.push(header);
      lines.push(part.textContent);
    } else {
      lines.push(header);
    }
  }
  return lines.join("\n");
}
function toBase64(data) {
  if (typeof data === "string") return data;
  try {
    return Buffer.from(data).toString("base64");
  } catch {
    return null;
  }
}
export {
  attachmentPartsToUiFileParts,
  resolveAttachmentParts,
  resolveAttachmentPartsForAgent,
  summarizeAttachmentPartsForPrompt
};
//# sourceMappingURL=attachment-parts.js.map
