// Lokaler React-Query-Adapter.
//
// Vorher: Express-Backend mit /api/* Routen.
// Jetzt: alle "API"-Aufrufe gehen durch IndexedDB. Wir behalten die alte
// fetch()-ähnliche Signatur, damit die Pages unverändert bleiben.
//
// Unterstützte Endpoints:
//   GET    /api/settings
//   PATCH  /api/settings              body: Partial<Settings>
//   GET    /api/recordings
//   GET    /api/recordings/:id
//   POST   /api/recordings            body: InsertRecording
//   PATCH  /api/recordings/:id        body: Partial<Recording>
//   DELETE /api/recordings/:id
//   GET    /api/beats
//
// uploadAudio(id, blob) → schreibt direkt in IndexedDB.

import { QueryClient, QueryFunction } from "@tanstack/react-query";
import * as LDB from "./local-db";

export const API_BASE = ""; // legacy export, ungenutzt

class HttpError extends Error {
  status: number;
  constructor(status: number, message: string) {
    super(`${status}: ${message}`);
    this.status = status;
  }
}

function ok<T>(data: T): Response {
  return new Response(JSON.stringify(data), {
    status: 200,
    headers: { "Content-Type": "application/json" },
  });
}

function notFound(msg = "not found"): never {
  throw new HttpError(404, msg);
}

// Parser: zerlegt "/api/recordings/12/audio" in Segmente nach dem /api/.
function parsePath(url: string): string[] {
  const clean = url.split("?")[0].replace(/^\/+/, "");
  if (!clean.startsWith("api/")) return [];
  return clean.substring(4).split("/").filter(Boolean);
}

async function route(method: string, url: string, body?: unknown): Promise<Response> {
  const seg = parsePath(url);
  // /api/settings
  if (seg[0] === "settings" && seg.length === 1) {
    if (method === "GET") return ok(await LDB.getSettings());
    if (method === "PATCH") return ok(await LDB.patchSettings((body ?? {}) as Partial<import("@shared/schema").Settings>));
  }
  // /api/recordings
  if (seg[0] === "recordings" && seg.length === 1) {
    if (method === "GET") return ok(await LDB.listRecordings());
    if (method === "POST") {
      const rec = await LDB.createRecording((body ?? {}) as LDB.CreateRecordingInput);
      return ok(rec);
    }
  }
  // /api/recordings/:id (und :id/audio)
  if (seg[0] === "recordings" && seg.length >= 2) {
    const id = Number(seg[1]);
    if (Number.isNaN(id)) notFound("invalid id");
    if (seg.length === 2) {
      if (method === "GET") {
        const r = await LDB.getRecording(id);
        if (!r) notFound();
        return ok(r);
      }
      if (method === "PATCH") {
        const r = await LDB.updateRecording(id, (body ?? {}) as Partial<import("@shared/schema").Recording>);
        if (!r) notFound();
        return ok(r);
      }
      if (method === "DELETE") {
        await LDB.deleteRecording(id);
        return ok({ ok: true });
      }
    }
  }
  // /api/beats
  if (seg[0] === "beats" && seg.length === 1) {
    if (method === "GET") return ok(LDB.BEATS);
  }
  throw new HttpError(405, `no route for ${method} /${seg.join("/")}`);
}

// ---------- Public API (signaturkompatibel zur alten queryClient.ts) ----------

export async function uploadAudio(id: number, blob: Blob): Promise<Response> {
  // Audio-Blob ins IndexedDB schreiben. Wir aktualisieren auch fileSize, falls
  // beim Anlegen noch unbekannt war (z. B. weil onstop nach metadata.created kam).
  await LDB.putAudio(id, blob);
  await LDB.updateRecording(id, { fileSize: blob.size });
  return ok({ ok: true, size: blob.size });
}

export async function apiRequest(method: string, url: string, data?: unknown): Promise<Response> {
  return route(method.toUpperCase(), url, data);
}

type UnauthorizedBehavior = "returnNull" | "throw";
export const getQueryFn: <T>(options: { on401: UnauthorizedBehavior }) => QueryFunction<T> =
  () =>
  async ({ queryKey }) => {
    // queryKey ist z. B. ["/api/recordings"] oder ["/api/recordings", 5].
    const url = queryKey
      .map((k) => (typeof k === "string" || typeof k === "number" ? String(k) : ""))
      .filter(Boolean)
      .join("/")
      .replace(/\/{2,}/g, "/");
    const res = await route("GET", url);
    return (await res.json()) as any;
  };

export const queryClient = new QueryClient({
  defaultOptions: {
    queries: {
      queryFn: getQueryFn({ on401: "throw" }),
      refetchInterval: false,
      refetchOnWindowFocus: false,
      staleTime: Infinity,
      retry: false,
    },
    mutations: {
      retry: false,
    },
  },
});
