import { type Router } from "express";
import { z } from "zod";

import { getAgentAuthContext } from "../auth/agentAuth.js";
import { type ChatSessionBridge } from "../chat/sessionBridge.js";
import { logger } from "../logger.js";
import { type ResultReferenceResolver } from "../results/resolver.js";
import { RESULT_REFERENCE_KIND } from "../results/store.js";
import { type SessionMetadataStore } from "../sessions/metadataStore.js";
import { type SessionUiStateStore } from "../sessions/uiStateStore.js";
import { toActorKey, toProjectKey } from "../utils/fileStore.js";
import {
  type ActiveRun,
  type RunStatus,
  cancelBackendTodos,
  completeBackendProgress,
  updateLastAssistantMessage,
} from "./chatUiState.js";

const abortParamsSchema = z.object({
  session_id: z.string().max(128),
});

type RegisterAuxiliaryRoutesOptions = {
  activeRuns: Map<string, ActiveRun>;
  lastRunStatuses: Map<string, RunStatus>;
  resultReferenceResolver: ResultReferenceResolver;
  sessionBridge: ChatSessionBridge;
  sessionMetadataStore: SessionMetadataStore;
  sessionUiStateStore: SessionUiStateStore;
};

const toSessionUiStateContext = (sessionId: string) => ({
  sessionId,
});

export const registerChatAuxiliaryRoutes = (
  chatRouter: Router,
  {
    activeRuns,
    lastRunStatuses,
    resultReferenceResolver,
    sessionBridge,
    sessionMetadataStore,
    sessionUiStateStore,
  }: RegisterAuxiliaryRoutesOptions,
) => {
  chatRouter.get("/render-references/:render_ref", async (req, res) => {
    const renderRef = req.params.render_ref?.trim();
    const authContext = getAgentAuthContext(req);
    const userId = authContext.userId;
    const projectId = authContext.projectId;
    const clientSessionId =
      typeof req.query.session_id === "string"
        ? req.query.session_id.trim()
        : undefined;

    if (!renderRef) {
      res.status(400).json({
        message: "render_ref is required",
      });
      return;
    }

    const result = await resultReferenceResolver.getFullAuthorized(
      renderRef,
      {
        actorKey: toActorKey(userId),
        clientSessionId,
        projectId,
      },
      {
        expectedKind: RESULT_REFERENCE_KIND.renderJunctionsPayload,
      },
    );

    if (!result) {
      res.status(404).json({ message: "render_ref not found" });
      return;
    }

    res.json(result);
  });

  chatRouter.delete("/sessions/:session_id/runs/current", async (req, res) => {
    const parsed = abortParamsSchema.safeParse(req.params);
    if (!parsed.success) {
      res.status(400).json({
        message: "invalid request payload",
        detail: parsed.error.flatten(),
      });
      return;
    }

    try {
      const authContext = getAgentAuthContext(req);
      const projectId = authContext.projectId;
      const userId = authContext.userId;
      const actorKey = toActorKey(userId);
      const projectKey = toProjectKey(projectId);
      const sessionRecord = await sessionMetadataStore.get(
        { actorKey, projectId, projectKey, userId },
        parsed.data.session_id,
      );
      const binding = sessionRecord
        ? await sessionBridge.abort({
            clientSessionId: sessionRecord.sessionId,
            sessionId: sessionRecord.sessionId,
          })
        : null;
      const run = activeRuns.get(parsed.data.session_id);
      if (run && run.status === "running") {
        run.status = "aborted";
        lastRunStatuses.set(parsed.data.session_id, "aborted");
        run.controller.abort();
        run.messages = updateLastAssistantMessage(run.messages, (message) => ({
          ...message,
          content:
            typeof message.content === "string" && message.content.trim()
              ? message.content
              : "⚠️ **请求已中断**",
          isError: true,
          progress: completeBackendProgress(message.progress),
          todos: cancelBackendTodos(message.todos),
        }));
        if (sessionRecord) {
          const currentState = await sessionUiStateStore.read(
            toSessionUiStateContext(sessionRecord.sessionId),
          );
          await sessionUiStateStore.write(toSessionUiStateContext(sessionRecord.sessionId), {
            sessionId: sessionRecord.sessionId,
            isTitleManuallyEdited: currentState?.isTitleManuallyEdited ?? false,
            messages: run.messages,
          });
        }
        for (const subscriber of run.subscribers) {
          subscriber.write("error", {
            session_id: parsed.data.session_id,
            message: "请求已中断",
          });
          subscriber.close();
        }
        run.subscribers.clear();
      }

      if (!binding && !run) {
        res.status(204).end();
        return;
      }

      logger.info(
        {
          clientSessionId: parsed.data.session_id,
          sessionId: binding?.sessionId ?? parsed.data.session_id,
        },
        "aborted chat session by client request",
      );
      res.status(202).json({
        session_id: parsed.data.session_id,
        aborted: true,
      });
    } catch (error) {
      const detail = error instanceof Error ? error.message : String(error);
      logger.error({ err: error }, "chat abort failed");
      res.status(500).json({
        message: "chat abort failed",
        detail,
      });
    }
  });
};
