import { Hono } from 'hono';
import type { CryptoProvider } from '@shade/core';
import { createInboxRoutes, type InboxRoutesOptions } from './routes.js';
import { MemoryInboxStore } from './memory-store.js';
import type { InboxStore } from './store.js';
import { InboxServerEvents } from './events.js';
import { createBlobRoutes, type BlobRoutesOptions } from './blob-routes.js';
import { MemoryBlobStore } from './memory-blob-store.js';
import type { BlobStore } from './blob-store.js';

export { createInboxRoutes } from './routes.js';
export type { InboxRoutesOptions } from './routes.js';
export { MemoryInboxStore } from './memory-store.js';
export type { InboxStore } from './store.js';
export {
  InboxServerEvents,
  shortHash as inboxShortHash,
} from './events.js';
export type {
  InboxServerEvent,
  InboxServerEventName,
  InboxServerEventMap,
  InboxServerEventListener,
} from './events.js';
export { InboxPruneTask } from './cleanup.js';
export {
  computeMsgId,
  isValidMsgId,
  constantTimeStringEqual,
} from './msg-id.js';
export {
  DEFAULT_INBOX_QUOTA,
  clampTtl,
} from './quota.js';
export type { InboxQuotaConfig } from './quota.js';
export { createBridgeRoutes } from './bridge.js';
export type { BridgeRoutesOptions, BridgeKind } from './bridge.js';
export { PresenceTracker } from './presence.js';
export type { TrackedBridgeKind } from './presence.js';
export { BridgeDeliveryLog } from './bridge-delivery-log.js';
export type { BridgeDeliveryLogOptions } from './bridge-delivery-log.js';
export { createBlobRoutes, DEFAULT_MAX_BLOB_BYTES } from './blob-routes.js';
export type { BlobRoutesOptions } from './blob-routes.js';
export { MemoryBlobStore } from './memory-blob-store.js';
export type { BlobStore, BlobSlotRecord, PutBlobResult } from './blob-store.js';

/**
 * Create a standalone Shade Inbox Server.
 *
 *   const crypto = new SubtleCryptoProvider();
 *   const inbox = createInboxServer({ crypto });
 *   export default { port: 3901, fetch: inbox.fetch };
 *
 * Or compose into an existing Hono app:
 *   const app = new Hono();
 *   app.route('/', createInboxServer({ crypto }));
 */
export function createInboxServer(
  options: {
    crypto: CryptoProvider;
    store?: InboxStore;
    disableRateLimit?: boolean;
    events?: InboxServerEvents;
    /**
     * V4.9 — when supplied, mounts the encrypted-blob primitive
     * (`/v1/blob/<slotId>`) on the same Hono app. Pass `null` to
     * explicitly opt out; omit to default to a `MemoryBlobStore`.
     */
    blobStore?: BlobStore | null;
    blobOptions?: Pick<BlobRoutesOptions, 'maxBlobBytes'>;
  } & Pick<InboxRoutesOptions, 'observability' | 'quota' | 'bridgeDeliveryLog'>,
): Hono {
  const store = options.store ?? new MemoryInboxStore();
  const routesOptions: InboxRoutesOptions = {};
  if (options.disableRateLimit !== undefined) routesOptions.disableRateLimit = options.disableRateLimit;
  if (options.events !== undefined) routesOptions.events = options.events;
  if (options.observability !== undefined) routesOptions.observability = options.observability;
  if (options.quota !== undefined) routesOptions.quota = options.quota;
  if (options.bridgeDeliveryLog !== undefined) routesOptions.bridgeDeliveryLog = options.bridgeDeliveryLog;

  const inboxApp = createInboxRoutes(store, options.crypto, routesOptions);

  // Compose with the blob primitive unless explicitly disabled. The
  // blob routes share the same Hono app so a single port serves both.
  if (options.blobStore === null) return inboxApp;
  const blobStore = options.blobStore ?? new MemoryBlobStore();
  const blobRoutesOptions: BlobRoutesOptions = {};
  if (options.disableRateLimit !== undefined) blobRoutesOptions.disableRateLimit = options.disableRateLimit;
  if (options.observability !== undefined) blobRoutesOptions.observability = options.observability;
  if (options.blobOptions?.maxBlobBytes !== undefined) {
    blobRoutesOptions.maxBlobBytes = options.blobOptions.maxBlobBytes;
  }
  const blobApp = createBlobRoutes(blobStore, options.crypto, blobRoutesOptions);

  const composed = new Hono();
  composed.route('/', inboxApp);
  composed.route('/', blobApp);
  return composed;
}
