const { Document } = require("../../../../models/documents");
const { safeJsonParse } = require("../../../http");
const { summarizeContent } = require("../utils/summarize");
const Provider = require("../providers/ai-provider");

const docSummarizer = {
  name: "document-summarizer",
  startupConfig: {
    params: {},
  },
  plugin: function () {
    return {
      name: this.name,
      setup(aibitat) {
        aibitat.function({
          super: aibitat,
          name: this.name,
          controller: new AbortController(),
          description:
            "List all documents in the workspace or summarize a specific document. See what files are available, get a summary of a document's contents, or read and condense a file into key points.",
          examples: [
            {
              prompt: "List my files",
              call: JSON.stringify({ action: "list", document_filename: null }),
            },
            {
              prompt: "Summarize the readme file",
              call: JSON.stringify({
                action: "summarize",
                document_filename: "readme.md",
              }),
            },
            {
              prompt: "Give me a summary of example.txt",
              call: JSON.stringify({
                action: "summarize",
                document_filename: "example.txt",
              }),
            },
          ],
          parameters: {
            $schema: "http://json-schema.org/draft-07/schema#",
            type: "object",
            properties: {
              action: {
                type: "string",
                enum: ["list", "summarize"],
                description:
                  "The action to take. 'list' will return all files available with their filename and descriptions. 'summarize' will open and summarize the file by the a document name.",
              },
              document_filename: {
                type: "string",
                "x-nullable": true,
                description:
                  "The file name of the document you want to get the full content of.",
              },
            },
            additionalProperties: false,
          },
          handler: async function ({ action, document_filename }) {
            if (action === "list") return await this.listDocuments();
            if (action === "summarize")
              return await this.summarizeDoc(document_filename);
            return "There is nothing we can do. This function call returns no information.";
          },

          /**
           * List all documents available in a workspace
           * @returns List of files and their descriptions if available.
           */
          listDocuments: async function () {
            try {
              this.super.introspect(
                `${this.caller}: Looking at the available documents.`
              );
              const documents = await Document.where({
                workspaceId: this.super.handlerProps.invocation.workspace_id,
              });
              if (documents.length === 0)
                return "No documents found - nothing can be done. Stop.";

              this.super.introspect(
                `${this.caller}: Found ${documents.length} documents`
              );
              const foundDocuments = documents.map((doc) => {
                const metadata = safeJsonParse(doc.metadata, {});
                return {
                  document_id: doc.docId,
                  filename: metadata?.title ?? "unknown.txt",
                  description: metadata?.description ?? "no description",
                };
              });

              return JSON.stringify(foundDocuments);
            } catch (error) {
              this.super.handlerProps.log(
                `document-summarizer.list raised an error. ${error.message}`
              );
              return `Let the user know this action was not successful. An error was raised while listing available files. ${error.message}`;
            }
          },

          summarizeDoc: async function (filename) {
            try {
              const availableDocs = safeJsonParse(
                await this.listDocuments(),
                []
              );
              if (!availableDocs.length) {
                this.super.handlerProps.log(
                  `${this.caller}: No available documents to summarize.`
                );
                return "No documents were found.";
              }

              const docInfo = availableDocs.find(
                (info) => info.filename === filename
              );
              if (!docInfo) {
                this.super.handlerProps.log(
                  `${this.caller}: No available document by the name "${filename}".`
                );
                return `No available document by the name "${filename}".`;
              }

              const document = await Document.content(docInfo.document_id);
              this.super.introspect(
                `${this.caller}: Grabbing all content for ${
                  filename ?? "a discovered file."
                }`
              );

              if (!document.content || document.content.length === 0) {
                throw new Error(
                  "This document has no readable content that could be found."
                );
              }

              // Report citation for the document being summarized
              this.super.addCitation?.({
                id: docInfo.document_id,
                title: document.title || filename,
                text: document.content,
                chunkSource: null,
                score: null,
              });

              const { TokenManager } = require("../../../helpers/tiktoken");
              if (
                new TokenManager(this.super.model).countFromString(
                  document.content
                ) < Provider.contextLimit(this.super.provider, this.super.model)
              ) {
                return document.content;
              }

              this.super.introspect(
                `${this.caller}: Summarizing ${filename ?? ""}...`
              );

              // Use a named listener so we can remove it after summarization completes,
              // preventing listener accumulation when summarizing many documents.
              const abortListener = () => {
                this.super.handlerProps.log(
                  "Abort was triggered, exiting summarization early."
                );
                this.controller.abort();
              };
              this.super.emitter.on("abort", abortListener);
              const cleanup = () => {
                this.super.emitter.removeListener("abort", abortListener);
              };

              return await summarizeContent({
                provider: this.super.provider,
                model: this.super.model,
                controllerSignal: this.controller.signal,
                content: document.content,
                aibitat: this.super,
              }).finally(cleanup);
            } catch (error) {
              this.super.handlerProps.log(
                `document-summarizer.summarizeDoc raised an error. ${error.message}`
              );
              return `Let the user know this action was not successful. An error was raised while summarizing the file. ${error.message}`;
            }
          },
        });
      },
    };
  },
};

module.exports = {
  docSummarizer,
};
