/**
 * Tauri adapter for {@link DesktopServerGateway} (ticket #68). One of the only
 * places that calls `invoke()`; features reach it exclusively through the port.
 *
 * Commands and payload keys are camelCase, matching the backend DTO convention.
 * `save_server_exposure_settings` / `preview_server_exposure_settings` take the
 * settings directly under a `settings` key (no `request` wrapper).
 *
 * **Polling note.** The backend exposes no status *event* — `embedded_server.rs`
 * emits nothing — so `onStatusChanged` is implemented here by polling
 * `embedded_server_status`. That keeps the port shape push-like (and swappable
 * for a real event later) while the polling schedule stays an adapter detail.
 */

import { invoke } from "@tauri-apps/api/core";

import type {
  EmbeddedServerStatus,
  ServerExposurePreview,
  ServerExposureSettings,
  Unsubscribe,
} from "@/domain";
import type { DesktopServerGateway } from "@/ports";

/** Status poll interval (ms). Cheap, in-process command; transitions are coarse. */
const STATUS_POLL_MS = 2000;

export class TauriDesktopServerGateway implements DesktopServerGateway {
  constructor(private readonly pollMs: number = STATUS_POLL_MS) {}

  getExposureSettings(): Promise<ServerExposureSettings> {
    return invoke<ServerExposureSettings>("get_server_exposure_settings");
  }

  async saveExposureSettings(settings: ServerExposureSettings): Promise<void> {
    await invoke("save_server_exposure_settings", { settings });
  }

  previewExposure(
    settings: ServerExposureSettings,
  ): Promise<ServerExposurePreview> {
    return invoke<ServerExposurePreview>("preview_server_exposure_settings", {
      settings,
    });
  }

  status(): Promise<EmbeddedServerStatus> {
    return invoke<EmbeddedServerStatus>("embedded_server_status");
  }

  start(): Promise<EmbeddedServerStatus> {
    return invoke<EmbeddedServerStatus>("embedded_server_start");
  }

  stop(): Promise<EmbeddedServerStatus> {
    return invoke<EmbeddedServerStatus>("embedded_server_stop");
  }

  async onStatusChanged(
    handler: (status: EmbeddedServerStatus) => void,
  ): Promise<Unsubscribe> {
    let stopped = false;
    const timer = setInterval(() => {
      void this.status().then(
        (status) => {
          // The unsubscribe may land while a poll is in flight; drop late results
          // so a torn-down consumer never gets called.
          if (!stopped) handler(status);
        },
        () => {
          // A failed poll is not a status: swallow it and let the next tick try
          // again, rather than tearing the subscription down.
        },
      );
    }, this.pollMs);

    return () => {
      stopped = true;
      clearInterval(timer);
    };
  }
}
