import { streamText, Tool } from "ai";
import { readSettings } from "../../main/settings";

import log from "electron-log";
import { safeSend } from "../utils/safe_sender";
import {
  createOpenAI,
  openai,
  OpenAIResponsesProviderOptions,
} from "@ai-sdk/openai";
import { createTypedHandler } from "./base";
import { helpContracts } from "../types/help";
import { resolveBuiltinModelAlias } from "../shared/remote_language_model_catalog";
import { DyadError, DyadErrorKind } from "@/errors/dyad_error";

const logger = log.scope("help-bot");

// In-memory session store for help bot conversations
type HelpMessage = { role: "user" | "assistant"; content: string };
const helpSessions = new Map<string, HelpMessage[]>();
const activeHelpStreams = new Map<string, AbortController>();

export function registerHelpBotHandlers() {
  createTypedHandler(helpContracts.start, async (event, params) => {
    const { sessionId, message } = params;
    try {
      if (!sessionId || !message?.trim()) {
        throw new DyadError(
          "Missing sessionId or message",
          DyadErrorKind.External,
        );
      }

      // Clear any existing active streams (only one session at a time)
      for (const [existingSessionId, controller] of activeHelpStreams) {
        controller.abort();
        activeHelpStreams.delete(existingSessionId);
        helpSessions.delete(existingSessionId);
      }

      // Append user message to session history
      const history = helpSessions.get(sessionId) ?? [];
      const updatedHistory: HelpMessage[] = [
        ...history,
        { role: "user", content: message },
      ];

      const abortController = new AbortController();
      activeHelpStreams.set(sessionId, abortController);
      const settings = await readSettings();
      const apiKey = settings.providerSettings?.["auto"]?.apiKey?.value;
      const provider = createOpenAI({
        baseURL: "https://helpchat.dyad.sh/v1",
        apiKey,
      });
      const helpBotModel = await resolveBuiltinModelAlias(
        "dyad/help-bot/default",
      );

      if (!helpBotModel || helpBotModel.providerId !== "openai") {
        // Help bot requires OpenAI provider because it uses the OpenAI
        // responses API via a custom baseURL (helpchat.dyad.sh).
        throw new Error(
          `Help bot requires an OpenAI model (got provider: ${helpBotModel?.providerId ?? "none"}). ` +
            `The 'dyad/help-bot/default' alias must resolve to an OpenAI model.`,
        );
      }

      let assistantContent = "";

      const stream = streamText({
        model: provider.responses(helpBotModel.apiName),
        providerOptions: {
          openai: {
            reasoningSummary: "auto",
          } satisfies OpenAIResponsesProviderOptions,
        },
        tools: {
          web_search_preview: openai.tools.webSearchPreview({
            searchContextSize: "high",
          }) as Tool,
        },
        messages: updatedHistory as any,
        maxRetries: 1,
        onError: (error) => {
          let errorMessage = (error as any)?.error?.message;
          logger.error("help bot stream error", errorMessage);
          safeSend(event.sender, "help:chat:response:error", {
            sessionId,
            error: String(errorMessage),
          });
        },
      });

      (async () => {
        try {
          for await (const part of stream.fullStream) {
            if (abortController.signal.aborted) break;

            if (part.type === "text-delta") {
              assistantContent += part.text;
              safeSend(event.sender, "help:chat:response:chunk", {
                sessionId,
                delta: part.text,
                type: "text",
              });
            }
          }

          // Finalize session history
          const finalHistory: HelpMessage[] = [
            ...updatedHistory,
            { role: "assistant", content: assistantContent },
          ];
          helpSessions.set(sessionId, finalHistory);

          safeSend(event.sender, "help:chat:response:end", { sessionId });
        } catch (err) {
          if ((err as any)?.name === "AbortError") {
            logger.log("help bot stream aborted", sessionId);
            return;
          }
          logger.error("help bot stream loop error", err);
          safeSend(event.sender, "help:chat:response:error", {
            sessionId,
            error: String(err instanceof Error ? err.message : err),
          });
        } finally {
          activeHelpStreams.delete(sessionId);
        }
      })();

      return { ok: true } as const;
    } catch (err) {
      logger.error("help:chat:start error", err);
      throw err instanceof Error ? err : new Error(String(err));
    }
  });

  createTypedHandler(helpContracts.cancel, async (_, sessionId) => {
    const controller = activeHelpStreams.get(sessionId);
    if (controller) {
      controller.abort();
      activeHelpStreams.delete(sessionId);
    }
    return { ok: true } as const;
  });
}
