"use strict";

import {__} from "@wordpress/i18n";
import Anthropic from "@anthropic-ai/sdk";

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

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

    /**
     * Construct
     *
     * @param {string} apiKey API key
     * @param {string} model AI client model
     */
    constructor({apiKey, model}) {
        /**
         * Init properties
         */
        // Trim whitespace from API key to prevent issues
        this.#credentials.apiKey = apiKey ? apiKey.trim() : null;
        this.#credentials.model = model;
        // Init API client
        this.#initApiClient();
    }

    /**
     * Initialize the API client
     */
    #initApiClient() {
        // Init API client with proper browser configuration
        this.#client = new Anthropic({
            apiKey: this.#credentials.apiKey,
            dangerouslyAllowBrowser: true,
            // Ensure headers are set correctly
            defaultHeaders: {
                'x-api-key': this.#credentials.apiKey,
            },
        });
    }

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

    /**
     * Generate content
     *
     * @param {string} systemInfo AI description
     * @param {string} prompt Request
     * @param {number} maxTokens Maximum tokens to generate
     * @returns {{status: boolean,data: string}}
     */
    async generateContent({systemInfo = "You are a helpful assistant.", prompt, maxTokens = 1024}) {
        try {
            const result = await this.chatCompletion({systemInfo, content: prompt, maxTokens});
            return {
                status: result.status,
                data: result.status ? result.data?.content[0]?.text : result.data
            };
        } catch (e) {
            return {
                status: false,
                data: e.message
            };
        }
    }

    /**
     * The chat completion endpoint request
     *
     * @param {string} systemInfo System information
     * @param {string} content Content
     * @param {number} maxTokens Maximum tokens to generate
     * @returns {Promise<{status: boolean, data: any}>}
     */
    async chatCompletion({systemInfo = "You are a helpful assistant.", content, maxTokens = 1024}) {
        try {
            if (!this.isApiClientInitialized()) {
                return {
                    status: false,
                    data: __("The Claude API client isn't initialized.", 'limb-chatbot')
                };
            }
            const message = await this.#client.messages.create({
                model: this.#credentials.model,
                max_tokens: maxTokens,
                system: systemInfo,
                messages: [
                    {role: "user", content},
                ],
            });

            // TODO: track usage
            // {
            //     "id": "msg_01XFDUDYJgAACzvnptvVoYEL",
            //     "type": "message",
            //     "role": "assistant",
            //     "content": [
            //         {
            //             "type": "text",
            //             "text": "Hello!"
            //         }
            //     ],
            //     "model": "claude-3-5-sonnet-20241022",
            //     "stop_reason": "end_turn",
            //     "stop_sequence": null,
            //     "usage": {
            //         "input_tokens": 12,
            //         "output_tokens": 6
            //     }
            // }

            return {
                status: true,
                data: message
            };
        } catch (e) {
            // Capture Anthropic API errors
            const errorMessage = e.error?.message || e.message || 'Unknown error';
            return {
                status: false,
                data: errorMessage
            };
        }
    }

    /**
     * Chat with conversation history
     *
     * @param {string} systemInfo System information
     * @param {Array} messages Array of messages in format [{role: "user", content: "text"}, {role: "assistant", content: "text"}]
     * @param {number} maxTokens Maximum tokens to generate
     * @return {{status: boolean, data: string}}
     */
    async chat({systemInfo = "You are a helpful assistant.", messages, maxTokens = 1024}) {
        try {
            if (!this.isApiClientInitialized()) {
                return {
                    status: false,
                    data: __("The Claude API client isn't initialized.", 'limb-chatbot')
                };
            }
            const message = await this.#client.messages.create({
                model: this.#credentials.model,
                max_tokens: maxTokens,
                system: systemInfo,
                messages,
            });

            // TODO: track usage
            // message.usage
            // { input_tokens: 25, output_tokens: 25 }

            return {
                status: true,
                data: message.content[0]?.text || ""
            };
        } catch (e) {
            return {
                status: false,
                data: e.message
            };
        }
    }

    /**
     * Stream chat completion
     *
     * @param {string} systemInfo System information
     * @param {Array} messages Array of messages
     * @param {number} maxTokens Maximum tokens to generate
     * @param {Function} onChunk Callback function for each chunk
     * @return {Promise<{status: boolean, data: string}>}
     */
    async streamChat({systemInfo = "You are a helpful assistant.", messages, maxTokens = 1024, onChunk}) {
        try {
            if (!this.isApiClientInitialized()) {
                return {
                    status: false,
                    data: __("The Claude API client isn't initialized.", 'limb-chatbot')
                };
            }

            const stream = await this.#client.messages.stream({
                model: this.#credentials.model,
                max_tokens: maxTokens,
                system: systemInfo,
                messages,
            });

            let fullText = "";

            for await (const chunk of stream) {
                if (chunk.type === 'content_block_delta' && chunk.delta?.text) {
                    fullText += chunk.delta.text;
                    if (onChunk && typeof onChunk === 'function') {
                        onChunk(chunk.delta.text);
                    }
                }
            }

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