import type { BackendCommand } from "$lib/backend/types";

type InvokeArgs = Record<string, unknown> | undefined;

type WindowWithTauri = Window & {
  __TAURI_INTERNALS__?: unknown;
};

type UiSettingsPayload = {
  tags?: string[];
  fragmentTypes?: string[];
  defaultStartupView?: string;
};

type FetchJsonOptions = {
  keepalive?: boolean;
};

const UI_SETTINGS_KEY = "journal.ui.settings";

function normalizedApiBase(): string {
  const configured = import.meta.env.VITE_JOURNAL_API_BASE?.trim();
  if (!configured) {
    return "/api";
  }

  return configured.endsWith("/") ? configured.slice(0, -1) : configured;
}

export function isTauriRuntime(): boolean {
  if (typeof window === "undefined") {
    return false;
  }

  return Object.prototype.hasOwnProperty.call(
    window as WindowWithTauri,
    "__TAURI_INTERNALS__",
  );
}

function readUiSettingsFromLocalStorage(): UiSettingsPayload {
  if (typeof window === "undefined") {
    return {};
  }

  const raw = window.localStorage.getItem(UI_SETTINGS_KEY);
  if (!raw) {
    return {};
  }

  try {
    const parsed = JSON.parse(raw) as UiSettingsPayload;
    return {
      tags: Array.isArray(parsed.tags) ? parsed.tags : undefined,
      fragmentTypes: Array.isArray(parsed.fragmentTypes)
        ? parsed.fragmentTypes
        : undefined,
      defaultStartupView:
        typeof parsed.defaultStartupView === "string"
          ? parsed.defaultStartupView
          : undefined,
    };
  } catch {
    return {};
  }
}

function writeUiSettingsToLocalStorage(payload: UiSettingsPayload): void {
  if (typeof window === "undefined") {
    return;
  }

  const safePayload: UiSettingsPayload = {
    tags: Array.isArray(payload.tags) ? payload.tags : undefined,
    fragmentTypes: Array.isArray(payload.fragmentTypes)
      ? payload.fragmentTypes
      : undefined,
    defaultStartupView:
      typeof payload.defaultStartupView === "string"
        ? payload.defaultStartupView
        : undefined,
  };

  window.localStorage.setItem(UI_SETTINGS_KEY, JSON.stringify(safePayload));
}

async function fetchJson<T>(
  path: string,
  init: RequestInit = {},
  options: FetchJsonOptions = {},
): Promise<T> {
  const response = await fetch(`${normalizedApiBase()}${path}`, {
    ...init,
    keepalive: options.keepalive === true,
    headers: {
      "Content-Type": "application/json",
      ...(init.headers ?? {}),
    },
  });

  if (!response.ok) {
    const text = await response.text();
    throw new Error(text || `Request failed (${response.status})`);
  }

  if (response.status === 204) {
    return undefined as T;
  }

  return (await response.json()) as T;
}

export async function invoke<T>(
  command: string,
  args?: InvokeArgs,
): Promise<T> {
  if (isTauriRuntime()) {
    const tauriCore = await import("@tauri-apps/api/core");
    return tauriCore.invoke<T>(command, args);
  }

  switch (command) {
    case "sidecar_command": {
      const envelope = args?.command;
      if (!envelope || typeof envelope !== "object") {
        throw new Error("Missing command payload.");
      }

      const keepalive = args?.keepalive === true;

      return fetchJson<T>(
        "/command",
        {
          method: "POST",
          body: JSON.stringify(envelope as BackendCommand),
        },
        { keepalive },
      );
    }
    case "get_sidecar_root":
      return fetchJson<T>("/sidecar/root");
    case "set_sidecar_root": {
      const path = typeof args?.path === "string" ? args.path : "";
      return fetchJson<T>("/sidecar/root", {
        method: "POST",
        body: JSON.stringify({ path }),
      });
    }
    case "get_ui_settings":
      return readUiSettingsFromLocalStorage() as T;
    case "set_ui_settings": {
      const tags = Array.isArray(args?.tags)
        ? (args?.tags as string[])
        : undefined;
      const fragmentTypes = Array.isArray(args?.fragmentTypes)
        ? (args?.fragmentTypes as string[])
        : Array.isArray(args?.fragment_types)
          ? (args?.fragment_types as string[])
          : undefined;
      const defaultStartupView =
        typeof args?.defaultStartupView === "string"
          ? args.defaultStartupView
          : typeof args?.default_startup_view === "string"
            ? args.default_startup_view
            : undefined;

      writeUiSettingsToLocalStorage({
        tags,
        fragmentTypes,
        defaultStartupView,
      });
      return undefined as T;
    }
    case "shutdown":
      return undefined as T;
    case "speech_start":
    case "speech_stop":
      throw new Error(
        "Speech dictation is available in the desktop app runtime only.",
      );
    default:
      throw new Error(`Unsupported command in web runtime: ${command}`);
  }
}
