// src/transport/FederationTransportAdapter.ts
//
// Adapter that bridges @open-matrix/federation Transport to Matrix-3 ITransportAdapter
//

import type { ITransportAdapter, TransportHandler, IWireTopicInfo } from '../engine/remoting/ITransportAdapter';
import type { ITransportMessageMeta } from '../engine/remoting/ITransportMessageMeta';
import type { Transport, MessageHandler } from '@open-matrix/federation';

/**
 * Adapter that wraps a federation Transport to implement ITransportAdapter.
 *
 * This bridges the interface differences:
 * - Federation Transport: publish(topic, payload: string), subscribe handler receives (topic, payload: string)
 * - ITransportAdapter: publish(topic, payload: unknown), subscribe handler receives (payload, meta)
 */
export class FederationTransportAdapter implements ITransportAdapter {
  private readonly _inner: Transport;

  constructor(transport: Transport) {
    this._inner = transport;
  }

  /**
   * The root of the underlying transport, if available.
   * Used by RequestReply for cross-root federation detection.
   */
  get root(): string {
    const candidate = this._inner as { readonly root?: unknown; readonly realm?: unknown };
    if (typeof candidate.root === 'string') return candidate.root;
    if (typeof candidate.realm === 'string') return candidate.realm;
    return '';
  }

  /** @deprecated Use `root`. */
  get realm(): string {
    return this.root;
  }

  /**
   * Get the underlying client for cross-root messaging.
   */
  getUnderlyingClient(): unknown {
    return this._inner;
  }

  async disconnect(): Promise<void> {
    const candidate = this._inner as { disconnect?: unknown };
    if (typeof candidate.disconnect === 'function') {
      await candidate.disconnect();
    }
  }

  /**
   * Get the topic prefix (always empty for this adapter).
   */
  get topicPrefix(): string {
    return '';
  }

  /**
   * Get wire topic info for debugging.
   */
  getWireTopicInfo(topic: string): IWireTopicInfo {
    return {
      appTopic: topic,
      wireTopic: topic,
      prefix: '',
    };
  }

  /**
   * Publish a message.
   * Converts unknown payload to string for federation transport.
   */
  publish(topic: string, payload?: unknown): void {
    const payloadStr = payload === undefined ? '' : JSON.stringify(payload);
    this._inner.publish(topic, payloadStr);
  }

  /**
   * Subscribe to a topic.
   * Wraps the handler to convert federation format to ITransportAdapter format.
   */
  subscribe(topic: string, handler: TransportHandler): () => void {
    const wrappedHandler: MessageHandler = (receivedTopic: string, payloadStr: string, meta) => {
      // Parse the string payload back to object
      let payload: unknown;
      try {
        payload = payloadStr ? JSON.parse(payloadStr) : undefined;
      } catch {
        // If not JSON, use the string as-is
        payload = payloadStr;
      }

      // Create ITransportMessageMeta from federation meta
      const transportMeta: ITransportMessageMeta = {
        topic: receivedTopic,
        ts: Date.now(),
      };

      // Call the handler with Matrix-3 format
      handler(payload, transportMeta);
    };

    return this._inner.subscribe(topic, wrappedHandler);
  }
}

/**
 * Create an adapter-wrapped transport from a federation Transport.
 */
export function wrapFederationTransport(transport: Transport): ITransportAdapter {
  return new FederationTransportAdapter(transport);
}
