import { randomUUID } from 'node:crypto';
import { getCollection } from '../../lib/datastore.js';
import type {
  EventSubscriptionDoc,
  CreateEventSubscriptionInput,
  UpdateEventSubscriptionInput,
  ListEventSubscriptionsQuery,
  DlqEntryDoc,
  ListDlqQuery,
} from './types.js';

function subsCollection() {
  return getCollection<EventSubscriptionDoc>('event_subscriptions', '/productId');
}

function dlqCollection() {
  return getCollection<DlqEntryDoc>('event_dlq', '/productId');
}

// ── Subscription CRUD ───────────────────────────────────────

export async function create(
  productId: string,
  input: CreateEventSubscriptionInput,
  createdBy: string
): Promise<EventSubscriptionDoc> {
  const now = new Date().toISOString();
  const doc: EventSubscriptionDoc = {
    id: `esub_${randomUUID()}`,
    productId,
    topic: input.topic,
    handlerType: input.handlerType,
    status: 'active',
    config: input.config,
    filterExpression: input.filterExpression,
    description: input.description,
    createdBy,
    createdAt: now,
    updatedAt: now,
  };
  return subsCollection().create(doc);
}

export async function getById(
  id: string,
  productId: string
): Promise<EventSubscriptionDoc | undefined> {
  try {
    const doc = await subsCollection().findById(id, productId);
    return doc ?? undefined;
  } catch {
    return undefined;
  }
}

export async function list(
  productId: string,
  query: ListEventSubscriptionsQuery
): Promise<EventSubscriptionDoc[]> {
  return subsCollection().findMany({
    filter: {
      productId,
      ...(query.topic ? { topic: query.topic } : {}),
      ...(query.handlerType ? { handlerType: query.handlerType } : {}),
      ...(query.status ? { status: query.status } : {}),
    },
    sort: { createdAt: -1 },
    limit: query.limit,
  });
}

export async function update(
  id: string,
  productId: string,
  input: UpdateEventSubscriptionInput
): Promise<EventSubscriptionDoc | undefined> {
  const existing = await getById(id, productId);
  if (!existing) return undefined;

  const updates: Partial<EventSubscriptionDoc> = {
    ...(input.status !== undefined && { status: input.status }),
    ...(input.config !== undefined && { config: input.config }),
    ...(input.filterExpression !== undefined && { filterExpression: input.filterExpression }),
    ...(input.description !== undefined && { description: input.description }),
    updatedAt: new Date().toISOString(),
  };

  return subsCollection().update(id, productId, updates);
}

export async function remove(id: string, productId: string): Promise<boolean> {
  try {
    await subsCollection().delete(id, productId);
    return true;
  } catch {
    return false;
  }
}

// ── Active subscription lookup by topic ─────────────────────

export async function findActiveByTopic(
  productId: string,
  topic: string
): Promise<EventSubscriptionDoc[]> {
  return subsCollection().findMany({
    filter: { productId, topic, status: 'active' },
  });
}

// ── Dead-Letter Queue ───────────────────────────────────────

export async function createDlqEntry(entry: DlqEntryDoc): Promise<DlqEntryDoc> {
  return dlqCollection().create(entry);
}

export async function listDlq(productId: string, query: ListDlqQuery): Promise<DlqEntryDoc[]> {
  return dlqCollection().findMany({
    filter: {
      productId,
      ...(query.topic ? { topic: query.topic } : {}),
    },
    sort: { createdAt: -1 },
    limit: query.limit,
  });
}

export async function getDlqEntry(id: string, productId: string): Promise<DlqEntryDoc | undefined> {
  try {
    const doc = await dlqCollection().findById(id, productId);
    return doc ?? undefined;
  } catch {
    return undefined;
  }
}

export async function deleteDlqEntry(id: string, productId: string): Promise<boolean> {
  try {
    await dlqCollection().delete(id, productId);
    return true;
  } catch {
    return false;
  }
}

export async function purgeDlq(productId: string): Promise<number> {
  const entries = await dlqCollection().findMany({ filter: { productId }, limit: 1000 });
  let count = 0;
  for (const entry of entries) {
    try {
      await dlqCollection().delete(entry.id, productId);
      count++;
    } catch {
      // best-effort
    }
  }
  return count;
}
