import { randomUUID } from "node:crypto";

import {
  buildHwlabKafkaProducerMessage,
  defaultKafkaFactory,
  projectAgentRunKafkaEventToHwlabEvent,
} from "../cloud/kafka-event-bridge.ts";
import type { WorkbenchEventPublisher } from "./contracts.ts";

export function createWorkbenchKafkaEventPublisher(options: {
  env: Record<string, string | undefined>;
  kafkaFactory?: typeof defaultKafkaFactory;
  now?: () => string;
}): WorkbenchEventPublisher {
  const config = workbenchKafkaPublisherConfig(options.env);
  const kafka = (options.kafkaFactory ?? defaultKafkaFactory)(config);
  const producer = kafka.producer({ allowAutoTopicCreation: false });
  let ready: Promise<unknown> | null = null;
  const now = options.now ?? (() => new Date().toISOString());

  return {
    async publish(input) {
      ready ??= producer.connect();
      await ready;
      const createdAt = timestamp(input.event.createdAt) ?? now();
      const sourceEventId = text(input.event.sourceEventId) || `workbench:${input.traceId}:${randomUUID()}`;
      const sourceType = text(input.event.type) || "backend_status";
      const source = {
        schema: "hwlab.workbench.activity.v1",
        eventId: sourceEventId,
        traceId: input.traceId,
        sessionId: input.sessionId,
        event: {
          id: sourceEventId,
          type: sourceType,
          traceId: input.traceId,
          createdAt,
          payload: {
            ...input.event,
            type: sourceType,
            traceId: input.traceId,
            sessionId: input.sessionId,
          },
        },
      };
      const envelope = projectAgentRunKafkaEventToHwlabEvent(source, {
        source: `${config.clientId}:workbench-activity`,
        producedAt: createdAt,
      });
      if (!envelope) throw codedError("workbench_kafka_event_projection_failed", "Workbench event could not be projected to hwlab.event.v1.");
      envelope.event.source = "hwlab.workbench.activity";
      envelope.diagnostics.projectedBy = "hwlab-workbench-kafka-event-publisher";
      await producer.send({ topic: config.hwlabTopic, messages: [buildHwlabKafkaProducerMessage(envelope)] });
      return { published: true, topic: config.hwlabTopic, traceId: input.traceId, sessionId: input.sessionId, sourceEventId, valuesPrinted: false };
    },
    async close() {
      if (!ready) return;
      await ready.catch(() => undefined);
      await producer.disconnect();
    },
  };
}

function workbenchKafkaPublisherConfig(env: Record<string, string | undefined>) {
  const brokers = csv(env.HWLAB_KAFKA_BOOTSTRAP_SERVERS);
  const hwlabTopic = text(env.HWLAB_KAFKA_EVENT_TOPIC);
  const clientId = text(env.HWLAB_KAFKA_CLIENT_ID);
  const missing = [
    brokers.length === 0 ? "HWLAB_KAFKA_BOOTSTRAP_SERVERS" : null,
    hwlabTopic ? null : "HWLAB_KAFKA_EVENT_TOPIC",
    clientId ? null : "HWLAB_KAFKA_CLIENT_ID",
  ].filter(Boolean);
  if (missing.length > 0) throw codedError("workbench_kafka_publisher_unconfigured", `Workbench event publisher configuration is incomplete: ${missing.join(", ")}`);
  return {
    brokers,
    hwlabTopic,
    clientId,
    dnsServers: csv(env.HWLAB_KAFKA_DNS_SERVERS),
    dnsSearchDomains: csv(env.HWLAB_KAFKA_DNS_SEARCH_DOMAINS),
  };
}

function csv(value: unknown): string[] { return text(value).split(",").map((item) => item.trim()).filter(Boolean); }
function text(value: unknown): string { return String(value ?? "").trim(); }
function timestamp(value: unknown): string | null { const result = text(value); return result && Number.isFinite(Date.parse(result)) ? new Date(Date.parse(result)).toISOString() : null; }
function codedError(code: string, message: string) { return Object.assign(new Error(message), { code }); }
