// HTTP shim of the Wails MatrixService bindings — used in dev/E2E mode when
// running the frontend via `pnpm dev` against a real Wails app's E2E HTTP
// server (default http://127.0.0.1:8767). Vite resolve.alias swaps the
// `wailsjs/go/main/MatrixService` import to this file when VITE_E2E_API is set.

const API: string =
  (import.meta as any).env?.VITE_E2E_API || "http://127.0.0.1:8767";

async function callJSON<T>(
  path: string,
  init: RequestInit = {},
): Promise<T> {
  const res = await fetch(API + path, {
    ...init,
    headers: {
      "Content-Type": "application/json",
      ...(init.headers || {}),
    },
  });
  if (!res.ok) {
    const body = await res.text();
    throw new Error(`E2E API ${path} -> ${res.status}: ${body}`);
  }
  return res.json() as Promise<T>;
}

// --- read endpoints ---
export async function GetLastUserID(): Promise<string> {
  // /last_user reads last_user.txt directly so /wipe_session causes the
  // frontend to fall back to LoginScreen even if sync is still active.
  const r: any = await callJSON("/last_user");
  return r.user_id || "";
}

export async function GetSession(user_id: string): Promise<any> {
  // E2E server does not expose per-user session. Approximate with diagnostics.
  const d: any = await callJSON("/diagnostics");
  return {
    user_id,
    has_token: !!d.user_id,
    homeserver_url: d.homeserver_url || "",
  };
}

export async function GetDiagnostics(): Promise<any> {
  return callJSON("/diagnostics");
}

export async function GetLogPath(): Promise<string> {
  const r: any = await callJSON("/logs?n=1");
  return r.path || "";
}

export async function GetLogTail(n: number): Promise<string[]> {
  const r: any = await callJSON(`/logs?n=${encodeURIComponent(n)}`);
  return r.lines || [];
}

export async function ListRooms(): Promise<any[]> {
  const r: any = await callJSON("/rooms");
  return r.rooms || [];
}

export async function LoadTimeline(room_id: string, limit: number): Promise<any[]> {
  const r: any = await callJSON(
    `/timeline?room_id=${encodeURIComponent(room_id)}&limit=${limit}`,
  );
  return r.events || [];
}

// --- write endpoints ---
// Dev mode forces skip_crypto: MatrixCryptoInit hangs indefinitely when MAS is
// active (the in-flight UIA roundtrip does not respect ctx cancellation). The
// frontend still sees rooms + can send plaintext to non-E2EE rooms; encrypted
// timelines render the wrapper events with "Encrypted" placeholders.
export async function Start(user_id: string): Promise<void> {
  await callJSON("/start?skip_crypto=true", {
    method: "POST",
    body: JSON.stringify({ user_id }),
  });
}

export async function StartNoCrypto(user_id: string): Promise<void> {
  await callJSON("/start?skip_crypto=true", {
    method: "POST",
    body: JSON.stringify({ user_id }),
  });
}

export async function Stop(): Promise<void> {
  await fetch(API + "/stop", { method: "POST" });
}

export async function SendText(room_id: string, body: string): Promise<string> {
  const r: any = await callJSON("/send", {
    method: "POST",
    body: JSON.stringify({ room_id, body }),
  });
  return r.event_id || "";
}

export async function SendMarkdown(room_id: string, body: string): Promise<string> {
  const r: any = await callJSON("/send", {
    method: "POST",
    body: JSON.stringify({ room_id, body, markdown: true }),
  });
  return r.event_id || "";
}

// --- login flow ---
// In real Wails the Login() call opens the OIDC loopback flow. In dev/E2E we
// short-circuit via /signin_admin: the backend uses its env-stored
// MATRIX_SYNAPSE_ADMIN_TOKEN to whoami and persist the token. user_id is
// resolved server-side — no frontend prompt needed.
export async function Login(): Promise<string> {
  const r: any = await callJSON("/signin_admin", {
    method: "POST",
    body: JSON.stringify({}),
  });
  if (!r.user_id) throw new Error("signin_admin returned no user_id");
  return r.user_id;
}

export async function Logout(_user_id: string): Promise<void> {
  await fetch(API + "/wipe_session", { method: "POST" });
}

export async function SetContext(_arg: any): Promise<void> {
  // no-op in dev
}
