/**
 * Centralized API client for all backend communication.
 *
 * Every fetch call in the app should go through here so that
 * endpoint URLs live in one place and are easy to update
 * (e.g. when adding multi-node support with /api/nodes/{id}/...).
 */

// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------

async function json<T>(res: Response): Promise<T> {
  if (!res.ok) throw new Error(await res.text());
  return res.json();
}

// ---------------------------------------------------------------------------
// Pages
// ---------------------------------------------------------------------------

export interface PageMeta {
  name: string;
  title: string | null;
  published: boolean;
  has_source: boolean;
  last_modified: number | null;
  size: number | null;
}

export interface PageDetail {
  name: string;
  source: string | null;
}

export async function fetchPages(): Promise<PageMeta[]> {
  const res = await fetch("/api/pages");
  return json(res);
}

export async function fetchPage(name: string): Promise<PageDetail> {
  const res = await fetch(`/api/pages/${name}`);
  return json(res);
}

export async function savePage(
  name: string,
  source: string,
  publish: boolean,
): Promise<PageMeta> {
  const res = await fetch(`/api/pages/${name}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ source, publish }),
  });
  return json(res);
}

export async function deletePage(name: string): Promise<void> {
  const res = await fetch(`/api/pages/${name}`, { method: "DELETE" });
  if (!res.ok) throw new Error(await res.text());
}

// ---------------------------------------------------------------------------
// Compile
// ---------------------------------------------------------------------------

export interface CompileResult {
  ascii: string;
  micron: string;
  script: string;
  is_dynamic: boolean;
  warnings: string[];
}

export async function compile(
  source: string,
  signal?: AbortSignal,
): Promise<CompileResult> {
  const res = await fetch("/api/compile", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ source }),
    signal,
  });
  if (!res.ok) {
    const err = await res.json().catch(() => ({ detail: "Compile failed" }));
    throw new Error(err.detail || "Compile failed");
  }
  return res.json();
}

// ---------------------------------------------------------------------------
// DSL Metadata
// ---------------------------------------------------------------------------

export interface DslCommand {
  label: string;
  detail: string;
  section: string;
  snippet: string;
}

export interface DslMeta {
  keywords: string[];
  values: string[];
  commands: DslCommand[];
  themes: string[];
}

export async function fetchDslMeta(): Promise<DslMeta> {
  const res = await fetch("/api/dsl-meta");
  return json(res);
}

// ---------------------------------------------------------------------------
// Node Management
// ---------------------------------------------------------------------------

export async function restartNode(): Promise<void> {
  const res = await fetch("/api/restart", { method: "POST" });
  if (!res.ok) throw new Error(await res.text());
}

// ---------------------------------------------------------------------------
// Browse — network node discovery + remote pages
// ---------------------------------------------------------------------------

export interface NetworkNode {
  hash: string;
  name: string;
  last_seen: number;
  is_self: boolean;
  type?: "node" | "interface";
  interface?: string | null;
  online?: boolean;
  target?: string | null;
  txb?: number;
  rxb?: number;
  bitrate?: number;
  clients?: number | null;
}

export async function fetchBrowseNodes(): Promise<NetworkNode[]> {
  const res = await fetch("/api/browse/nodes");
  const data = await json<NetworkNode[] | unknown>(res);
  return Array.isArray(data) ? data : [];
}

export function subscribeBrowseNodes(
  onNode: (node: NetworkNode) => void,
): () => void {
  const es = new EventSource("/api/browse/nodes/stream");
  es.onmessage = (e) => {
    try {
      onNode(JSON.parse(e.data));
    } catch { /* ignore parse errors */ }
  };
  return () => es.close();
}

export async function fetchRemotePage(
  hash: string,
  path: string = "index.mu",
): Promise<{ content: string | null; error?: string }> {
  const res = await fetch(
    `/api/browse/page/${hash}?path=${encodeURIComponent(path)}`,
  );
  return json(res);
}

// ---------------------------------------------------------------------------
// File Browser
// ---------------------------------------------------------------------------

export interface FileEntry {
  name: string;
  path: string;
  type: "file" | "folder" | "env";
  size: number | null;
  last_modified: number | null;
  title: string | null;
  published: boolean;
}

export async function fetchFiles(path: string = ""): Promise<FileEntry[]> {
  const params = path ? `?path=${encodeURIComponent(path)}` : "";
  const res = await fetch(`/api/files${params}`);
  return json(res);
}

export async function createFolder(path: string): Promise<void> {
  const res = await fetch("/api/files/mkdir", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ path }),
  });
  if (!res.ok) throw new Error(await res.text());
}

export async function moveFile(from: string, to: string): Promise<void> {
  const res = await fetch("/api/files/move", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ from, to }),
  });
  if (!res.ok) throw new Error(await res.text());
}

export async function fetchEnv(): Promise<string> {
  const res = await fetch("/api/files/env");
  const data = await json<{ content: string }>(res);
  return data.content;
}

export async function saveEnv(content: string): Promise<void> {
  const res = await fetch("/api/files/env", {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ content }),
  });
  if (!res.ok) throw new Error(await res.text());
}

// ---------------------------------------------------------------------------
// Config (Reticulum + NomadNet)
// ---------------------------------------------------------------------------

export async function fetchConfig(kind: "reticulum" | "nomadnet"): Promise<string> {
  const res = await fetch(`/api/browse/config/${kind}`);
  const data = await json<{ content: string }>(res);
  return data.content;
}

export async function saveConfig(kind: "reticulum" | "nomadnet", content: string): Promise<void> {
  const res = await fetch(`/api/browse/config/${kind}`, {
    method: "POST",
    headers: { "Content-Type": "application/json" },
    body: JSON.stringify({ content }),
  });
  if (!res.ok) throw new Error(await res.text());
}

export async function restartServices(): Promise<{ nomadnet_restarted: boolean }> {
  const res = await fetch("/api/browse/restart", { method: "POST" });
  if (!res.ok) throw new Error(await res.text());
  return res.json();
}

// ---------------------------------------------------------------------------
// Images
// ---------------------------------------------------------------------------

export interface UploadResult {
  filename: string;
  path: string;
}

export async function uploadImage(file: File): Promise<UploadResult> {
  const form = new FormData();
  form.append("file", file);
  const res = await fetch("/api/upload-image", { method: "POST", body: form });
  return json(res);
}
