import { api } from "@/lib/convex/api";
import {
  DocumentBridgeError,
  type BridgeContext,
  type BridgeTarget,
  type CommandEnvelope,
} from "@/lib/documents/bridge";
import { getAuthedConvexClient } from "@/lib/convex/route";
import type { ConvexHttpClient } from "convex/browser";

export type BridgeCommandLogStatus = "pending" | "succeeded" | "failed" | "rolled_back";
export type BridgeDomainEventStatus = "pending" | "committed" | "rejected" | "failed";

function buildPayloadSummary(commandName: string, context: BridgeContext): string {
  return `command=${commandName};request_id=${context.requestId};trace_id=${context.traceId}`;
}

function normalizeWorkspaceId(context: BridgeContext, target?: BridgeTarget | null): string | null {
  return target?.workspaceId?.trim() || context.workspaceId?.trim() || null;
}

export async function recordBridgeCommandArtifacts<T>(input: {
  context: BridgeContext;
  envelope: CommandEnvelope<T>;
  client?: ConvexHttpClient;
  status?: BridgeCommandLogStatus;
  eventStatus?: BridgeDomainEventStatus;
  error?: string | null;
  now?: string;
}): Promise<void> {
  const workspaceId = normalizeWorkspaceId(input.context, input.envelope.target);
  if (!workspaceId) return;

  const client = input.client ?? (await getAuthedConvexClient()).client;
  const commandLogId = `clog_${input.envelope.commandId}`;
  const eventId = `evt_${input.envelope.commandId}`;
  const now = input.now ?? new Date().toISOString();
  const payload = input.envelope.payload as Record<string, unknown>;
  const status = input.status ?? "succeeded";
  const eventStatus =
    input.eventStatus ??
    (status === "pending" ? "pending" : status === "failed" || status === "rolled_back" ? "failed" : "committed");
  const aggregateType = input.envelope.target?.blockId ? "block" : input.envelope.target?.pageId ? "page" : "workspace";
  const aggregateId =
    input.envelope.target?.blockId ?? input.envelope.target?.pageId ?? workspaceId;

  await client.mutation(api.bridgeLogs.recordCommandLog, {
    workspaceId,
    id: commandLogId,
    requestId: input.context.requestId,
    traceId: input.context.traceId,
    commandId: input.envelope.commandId,
    commandName: input.envelope.name,
    actorId: input.context.actor.actorId,
    actorType: input.context.actor.actorType,
    sourceChannel: input.context.source.channel,
    sourceClient: input.context.source.client,
    status,
    targetPageId: input.envelope.target?.pageId ?? null,
    targetBlockId: input.envelope.target?.blockId ?? null,
    payload,
    payloadSummary: buildPayloadSummary(input.envelope.name, input.context),
    refs: input.envelope.refs,
    idempotencyKey: input.envelope.idempotencyKey,
    error: input.error ?? null,
    createdAt: now,
    finishedAt: status === "pending" ? null : now,
  });

  await client.mutation(api.bridgeLogs.recordDomainEvent, {
    workspaceId,
    id: eventId,
    requestId: input.context.requestId,
    traceId: input.context.traceId,
    commandId: input.envelope.commandId,
    commandLogId,
    eventType: `${input.envelope.name}.requested`,
    aggregateType,
    aggregateId,
    eventVersion: 1,
    status: eventStatus,
    actorType: input.context.actor.actorType,
    payload: {
      request_id: input.context.requestId,
      trace_id: input.context.traceId,
      command_id: input.envelope.commandId,
      command_name: input.envelope.name,
      idempotency_key: input.envelope.idempotencyKey,
      error: input.error ?? null,
    },
    createdAt: now,
  });
}

function normalizeBridgeErrorMessage(error: unknown) {
  if (error instanceof Error) {
    return error.message;
  }
  if (typeof error === "string" && error.trim()) {
    return error.trim();
  }
  return "未知 bridge 错误";
}

function resolveFailureStatuses(error: unknown): {
  status: BridgeCommandLogStatus;
  eventStatus: BridgeDomainEventStatus;
} {
  const details =
    error instanceof DocumentBridgeError && error.details && typeof error.details === "object"
      ? (error.details as Record<string, unknown>)
      : null;
  const reason = typeof details?.reason === "string" ? details.reason.trim() : "";
  if (reason === "rolled_back" || reason === "compensation_applied") {
    return {
      status: "rolled_back",
      eventStatus: "failed",
    };
  }
  if (error instanceof DocumentBridgeError && error.code === "REJECTED") {
    return {
      status: "failed",
      eventStatus: "rejected",
    };
  }
  return {
    status: "failed",
    eventStatus: "failed",
  };
}

export async function recordBridgeCommandFailureArtifacts<T>(input: {
  context: BridgeContext;
  envelope: CommandEnvelope<T>;
  client?: ConvexHttpClient;
  error: unknown;
}): Promise<void> {
  const { status, eventStatus } = resolveFailureStatuses(input.error);
  try {
    await recordBridgeCommandArtifacts({
      context: input.context,
      envelope: input.envelope,
      client: input.client,
      status,
      eventStatus,
      error: normalizeBridgeErrorMessage(input.error),
    });
  } catch (loggingError) {
    console.warn("[bridge-log] failure artifacts skipped:", loggingError);
  }
}
