"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.XataChatMessageHistory = void 0;
const client_1 = require("@xata.io/client");
const chat_history_1 = require("@langchain/core/chat_history");
const messages_1 = require("@langchain/core/messages");
const chatMemoryColumns = [
    { name: "sessionId", type: "string" },
    { name: "type", type: "string" },
    { name: "role", type: "string" },
    { name: "content", type: "text" },
    { name: "name", type: "string" },
    { name: "additionalKwargs", type: "text" },
];
/**
 * A class for managing chat message history using Xata.io client. It
 * extends the BaseListChatMessageHistory class and provides methods to
 * get, add, and clear messages. It also ensures the existence of a table
 * where the chat messages are stored.
 * @example
 * ```typescript
 * const chatHistory = new XataChatMessageHistory({
 *   table: "messages",
 *   sessionId: new Date().toISOString(),
 *   client: new BaseClient({
 *     databaseURL: process.env.XATA_DB_URL,
 *     apiKey: process.env.XATA_API_KEY,
 *     branch: "main",
 *   }),
 *   apiKey: process.env.XATA_API_KEY,
 * });
 *
 * const chain = new ConversationChain({
 *   llm: new ChatOpenAI({ model: "gpt-4o-mini" }),
 *   memory: new BufferMemory({ chatHistory }),
 * });
 *
 * const response = await chain.invoke({
 *   input: "What did I just say my name was?",
 * });
 * console.log({ response });
 * ```
 */
class XataChatMessageHistory extends chat_history_1.BaseListChatMessageHistory {
    constructor(fields) {
        super(fields);
        Object.defineProperty(this, "lc_namespace", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: ["langchain", "stores", "message", "xata"]
        });
        Object.defineProperty(this, "client", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "sessionId", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "table", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "tableInitialized", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "createTable", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        Object.defineProperty(this, "apiClient", {
            enumerable: true,
            configurable: true,
            writable: true,
            value: void 0
        });
        const { sessionId, config, client, table } = fields;
        this.sessionId = sessionId;
        this.table = table || "memory";
        if (client) {
            this.client = client;
        }
        else if (config) {
            this.client = new client_1.BaseClient(config);
        }
        else {
            throw new Error("Either a client or a config must be provided to XataChatMessageHistoryInput");
        }
        if (fields.createTable !== false) {
            this.createTable = true;
            const apiKey = fields.apiKey || fields.config?.apiKey;
            if (!apiKey) {
                throw new Error("If createTable is set, an apiKey must be provided to XataChatMessageHistoryInput, either directly or through the config object");
            }
            this.apiClient = new client_1.XataApiClient({ apiKey });
        }
        else {
            this.createTable = false;
        }
        this.tableInitialized = false;
    }
    /**
     * Retrieves all messages associated with the session ID, ordered by
     * creation time.
     * @returns A promise that resolves to an array of BaseMessage instances.
     */
    async getMessages() {
        await this.ensureTable();
        const records = await this.client.db[this.table]
            .filter({ sessionId: this.sessionId })
            .sort("xata.createdAt", "asc")
            .getAll();
        const rawStoredMessages = records;
        const orderedMessages = rawStoredMessages.map((message) => {
            const data = {
                content: message.content,
                additional_kwargs: JSON.parse(message.additionalKwargs),
            };
            if (message.role) {
                data.role = message.role;
            }
            if (message.name) {
                data.name = message.name;
            }
            return {
                type: message.type,
                data,
            };
        });
        return (0, messages_1.mapStoredMessagesToChatMessages)(orderedMessages);
    }
    /**
     * Adds a new message to the database.
     * @param message The BaseMessage instance to be added.
     * @returns A promise that resolves when the message has been added.
     */
    async addMessage(message) {
        await this.ensureTable();
        const messageToAdd = (0, messages_1.mapChatMessagesToStoredMessages)([message]);
        await this.client.db[this.table].create({
            sessionId: this.sessionId,
            type: messageToAdd[0].type,
            content: messageToAdd[0].data.content,
            role: messageToAdd[0].data.role,
            name: messageToAdd[0].data.name,
            additionalKwargs: JSON.stringify(messageToAdd[0].data.additional_kwargs),
        });
    }
    /**
     * Deletes all messages associated with the session ID.
     * @returns A promise that resolves when the messages have been deleted.
     */
    async clear() {
        await this.ensureTable();
        const records = await this.client.db[this.table]
            .select(["id"])
            .filter({ sessionId: this.sessionId })
            .getAll();
        const ids = records.map((m) => m.id);
        await this.client.db[this.table].delete(ids);
    }
    /**
     * Checks if the table exists and creates it if it doesn't. This method is
     * called before any operation on the table.
     * @returns A promise that resolves when the table has been ensured.
     */
    async ensureTable() {
        if (!this.createTable) {
            return;
        }
        if (this.tableInitialized) {
            return;
        }
        const { databaseURL, branch } = await this.client.getConfig();
        const [, , host, , database] = databaseURL.split("/");
        const urlParts = (0, client_1.parseWorkspacesUrlParts)(host);
        if (urlParts == null) {
            throw new Error("Invalid databaseURL");
        }
        const { workspace, region } = urlParts;
        const tableParams = {
            workspace,
            region,
            database,
            branch,
            table: this.table,
        };
        let schema = null;
        try {
            schema = await this.apiClient.tables.getTableSchema(tableParams);
        }
        catch (e) {
            // pass
        }
        if (schema == null) {
            await this.apiClient.tables.createTable(tableParams);
            await this.apiClient.tables.setTableSchema({
                ...tableParams,
                schema: {
                    columns: chatMemoryColumns,
                },
            });
        }
    }
}
exports.XataChatMessageHistory = XataChatMessageHistory;
