import {
  convertToModelMessages,
  streamText,
  validateUIMessages,
  type LanguageModel,
  experimental_createMCPClient as createMCPClient,
  generateId,
  stepCountIs,
} from "ai";
import { z } from "zod";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";

import { createHybridActionApiRoute } from "~/services/routeBuilders/apiBuilder.server";
import {
  createConversationHistory,
  getConversationAndHistory,
} from "~/services/conversation.server";

import { getModel } from "~/lib/model.server";
import { UserTypeEnum } from "@core/types";
import { nanoid } from "nanoid";
import { getOrCreatePersonalAccessToken } from "~/services/personalAccessToken.server";
import {
  hasAnswer,
  hasQuestion,
  REACT_SYSTEM_PROMPT,
} from "~/lib/prompt.server";
import { enqueueCreateConversationTitle } from "~/lib/queue-adapter.server";
import { env } from "~/env.server";

const ChatRequestSchema = z.object({
  message: z.object({
    id: z.string().optional(),
    parts: z.array(z.any()),
    role: z.string(),
  }),
  id: z.string(),
});

const { loader, action } = createHybridActionApiRoute(
  {
    body: ChatRequestSchema,
    allowJWT: true,
    authorization: {
      action: "conversation",
    },
    corsStrategy: "all",
  },
  async ({ body, authentication }) => {
    const randomKeyName = `chat_${nanoid(10)}`;
    const pat = await getOrCreatePersonalAccessToken({
      name: randomKeyName,
      userId: authentication.userId,
    });

    const message = body.message.parts[0].text;
    const id = body.message.id;
    const apiEndpoint = `${env.APP_ORIGIN}/api/v1/mcp?source=core`;
    const url = new URL(apiEndpoint);

    const mcpClient = await createMCPClient({
      transport: new StreamableHTTPClientTransport(url, {
        requestInit: {
          headers: pat.token
            ? {
                Authorization: `Bearer ${pat.token}`,
              }
            : {},
        },
      }),
    });

    const conversation = await getConversationAndHistory(
      body.id,
      authentication.userId,
    );

    const conversationHistory = conversation?.ConversationHistory ?? [];

    if (conversationHistory.length === 0) {
      // Trigger conversation title task
      await enqueueCreateConversationTitle({
        conversationId: body.id,
        message,
      });
    }

    if (conversationHistory.length > 1) {
      await createConversationHistory(message, body.id, UserTypeEnum.User);
    }

    const messages = conversationHistory.map((history: any) => {
      return {
        parts: [{ text: history.message, type: "text" }],
        role: "user",
        id: history.id,
      };
    });

    const tools = { ...(await mcpClient.tools()) };

    const finalMessages = [
      ...messages,
      {
        parts: [{ text: message, type: "text" }],
        role: "user",
        id: id ?? generateId(),
      },
    ];

    const validatedMessages = await validateUIMessages({
      messages: finalMessages,
    });

    const result = streamText({
      model: getModel() as LanguageModel,
      messages: [
        {
          role: "system",
          content: REACT_SYSTEM_PROMPT,
        },
        ...convertToModelMessages(validatedMessages),
      ],
      tools,
      stopWhen: [stepCountIs(10), hasAnswer, hasQuestion],
    });

    result.consumeStream(); // no await

    return result.toUIMessageStreamResponse({
      originalMessages: validatedMessages,
      onFinish: async ({ messages }) => {
        const lastMessage = messages.pop();
        let message = "";
        lastMessage?.parts.forEach((part) => {
          if (part.type === "text") {
            message += part.text;
          }
        });

        await createConversationHistory(message, body.id, UserTypeEnum.Agent);
      },
      // async consumeSseStream({ stream }) {
      //   // Create a resumable stream from the SSE stream
      //   const streamContext = createResumableStreamContext({ waitUntil: null });
      //   await streamContext.createNewResumableStream(
      //     conversation.conversationHistoryId,
      //     () => stream,
      //   );
      // },
    });
  },
);

export { loader, action };
