/**
 * webcamClient.js
 * Kapselt alle Zugriffe auf den WebCam-Service.
 * Basis-URL kommt von aussen (WEBCAM_URL) — kein Hartkodieren hier.
 *
 * Verwendung:
 *   import { WebcamClient } from './webcamClient.js';
 *   const wc = new WebcamClient(process.env.WEBCAM_URL);
 *   const cameras = await wc.getCameras();
 *   const response = await wc.getSnapshot('cam0');   // Response-Objekt, JPEG-Body
 */

const TIMEOUT_MS = 15_000;

export class WebcamClient {
  /** @param {string} baseUrl  z.B. "http://appRobotWebcam:8444" */
  constructor(baseUrl) {
    if (!baseUrl) throw new Error('WebcamClient: baseUrl ist erforderlich');
    this.baseUrl = baseUrl.replace(/\/$/, '');
  }

  /**
   * Liste aller Kameras mit Metadaten.
   * `calibrationUrl` ist enthalten, wenn eine .npz unter data/calibration/{id}/ liegt.
   * @returns {Promise<{cameras: CameraMeta[]}>}
   */
  async getCameras() {
    const res = await this.#get('/api/cameras');
    if (!res.ok) throw new Error(`getCameras: HTTP ${res.status}`);
    return res.json();
  }

  /**
   * HD-JPEG einer Kamera als fetch-Response.
   * Caller kann res.body direkt pipen (kein Buffering).
   * @param {string} id   Kamera-ID, z.B. "cam0"
   * @param {boolean} hires  true = /hires (Default), false = Live-Auflösung
   * @returns {Promise<Response>}
   */
  async getSnapshot(id, hires = true) {
    const path = hires ? `/api/snapshot/${id}/hires` : `/api/snapshot/${id}`;
    const res = await this.#get(path);
    if (!res.ok) throw new Error(`getSnapshot(${id}): HTTP ${res.status}`);
    return res;
  }

  /**
   * Kalibrierungsdatei (.npz) als ArrayBuffer.
   * Kann direkt an den BodyTracker weitergereicht werden.
   * Wirft bei 404 (noch keine Kalibrierung vorhanden).
   * @param {string} id  Kamera-ID
   * @returns {Promise<ArrayBuffer>}
   */
  async getCalibration(id) {
    const res = await this.#get(`/api/cameras/${id}/calibration`);
    if (res.status === 404) throw new Error(`Keine Kalibrierung für Kamera "${id}"`);
    if (!res.ok) throw new Error(`getCalibration(${id}): HTTP ${res.status}`);
    return res.arrayBuffer();
  }

  /**
   * Gesundheitsstatus des WebCam-Service inkl. Kamera-Zustände.
   * @returns {Promise<{status: string, cameras: CameraState[]}>}
   */
  async health() {
    const res = await this.#get('/health');
    if (!res.ok) throw new Error(`health: HTTP ${res.status}`);
    return res.json();
  }

  // ── intern ──────────────────────────────────────────────────────────────────

  #get(path, options = {}) {
    const url = `${this.baseUrl}${path}`;
    return fetch(url, {
      ...options,
      signal: AbortSignal.timeout(TIMEOUT_MS),
    });
  }
}

/**
 * @typedef {Object} CameraMeta
 * @property {string}      id
 * @property {string}      name
 * @property {string}      position        "front" | "left" | "right"
 * @property {boolean}     stream
 * @property {boolean}     hires
 * @property {string}      encode
 * @property {string|null} mseCodec
 * @property {string}      note            Hardware-Serial (stable key)
 * @property {string|null} [calibrationUrl]  vorhanden wenn .npz existiert
 *
 * @typedef {Object} CameraState
 * @property {string}  id
 * @property {string}  name
 * @property {string}  device
 * @property {string}  state   "running" | "idle" | "stopping"
 * @property {boolean} hasFrame
 */
