import "server-only";

import { randomUUID } from "node:crypto";
import { api } from "@/lib/convex/api";
import { getAuthedConvexClient } from "@/lib/convex/route";
import {
  findBlockInTree,
  getBlocksFromDocumentContent,
  removeBlockSubtree,
  replaceBlockInTree,
  withBlocksWrittenBack,
} from "@/lib/blocks";
import { createBlockSnapshotOpsAdapter, type BlockInsertSpec } from "@/lib/blocks/block-ops-adapter";
import {
  assertBlockId,
  assertDocumentId,
  assertNextBlock,
  buildDocumentBridgeContext,
  buildDocumentCommandEnvelope,
  buildDocumentQueryEnvelope,
  documentBridgeErrorResponse,
  DocumentBridgeError,
  type BridgeContext,
  type CommandEnvelope,
} from "@/lib/documents/bridge";
import { recordBridgeCommandArtifacts } from "@/lib/documents/bridge-log";
import {
  executeRustBridgeMutationTransport,
  executeRustBridgeQueryTransport,
  resolveRustBridgeCommandPlan,
  resolveRustBridgeQueryPlan,
} from "@/lib/documents/rust-runtime";

type DocumentContentQueryResult = {
  content?: unknown;
  revision?: number | null;
  conflict_detection_key?: string | null;
} | null;

type DocumentMetaResult = {
  id?: string;
  workspace_id?: string | null;
  embed_default_block_id?: string | null;
} | null;

type DocumentState = {
  documentId: string;
  workspaceId: string | null;
  content: unknown;
  blocks: ReturnType<typeof getBlocksFromDocumentContent>;
  revision: number | null;
  conflictDetectionKey: string | null;
  meta: DocumentMetaResult;
};

type BlockCommandMeta = {
  requestId: string;
  traceId: string;
  commandId: string;
  commandName: string;
};

type BlockCommandResult<TResult> = BlockCommandMeta & {
  result: TResult;
};

type GetBlockResult = {
  block: Record<string, unknown>;
  meta: {
    requestId: string;
    traceId: string;
    queryName: string;
  };
};

function normalizeRevision(value: number | null | undefined): number | null {
  return typeof value === "number" && Number.isInteger(value) ? value : null;
}

function normalizeConflictDetectionKey(
  documentId: string,
  revision: number | null,
  value: string | null | undefined,
): string | null {
  if (typeof value === "string" && value.trim()) {
    return value.trim();
  }
  return revision === null ? null : `${documentId}:${revision}`;
}

async function loadDocumentState(input: {
  request: Request;
  documentId: string;
  workspaceId?: string | null;
}) {
  const { client } = await getAuthedConvexClient();
  const context = await buildDocumentBridgeContext({
    request: input.request,
    workspaceId: input.workspaceId ?? null,
  });
  const envelope = buildDocumentQueryEnvelope({
    name: "documents.content.get",
    payload: {
      documentId: input.documentId,
      workspaceId: input.workspaceId ?? null,
    },
  });
  const plan = await resolveRustBridgeQueryPlan({
    context,
    envelope,
  });
  const result = await executeRustBridgeQueryTransport<DocumentContentQueryResult>({
    client,
    plan,
  });
  if (!result) {
    throw new DocumentBridgeError("页面不存在或无权限", 404, "NOT_FOUND");
  }
  const meta = await client.query(api.documents.getMeta, { id: input.documentId });
  const revision = normalizeRevision(result.revision);
  const workspaceId = (meta?.workspace_id ?? input.workspaceId ?? null) as string | null;
  return {
    client,
    context,
    queryName: envelope.name,
    state: {
      documentId: input.documentId,
      workspaceId,
      content: result.content ?? null,
      blocks: getBlocksFromDocumentContent(result.content ?? null),
      revision,
      conflictDetectionKey: normalizeConflictDetectionKey(
        input.documentId,
        revision,
        result.conflict_detection_key,
      ),
      meta: meta as DocumentMetaResult,
    } satisfies DocumentState,
  };
}

async function resolveBlockCommandEnvelope<TPayload>(input: {
  context: BridgeContext;
  envelope: CommandEnvelope<TPayload>;
}) {
  await resolveRustBridgeCommandPlan({
    context: input.context,
    envelope: input.envelope,
  });
  return {
    requestId: input.context.requestId,
    traceId: input.context.traceId,
    commandId: input.envelope.commandId,
    commandName: input.envelope.name,
  } satisfies BlockCommandMeta;
}

async function executeDocumentSaveTransport(input: {
  client: Awaited<ReturnType<typeof getAuthedConvexClient>>["client"];
  request: Request;
  documentId: string;
  workspaceId: string | null;
  content: unknown;
  revision: number | null;
  conflictDetectionKey: string | null;
  recordArtifacts?: boolean;
}) {
  const context = await buildDocumentBridgeContext({
    request: input.request,
    workspaceId: input.workspaceId,
  });
  const envelope = buildDocumentCommandEnvelope({
    name: "documents.save",
    payload: {
      documentId: input.documentId,
      workspaceId: input.workspaceId,
      revision: input.revision,
      content: input.content,
      conflictDetectionKey: input.conflictDetectionKey,
    },
    context,
    target: {
      workspaceId: input.workspaceId,
      pageId: input.documentId,
    },
  });
  const plan = await resolveRustBridgeCommandPlan({
    context,
    envelope,
  });
  const result = await executeRustBridgeMutationTransport<{
    revision?: number | null;
    conflict_detection_key?: string | null;
  }>({
    client: input.client,
    plan,
  });
  if (input.recordArtifacts !== false) {
    await recordBridgeCommandArtifacts({
      client: input.client,
      context,
      envelope,
    });
  }
  return {
    requestId: context.requestId,
    traceId: context.traceId,
    commandId: envelope.commandId,
    commandName: envelope.name,
    result,
  } satisfies BlockCommandResult<{
    revision?: number | null;
    conflict_detection_key?: string | null;
  }>;
}

export async function executeBlockGetQuery(input: {
  request: Request;
  sourceDocumentId: string;
  blockId: string;
}): Promise<GetBlockResult> {
  const documentId = assertDocumentId(input.sourceDocumentId);
  const blockId = assertBlockId(input.blockId);
  const { context, state } = await loadDocumentState({
    request: input.request,
    documentId,
  });
  const envelope = buildDocumentQueryEnvelope({
    name: "blocks.get",
    payload: {
      blockId,
      workspaceId: state.workspaceId,
    },
  });
  await resolveRustBridgeQueryPlan({
    context,
    envelope,
  });
  const hit = findBlockInTree(state.blocks as never[], blockId);
  if (!hit || !hit.block || typeof hit.block !== "object") {
    throw new DocumentBridgeError("块不存在或无权限", 404, "NOT_FOUND");
  }
  return {
    block: hit.block as Record<string, unknown>,
    meta: {
      requestId: context.requestId,
      traceId: context.traceId,
      queryName: envelope.name,
    },
  };
}

export async function executeBlockPatchCommand(input: {
  request: Request;
  sourceDocumentId: string;
  workspaceId?: string | null;
  blockId: string;
  nextBlock: unknown;
}) {
  const documentId = assertDocumentId(input.sourceDocumentId);
  const blockId = assertBlockId(input.blockId);
  assertNextBlock(input.nextBlock);

  const { client, state } = await loadDocumentState({
    request: input.request,
    documentId,
    workspaceId: input.workspaceId ?? null,
  });
  const replaced = replaceBlockInTree(state.blocks as never[], blockId, input.nextBlock as never);
  if (!replaced.ok) {
    throw new DocumentBridgeError("块不存在或无权限", 404, "NOT_FOUND");
  }
  const nextContent = withBlocksWrittenBack(state.content, replaced.nextBlocks as never[]);
  const context = await buildDocumentBridgeContext({
    request: input.request,
    workspaceId: state.workspaceId,
  });
  const envelope = buildDocumentCommandEnvelope({
    name: "blocks.patch",
    payload: {
      documentId,
      workspaceId: state.workspaceId,
      blockId,
      nextBlock: input.nextBlock,
    },
    context,
    target: {
      workspaceId: state.workspaceId,
      pageId: documentId,
      blockId,
    },
  });
  const meta = await resolveBlockCommandEnvelope({
    context,
    envelope,
  });
  const save = await executeDocumentSaveTransport({
    client,
    request: input.request,
    documentId,
    workspaceId: state.workspaceId,
    content: nextContent,
    revision: state.revision,
    conflictDetectionKey: state.conflictDetectionKey,
    recordArtifacts: false,
  });
  await recordBridgeCommandArtifacts({
    client,
    context,
    envelope,
  });
  return {
    ...meta,
    result: save.result,
  } satisfies BlockCommandResult<{ revision?: number | null; conflict_detection_key?: string | null }>;
}

export async function executeBlockMoveCommand(input: {
  request: Request;
  sourceDocumentId: string;
  blockId: string;
  targetDocumentId: string;
}) {
  const sourceDocumentId = assertDocumentId(input.sourceDocumentId);
  const targetDocumentId = assertDocumentId(input.targetDocumentId);
  const blockId = assertBlockId(input.blockId);

  if (sourceDocumentId === targetDocumentId) {
    const context = await buildDocumentBridgeContext({ request: input.request, workspaceId: null });
    return {
      requestId: context.requestId,
      traceId: context.traceId,
      commandId: "noop",
      commandName: "blocks.move",
      result: { ok: true, noop: true },
    };
  }

  const { client, state: sourceState } = await loadDocumentState({
    request: input.request,
    documentId: sourceDocumentId,
  });
  const removed = removeBlockSubtree(sourceState.blocks as never[], blockId);
  if (!removed.removed) {
    throw new DocumentBridgeError("源块不存在或无权限", 404, "NOT_FOUND");
  }
  const { state: targetState } = await loadDocumentState({
    request: input.request,
    documentId: targetDocumentId,
  });

  const nextSourceContent = withBlocksWrittenBack(sourceState.content, removed.nextBlocks as never[]);
  const nextTargetContent = withBlocksWrittenBack(targetState.content, [
    ...(targetState.blocks as never[]),
    removed.removed as never,
  ]);
  const workspaceId = sourceState.workspaceId ?? targetState.workspaceId ?? null;
  const context = await buildDocumentBridgeContext({
    request: input.request,
    workspaceId,
  });
  const envelope = buildDocumentCommandEnvelope({
    name: "blocks.move",
    payload: {
      sourceDocumentId,
      targetDocumentId,
      blockId,
    },
    context,
    target: {
      workspaceId,
      pageId: targetDocumentId,
      blockId,
    },
  });
  const meta = await resolveBlockCommandEnvelope({
    context,
    envelope,
  });
  const sourceSave = await executeDocumentSaveTransport({
    client,
    request: input.request,
    documentId: sourceDocumentId,
    workspaceId: sourceState.workspaceId,
    content: nextSourceContent,
    revision: sourceState.revision,
    conflictDetectionKey: sourceState.conflictDetectionKey,
    recordArtifacts: false,
  });
  const targetSave = await executeDocumentSaveTransport({
    client,
    request: input.request,
    documentId: targetDocumentId,
    workspaceId: targetState.workspaceId,
    content: nextTargetContent,
    revision: targetState.revision,
    conflictDetectionKey: targetState.conflictDetectionKey,
    recordArtifacts: false,
  });
  await recordBridgeCommandArtifacts({
    client,
    context,
    envelope,
  });
  return {
    ...meta,
    result: {
      ok: true,
      sourceRevision: sourceSave.result.revision ?? null,
      targetRevision: targetSave.result.revision ?? null,
    },
  } satisfies BlockCommandResult<{
    ok: boolean;
    sourceRevision: number | null;
    targetRevision: number | null;
  }>;
}

export async function executeBlockEmbedCommand(input: {
  request: Request;
  sourceDocumentId: string;
  blockId: string;
  targetDocumentId: string;
}) {
  const sourceDocumentId = assertDocumentId(input.sourceDocumentId);
  const targetDocumentId = assertDocumentId(input.targetDocumentId);
  const blockId = assertBlockId(input.blockId);

  if (sourceDocumentId === targetDocumentId) {
    throw new DocumentBridgeError("禁止嵌入到当前页面", 400, "VALIDATION_ERROR");
  }

  const { client, state: sourceState } = await loadDocumentState({
    request: input.request,
    documentId: sourceDocumentId,
  });
  const hit = findBlockInTree(sourceState.blocks as never[], blockId);
  if (!hit) {
    throw new DocumentBridgeError("源块不存在或无权限", 404, "NOT_FOUND");
  }
  const { state: targetState } = await loadDocumentState({
    request: input.request,
    documentId: targetDocumentId,
  });
  const targetBlocks = [...(targetState.blocks as Array<Record<string, unknown>>)];
  const anchorId =
    typeof targetState.meta?.embed_default_block_id === "string" && targetState.meta.embed_default_block_id.trim()
      ? targetState.meta.embed_default_block_id.trim()
      : null;
  const anchorIndex = anchorId
    ? targetBlocks.findIndex((block) => String(block?.id ?? "") === anchorId)
    : -1;
  const insertIndex = anchorIndex >= 0 ? anchorIndex + 1 : targetBlocks.length;
  const referenceBlock = {
    id: randomUUID(),
    type: "blockReference",
    props: {
      sourceDocumentId,
      targetBlockId: blockId,
      display: "embed",
    },
    content: [],
    children: [],
  };
  const nextBlocks = [
    ...targetBlocks.slice(0, insertIndex),
    referenceBlock,
    ...targetBlocks.slice(insertIndex),
  ];
  const nextContent = withBlocksWrittenBack(targetState.content, nextBlocks as never[]);
  const context = await buildDocumentBridgeContext({
    request: input.request,
    workspaceId: targetState.workspaceId,
  });
  const envelope = buildDocumentCommandEnvelope({
    name: "blocks.embed",
    payload: {
      sourceDocumentId,
      targetDocumentId,
      blockId,
      targetBlockId: anchorId,
    },
    context,
    target: {
      workspaceId: targetState.workspaceId,
      pageId: targetDocumentId,
      blockId,
    },
  });
  const meta = await resolveBlockCommandEnvelope({
    context,
    envelope,
  });
  const save = await executeDocumentSaveTransport({
    client,
    request: input.request,
    documentId: targetDocumentId,
    workspaceId: targetState.workspaceId,
    content: nextContent,
    revision: targetState.revision,
    conflictDetectionKey: targetState.conflictDetectionKey,
    recordArtifacts: false,
  });
  await recordBridgeCommandArtifacts({
    client,
    context,
    envelope,
  });
  return {
    ...meta,
    result: {
      ok: true,
      revision: save.result.revision ?? null,
      referenceBlockId: String(referenceBlock.id),
    },
  } satisfies BlockCommandResult<{
    ok: boolean;
    revision: number | null;
    referenceBlockId: string;
  }>;
}

export async function executeDocumentSnapshotSaveCommand(input: {
  request: Request;
  documentId: string;
  workspaceId?: string | null;
  content: unknown;
  recordArtifacts?: boolean;
}) {
  const documentId = assertDocumentId(input.documentId);
  const { client, state } = await loadDocumentState({
    request: input.request,
    documentId,
    workspaceId: input.workspaceId ?? null,
  });
  return executeDocumentSaveTransport({
    client,
    request: input.request,
    documentId,
    workspaceId: state.workspaceId,
    content: input.content,
    revision: state.revision,
    conflictDetectionKey: state.conflictDetectionKey,
    recordArtifacts: input.recordArtifacts,
  });
}

export async function executeDocumentBlockInsertCommand(input: {
  request: Request;
  documentId: string;
  afterBlockId?: string | null;
  beforeBlockId?: string | null;
  blocks: BlockInsertSpec[];
  baseBlocks?: unknown;
}) {
  const adapter = createBlockSnapshotOpsAdapter({
    baseBlocks: input.baseBlocks,
    loadBlocks: async () => {
      const { state } = await loadDocumentState({
        request: input.request,
        documentId: input.documentId,
      });
      return {
        blocks: state.blocks as unknown[],
        source: "server",
      };
    },
  });
  const snapshot = await adapter.insertBlocks({
    afterBlockId: input.afterBlockId ?? undefined,
    beforeBlockId: input.beforeBlockId ?? undefined,
    blocks: input.blocks,
  });
  const save = await executeDocumentSnapshotSaveCommand({
    request: input.request,
    documentId: input.documentId,
    content: snapshot.data,
  });
  return {
    ...save,
    inserted: snapshot.inserted,
    data: snapshot.data,
    source: snapshot.source,
  };
}

export async function executeDocumentBlockReplaceRangeCommand(input: {
  request: Request;
  documentId: string;
  blockId: string;
  text: string;
  mode: "replace" | "append" | "prepend";
  baseBlocks?: unknown;
}) {
  const adapter = createBlockSnapshotOpsAdapter({
    baseBlocks: input.baseBlocks,
    loadBlocks: async () => {
      const { state } = await loadDocumentState({
        request: input.request,
        documentId: input.documentId,
      });
      return {
        blocks: state.blocks as unknown[],
        source: "server",
      };
    },
  });
  const snapshot = await adapter.replaceRange({
    blockId: assertBlockId(input.blockId),
    text: input.text,
    mode: input.mode,
  });
  const save = await executeDocumentSnapshotSaveCommand({
    request: input.request,
    documentId: input.documentId,
    content: snapshot.data,
  });
  return {
    ...save,
    blockId: input.blockId,
    mode: input.mode,
    data: snapshot.data,
    source: snapshot.source,
  };
}

export { documentBridgeErrorResponse };
