import type { ChatMessage } from './runtime';

export type StreamplaceChatOptions = {
  endpoint?: string;
  wantedCollections?: string[];
  wantedDids?: string[];
  WebSocketImpl?: typeof WebSocket;
};

export type StreamplaceChatEvent = {
  source: 'jetstream';
  raw: unknown;
  message: ChatMessage;
};

type CommitEnvelope = {
  kind?: string;
  did?: string;
  time_us?: number;
  commit?: {
    operation?: string;
    collection?: string;
    record?: {
      text?: string;
      message?: string;
      createdAt?: string;
    };
  };
};

const DEFAULT_ENDPOINT = 'wss://jetstream2.us-east.bsky.network/subscribe';

export class StreamplaceChatAdapter {
  private readonly endpoint: string;
  private readonly wantedCollections: string[];
  private readonly wantedDids: string[];
  private readonly WebSocketImpl: typeof WebSocket;

  constructor(options: StreamplaceChatOptions = {}) {
    this.endpoint = options.endpoint ?? DEFAULT_ENDPOINT;
    this.wantedCollections = options.wantedCollections ?? [process.env.STREAMPLACE_CHAT_NSID ?? 'app.bsky.feed.post'];
    this.wantedDids = options.wantedDids ?? [];
    this.WebSocketImpl = options.WebSocketImpl ?? WebSocket;
  }

  createUrl() {
    const url = new URL(this.endpoint);
    for (const collection of this.wantedCollections) {
      url.searchParams.append('wantedCollections', collection);
    }
    for (const did of this.wantedDids) {
      url.searchParams.append('wantedDids', did);
    }
    return url.toString();
  }

  extractChatEvent(payload: string): StreamplaceChatEvent | undefined {
    const data = JSON.parse(payload) as CommitEnvelope;
    if (data.kind !== 'commit') return undefined;
    if (data.commit?.operation !== 'create') return undefined;
    const collection = data.commit?.collection;
    if (!collection || !this.wantedCollections.includes(collection)) return undefined;

    const text = (data.commit.record?.text ?? data.commit.record?.message)?.trim();
    if (!text) return undefined;

    return {
      source: 'jetstream',
      raw: data,
      message: {
        author: data.did ?? 'unknown',
        text,
        timestamp:
          data.commit.record?.createdAt ??
          (typeof data.time_us === 'number' ? new Date(Math.floor(data.time_us / 1000)).toISOString() : new Date().toISOString()),
      },
    };
  }

  subscribe(onEvent: (event: StreamplaceChatEvent) => void) {
    const ws = new this.WebSocketImpl(this.createUrl());

    ws.onmessage = (event) => {
      const text = typeof event.data === 'string' ? event.data : String(event.data);
      const parsed = this.extractChatEvent(text);
      if (parsed) onEvent(parsed);
    };

    return ws;
  }
}
