/**
 * Forward-compat shim for @mosaicstack/telemetry-client-js.
 *
 * @mosaicstack/telemetry-client-js is not yet published to the Gitea npm
 * registry (returns 404 as of 2026-04-04). This shim mirrors the minimal
 * interface that the real client will expose so that all telemetry wiring
 * can be implemented now and swapped for the real package when it lands.
 *
 * TODO: replace this shim with `import { ... } from '@mosaicstack/telemetry-client-js'`
 *       once the package is published.
 */

export interface TelemetryEvent {
  /** Event name / category */
  name: string;
  /** Arbitrary key-value payload */
  properties?: Record<string, unknown>;
  /** ISO timestamp — defaults to now if omitted */
  timestamp?: string;
}

/**
 * Minimal interface mirroring what @mosaicstack/telemetry-client-js exposes.
 */
export interface TelemetryClient {
  /** Initialise the client (must be called before captureEvent / upload). */
  init(options: TelemetryClientOptions): void;
  /** Queue a telemetry event for eventual upload. */
  captureEvent(event: TelemetryEvent): void;
  /**
   * Flush all queued events to the remote endpoint.
   * In dry-run mode the client must print instead of POST.
   */
  upload(): Promise<void>;
  /** Flush and release resources. */
  shutdown(): Promise<void>;
}

export interface TelemetryClientOptions {
  /** Remote OTLP / telemetry endpoint URL */
  endpoint?: string;
  /** Dry-run: print payloads instead of posting */
  dryRun?: boolean;
  /** Extra labels attached to every event */
  labels?: Record<string, string>;
}

// ─── Shim implementation ─────────────────────────────────────────────────────

/**
 * A no-network shim that buffers events and pretty-prints them in dry-run mode.
 * This is the ONLY implementation used until the real package is published.
 */
class TelemetryClientShim implements TelemetryClient {
  private options: TelemetryClientOptions = {};
  private queue: TelemetryEvent[] = [];

  init(options: TelemetryClientOptions): void {
    // Merge options without clearing the queue — buffered events must survive
    // re-initialisation so that `telemetry upload` can flush them.
    this.options = options;
  }

  captureEvent(event: TelemetryEvent): void {
    this.queue.push({
      ...event,
      timestamp: event.timestamp ?? new Date().toISOString(),
    });
  }

  async upload(): Promise<void> {
    const isDryRun = this.options.dryRun !== false; // dry-run is default

    if (isDryRun) {
      console.log('[dry-run] telemetry upload — no network call made');
      for (const evt of this.queue) {
        console.log(JSON.stringify({ ...evt, labels: this.options.labels }, null, 2));
      }
      this.queue = [];
      return;
    }

    // Real upload path — placeholder until real client replaces this shim.
    const endpoint = this.options.endpoint;
    if (!endpoint) {
      console.log('[dry-run] telemetry upload — no endpoint configured, no network call made');
      for (const evt of this.queue) {
        console.log(JSON.stringify(evt, null, 2));
      }
      this.queue = [];
      return;
    }

    // The real client is not yet published — throw so callers know no data
    // was actually sent. This prevents the CLI from marking an upload as
    // successful when only the shim is present.
    // TODO: remove once @mosaicstack/telemetry-client-js replaces this shim.
    throw new Error(
      `[shim] telemetry-client-js is not yet available — cannot POST to ${endpoint}. ` +
        'Remote upload is supported only after the mosaicstack.dev endpoint is live.',
    );
  }

  async shutdown(): Promise<void> {
    await this.upload();
  }
}

/** Singleton client instance. */
let _client: TelemetryClient | null = null;

/** Return (or lazily create) the singleton telemetry client. */
export function getTelemetryClient(): TelemetryClient {
  if (!_client) {
    _client = new TelemetryClientShim();
  }
  return _client;
}

/**
 * Replace the singleton — used in tests to inject a mock.
 */
export function setTelemetryClient(client: TelemetryClient): void {
  _client = client;
}

/**
 * Reset the singleton to null (useful in tests).
 */
export function resetTelemetryClient(): void {
  _client = null;
}
