import type { FastifyBaseLogger } from 'fastify';
import type { IcebergClient } from '../../clients/iceberg-client.js';

export interface StoredBlob {
  id: string;
  userId: string;
  sessionId: string;
  messageId: string;
  blobType: 'image' | 'audio';
  mimeType: string;
  data: string;      // base64
  caption?: string;
  timestamp: number; // microseconds
}

/**
 * Blob store for binary attachments (images, audio) referenced by conversation messages.
 *
 * Unlike text messages (Redis hot + Iceberg cold), blobs write directly to S3 Parquet
 * on each turn — they're infrequent enough that per-turn files don't cause fragmentation.
 * Blob IDs are stored in the parent message's metadata field for later retrieval.
 */
export class BlobStore {
  constructor(
    private icebergClient: IcebergClient | undefined,
    private logger: FastifyBaseLogger
  ) {}

  /**
   * Write all blobs for one assistant turn to a single S3 Parquet file.
   * Returns the blob IDs assigned. Failures are logged but do not throw.
   */
  async writeBlobs(
    userId: string,
    sessionId: string,
    messageId: string,
    blobs: Array<{ blobType: 'image' | 'audio'; mimeType: string; data: string; caption?: string }>
  ): Promise<string[]> {
    if (!this.icebergClient || blobs.length === 0) {
      return [];
    }

    const now = Date.now();
    const stored = blobs.map((b, i) => ({
      id: `blob_${userId}_${now}_${i}`,
      user_id: userId,
      session_id: sessionId,
      message_id: messageId,
      blob_type: b.blobType,
      mime_type: b.mimeType,
      data: b.data,
      caption: b.caption ?? null,
      timestamp: now * 1000, // microseconds
    }));

    try {
      await this.icebergClient.appendBlobs(userId, sessionId, messageId, stored);
      this.logger.info({ userId, sessionId, count: stored.length }, 'Blobs written to S3');
    } catch (error) {
      this.logger.error({ error }, 'Failed to write blobs to S3');
      // Don't throw — blob failure should not break the conversation turn
    }

    return stored.map(b => b.id);
  }

  /**
   * Retrieve blobs by their IDs from S3/Iceberg cold storage.
   */
  async getBlobsByIds(userId: string, sessionId: string, blobIds: string[]): Promise<StoredBlob[]> {
    if (!this.icebergClient || blobIds.length === 0) {
      return [];
    }

    try {
      const rows = await this.icebergClient.queryBlobs(userId, sessionId, blobIds);
      return rows.map(r => ({
        id: r.id,
        userId: r.user_id,
        sessionId: r.session_id,
        messageId: r.message_id,
        blobType: r.blob_type as 'image' | 'audio',
        mimeType: r.mime_type,
        data: r.data,
        caption: r.caption ?? undefined,
        timestamp: r.timestamp,
      }));
    } catch (error) {
      this.logger.error({ error, blobIds }, 'Failed to retrieve blobs');
      return [];
    }
  }
}
