/* ================================================================
   Laufzeitumgebung und Ablage von Zugangsdaten
   ================================================================
   Die App läuft in drei Umgebungen (COMPANION_APP_ARCHITECTURE.md §1):
   nativ per Capacitor (iOS/Android), als Iframe im HA-Dashboard und im
   normalen Browser. Der Unterschied betrifft nur zwei Dinge - wo das Token
   liegt und welche Basis-URL gilt -, deshalb ist genau das hier gekapselt
   und der Rest der Datenschicht umgebungsblind. */

export type Umgebung = "capacitor" | "iframe" | "browser";

export function umgebungErkennen(): Umgebung {
  if (typeof window === "undefined") return "browser";

  /* Capacitor meldet sich über ein globales Objekt an. Bewusst defensiv
     gelesen: das Paket ist hier (noch) keine Abhängigkeit, wir prüfen nur,
     ob die Laufzeit es bereitstellt. */
  const cap = (window as unknown as { Capacitor?: { isNativePlatform?: () => boolean } }).Capacitor;
  if (cap?.isNativePlatform?.()) return "capacitor";

  /* window.top !== window heißt: wir stecken in einem Iframe. Der Zugriff
     kann bei fremder Herkunft werfen - dann sind wir erst recht eingebettet. */
  try {
    if (window.top !== window.self) return "iframe";
  } catch {
    return "iframe";
  }

  return "browser";
}

/* ------------------------------------------------------------- Ablage */

/** Minimale, asynchrone Schlüssel-Wert-Ablage. Async, weil die native
    Variante (Keychain/Keystore über Capacitor Preferences) asynchron ist -
    lieber alle Aufrufer von Anfang an asynchron als später umbauen. */
export interface Ablage {
  lesen(schluessel: string): Promise<string | null>;
  schreiben(schluessel: string, wert: string): Promise<void>;
  loeschen(schluessel: string): Promise<void>;
}

/** localStorage-Variante für Browser und Iframe. Alle Zugriffe abgesichert:
    im privaten Modus und in Iframes mit blockierten Drittanbieter-Daten
    wirft schon der bloße Zugriff auf localStorage. */
export class BrowserAblage implements Ablage {
  #speicher = new Map<string, string>();

  #ls(): Storage | null {
    try {
      return window.localStorage;
    } catch {
      return null;
    }
  }

  async lesen(schluessel: string): Promise<string | null> {
    const ls = this.#ls();
    if (!ls) return this.#speicher.get(schluessel) ?? null;
    try {
      return ls.getItem(schluessel);
    } catch {
      return this.#speicher.get(schluessel) ?? null;
    }
  }

  async schreiben(schluessel: string, wert: string): Promise<void> {
    this.#speicher.set(schluessel, wert);
    try {
      this.#ls()?.setItem(schluessel, wert);
    } catch {
      /* Nur im Arbeitsspeicher - besser als ein Absturz. */
    }
  }

  async loeschen(schluessel: string): Promise<void> {
    this.#speicher.delete(schluessel);
    try {
      this.#ls()?.removeItem(schluessel);
    } catch {
      /* siehe oben */
    }
  }
}

/** Wird beim App-Start durch die native Umsetzung ersetzt (Keychain unter
    iOS, Keystore unter Android). Bis dahin gilt die Browser-Variante. */
let aktiveAblage: Ablage = new BrowserAblage();

export function ablageSetzen(ablage: Ablage): void {
  aktiveAblage = ablage;
}

export function ablage(): Ablage {
  return aktiveAblage;
}

/* --------------------------------------------------------- Zugangsdaten */

const SCHLUESSEL_BASIS = "dm360.basis_url";
const SCHLUESSEL_TOKEN = "dm360.token";

export interface Zugang {
  /** Basis-URL ohne abschließenden Schrägstrich, z. B. https://audi.datametric360.app */
  basisUrl: string;
  /** Long-Lived Access Token aus dem HA-Benutzerprofil. */
  token: string;
}

export async function zugangLesen(): Promise<Zugang | null> {
  const a = ablage();
  const [basisUrl, token] = await Promise.all([a.lesen(SCHLUESSEL_BASIS), a.lesen(SCHLUESSEL_TOKEN)]);
  if (!basisUrl || !token) return null;
  return { basisUrl, token };
}

export async function zugangSpeichern(zugang: Zugang): Promise<void> {
  const a = ablage();
  await Promise.all([
    a.schreiben(SCHLUESSEL_BASIS, basisUrlNormalisieren(zugang.basisUrl)),
    a.schreiben(SCHLUESSEL_TOKEN, zugang.token),
  ]);
}

export async function zugangVerwerfen(): Promise<void> {
  const a = ablage();
  await Promise.all([a.loeschen(SCHLUESSEL_BASIS), a.loeschen(SCHLUESSEL_TOKEN)]);
}

/** Schneidet abschließende Schrägstriche ab und ergänzt fehlendes Schema.
    Ohne das entstehen sonst Adressen wie "https://host//api/states". */
export function basisUrlNormalisieren(eingabe: string): string {
  let url = eingabe.trim();
  if (!/^https?:\/\//i.test(url)) url = `https://${url}`;
  return url.replace(/\/+$/, "");
}

/** WebSocket-Adresse zur Basis-URL: http->ws, https->wss. */
export function websocketUrl(basisUrl: string): string {
  return `${basisUrl.replace(/^http/i, "ws")}/api/websocket`;
}
