"use strict";

import {__} from "@wordpress/i18n";
import {GoogleGenerativeAI} from "@google/generative-ai";

export default class Gemini {
    /**
     * API client credentials
     * @type {{apiKey: string|null, model: string|null, generationConfig: object|null}}
     */
    #credentials = {
        apiKey: null,
        model: null,
        generationConfig: null,
    };

    /**
     * API client
     * @type {GoogleGenerativeAI|null}
     */
    #client = null;

    /**
     * API model
     * @type {GenerativeModel|null}
     */
    #model = null;

    /**
     * Construct
     *
     * @param {string} apiKey API key
     * @param {string} model AI client model
     * @param {object} generationConfig Configure text generation
     */
    constructor({apiKey, model, generationConfig}) {
        /**
         * Init properties
         */
        this.#credentials.apiKey = apiKey;
        this.#credentials.model = model;
        this.#credentials.generationConfig = generationConfig;
        // Init API client
        this.#initApiClient();
    }

    /**
     * Initialize the API client
     */
    #initApiClient() {
        // Init API client
        this.#client = new GoogleGenerativeAI(this.#credentials.apiKey);
        // Model args
        const modelArgs = {
            model: this.#credentials.model,
        };
        if (this.#credentials.generationConfig && ['candidateCount', 'stopSequences', 'maxOutputTokens', 'temperature'].some(key => key in this.#credentials.generationConfig)) {
            modelArgs.generationConfig = this.#credentials.generationConfig;
        }
        // Model
        this.#model = this.#client.getGenerativeModel(modelArgs);
    }

    /**
     * Is the API client initialized successfully
     *
     * @returns {boolean}
     */
    isApiClientInitialized() {
        return !!this.#client;
    }

    /**
     * File to generative content object
     *
     * @param {string} path File path
     * @param {string} mimeType Image mimetype, allowed only 'image/*'
     * @return {{inlineData: {data: string, mimeType: string}}|null}
     */
    #fileToGenerativePart(path, mimeType) {
        return {
            inlineData: {
                data: '', // TODO Buffer.from(fs.readFileSync(path)).toString("base64"),
                mimeType,
            },
        };
    }

    /**
     * Generate content
     *
     * @param {string} prompt Request
     * @param {{path: string, mimeType: string}} image Requested image data
     * @returns {{status: boolean,data: string}}
     */
    async generateContent({prompt, image}) {
        try {
            const result = await this.aiGenerateContent({prompt, image});
            return {
                status: result.status,
                data: result.data
            };
        } catch (e) {
            return {
                status: false,
                data: e.message
            };
        }
    }

    /**
     * The chat completion endpoint request
     *
     * @param {string} prompt Content
     * @param {{path: string, mimeType: string}} image Requested image data
     * @returns {{status: boolean,data: string}}
     */
    async aiGenerateContent({prompt, image}) {
        try {
            if (!this.isApiClientInitialized()) {
                return {
                    status: false,
                    data: __("The Google Gemini API client isn't initialized.", 'limb-chatbot')
                };
            }
            let args = prompt;
            if (image && image.path && image.mimeType) {
                args = [prompt, this.#fileToGenerativePart(image.path, image.mimeType)];
            }
            const result = await this.#model.generateContent(args);

            // TODO: track usage
            // generateResult.response.usageMetadata
            // { promptTokenCount: 11, candidatesTokenCount: 124, totalTokenCount: 135 }

            return {
                status: true,
                data: result.response.text()
            };
        } catch (e) {
            return {
                status: false,
                data: e.message
            };
        }
    }

    /**
     * Chat
     *
     * @param {string} text Requested text
     * @param {{role: string, parts: {text: string}[]}[]} history Chat history
     * @return {{status: boolean, data: string}}
     */
    async chat({text, history = []}) {
        try {
            if (!this.isApiClientInitialized()) {
                return {
                    status: false,
                    data: __("The Google Gemini API client isn't initialized.", 'limb-chatbot')
                };
            }
            const chat = await this.#model.startChat({history});
            const result = await chat.sendMessage(text);

            // TODO: track usage
            // chatResult.response.usageMetadata
            // { promptTokenCount: 25, candidatesTokenCount: 25, totalTokenCount: 50 }

            return {
                status: true,
                data: result.response.text()
            };
        } catch (e) {
            return {
                status: false,
                data: e.message
            };
        }
    }
}
