import {
  createSimpleMindMapBridge,
  type MindmapAdapterProjection,
  type SimpleMindMapBridge,
  type SimpleMindMapBridgeEvent,
  type SimpleMindMapPluginName,
} from "./simple-mind-map-bridge";
import type { MindmapCompatPayloadPatch, MindmapKernelCommand } from "./mindmap-command-diff";

export type {
  MindmapAdapterProjection,
  SimpleMindMapBridge,
  SimpleMindMapBridgeEvent,
} from "./simple-mind-map-bridge";

export type LeptosMindmapAdapterOptions = {
  el: HTMLElement;
  projection: MindmapAdapterProjection;
  mode?: "edit" | "readonly";
  pluginNames?: SimpleMindMapPluginName[];
  runtimeOptions?: Record<string, unknown>;
  onEvent?: (event: SimpleMindMapBridgeEvent) => void;
};

export type RequestMindmapAdapterProjectionInput = {
  documentId: string;
  mindmapId: string;
  endpoint?: string;
  fetcher?: typeof fetch;
};

export type MindmapCommandApplyInput = {
  documentId: string;
  mindmapId: string;
  commands: Array<MindmapKernelCommand | MindmapCompatPayloadPatch>;
  projectionRevision?: number | null;
  endpoint?: string;
  fetcher?: typeof fetch;
};

export type MindmapDataPutInput = {
  documentId: string;
  mindmapId: string;
  data: unknown;
  projectionEndpoint?: string;
  endpoint?: string;
  fetcher?: typeof fetch;
};

export type MindmapCommandApplyResult = {
  ok: true;
  kernelRevision: number | null;
  raw: unknown;
};

export class MindmapCommandBridgeError extends Error {
  readonly code: "command_failed";
  readonly status: number | null;

  constructor(message: string, status: number | null = null) {
    super(message);
    this.name = "MindmapCommandBridgeError";
    this.code = "command_failed";
    this.status = status;
  }
}

const isRecord = (value: unknown): value is Record<string, unknown> =>
  typeof value === "object" && value !== null && !Array.isArray(value);

const readErrorDetailFromPayload = (raw: unknown): string => {
  if (!isRecord(raw)) return "";
  const detail = [raw.error, raw.message, raw.details]
    .map((value) => {
      if (typeof value === "string") return value.trim();
      if (Array.isArray(value) && value.length > 0) return value.join("|");
      return "";
    })
    .find(Boolean);
  return detail ? `:${detail}` : "";
};

const readErrorDetail = async (response: Response): Promise<string> => {
  if (typeof response.text !== "function") {
    const raw = (await response.json().catch(() => null)) as unknown;
    return readErrorDetailFromPayload(raw);
  }
  const rawText = await response.text().catch(() => "");
  if (!rawText.trim()) return "";
  try {
    return readErrorDetailFromPayload(JSON.parse(rawText) as unknown) || `:${rawText.trim()}`;
  } catch {
    return `:${rawText.trim()}`;
  }
};

export const isMindmapAdapterProjection = (value: unknown): value is MindmapAdapterProjection => {
  if (!isRecord(value)) return false;
  return (
    value.schema === "mnote.mindmap.simple_mind_map_scene.v1" &&
    value.runtime === "simple-mind-map" &&
    "root" in value &&
    typeof value.kernelRevision === "number"
  );
};

export const createMindmapAdapterProjectionEndpoint = (documentId: string, mindmapId: string) => {
  const doc = encodeURIComponent(documentId);
  const map = encodeURIComponent(mindmapId);
  return `/api/mindmap/${doc}/${map}?view=simple_mind_map_scene&queryName=mindmap.simple_mind_map_scene.get`;
};

export const createMindmapCommandApplyEndpoint = (documentId: string, mindmapId: string) => {
  const doc = encodeURIComponent(documentId);
  const map = encodeURIComponent(mindmapId);
  return `/api/mindmap/${doc}/${map}`;
};

export const requestMindmapAdapterProjection = async (
  input: RequestMindmapAdapterProjectionInput,
): Promise<MindmapAdapterProjection> => {
  const fetcher = input.fetcher ?? fetch;
  const endpoint = input.endpoint ?? createMindmapAdapterProjectionEndpoint(input.documentId, input.mindmapId);
  const response = await fetcher(endpoint, {
    headers: { Accept: "application/json" },
  });
  if (!response.ok) {
    throw new Error(`projection_load_failed:${response.status}`);
  }
  const payload = (await response.json()) as unknown;
  const projection = isRecord(payload) && "result" in payload ? payload.result : payload;
  if (!isMindmapAdapterProjection(projection)) {
    throw new Error("projection_load_failed:invalid_adapter_projection");
  }
  return projection;
};

const readKernelRevision = (value: unknown): number | null => {
  if (!isRecord(value)) return null;
  const candidates = [
    value.kernelRevision,
    value.projectionRevision,
    isRecord(value.result) ? value.result.kernelRevision : null,
    isRecord(value.result) ? value.result.projectionRevision : null,
  ];
  for (const candidate of candidates) {
    if (typeof candidate === "number" && Number.isFinite(candidate)) return candidate;
  }
  return null;
};

export const executeMindmapCommandApply = async (
  input: MindmapCommandApplyInput,
): Promise<MindmapCommandApplyResult> => {
  if (input.commands.length === 0) {
    throw new MindmapCommandBridgeError("command_failed:empty_commands");
  }
  const fetcher = input.fetcher ?? fetch;
  const endpoint = input.endpoint ?? createMindmapCommandApplyEndpoint(input.documentId, input.mindmapId);
  const requestBody = JSON.stringify({
    commandName: "mindmap.command.apply",
    documentId: input.documentId,
    mindmapId: input.mindmapId,
    commands: input.commands,
    projectionRevision: input.projectionRevision ?? null,
  });
  const response = await fetcher(endpoint, {
    method: "POST",
    keepalive: requestBody.length <= 60_000,
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: requestBody,
  });
  const raw = (await response.json().catch(() => null)) as unknown;
  if (!response.ok) {
    const detail = readErrorDetailFromPayload(raw);
    throw new MindmapCommandBridgeError(
      `command_failed:${response.status}${detail}`,
      response.status,
    );
  }
  return {
    ok: true,
    kernelRevision: readKernelRevision(raw),
    raw,
  };
};

export const executeMindmapCommandApplyAndRefreshProjection = async (
  input: MindmapCommandApplyInput & { projectionEndpoint?: string },
): Promise<MindmapAdapterProjection> => {
  await executeMindmapCommandApply(input);
  return requestMindmapAdapterProjection({
    documentId: input.documentId,
    mindmapId: input.mindmapId,
    endpoint: input.projectionEndpoint,
    fetcher: input.fetcher,
  });
};

export const executeMindmapDataPutAndRefreshProjection = async (
  input: MindmapDataPutInput,
): Promise<MindmapAdapterProjection> => {
  const fetcher = input.fetcher ?? fetch;
  const endpoint = input.endpoint ?? createMindmapCommandApplyEndpoint(input.documentId, input.mindmapId);
  const response = await fetcher(endpoint, {
    method: "POST",
    headers: {
      Accept: "application/json",
      "Content-Type": "application/json",
    },
    body: JSON.stringify({
      data: input.data,
      createOnly: false,
    }),
  });
  if (!response.ok) {
    const detail = await readErrorDetail(response);
    throw new MindmapCommandBridgeError(`command_failed:${response.status}${detail}`, response.status);
  }
  await response.json().catch(() => null);
  return requestMindmapAdapterProjection({
    documentId: input.documentId,
    mindmapId: input.mindmapId,
    endpoint: input.projectionEndpoint,
    fetcher,
  });
};

export const createLeptosMindmapAdapter = async (
  input: LeptosMindmapAdapterOptions,
): Promise<SimpleMindMapBridge> => {
  if (!isMindmapAdapterProjection(input.projection)) {
    throw new Error("adapter_init_failed:invalid_projection");
  }
  return createSimpleMindMapBridge(input);
};
