import { ApiError, AuthenticationError, NetworkError, } from "./types";
import { AbstractClient } from "./abstract-client";
import encoding from "./wechat_lib/encoding.js";
/**
 * WeChat Mini Program client for interacting with the Axiom Lattice Agent Service API
 */
export class WeChatClient extends AbstractClient {
    /**
     * Creates a new WeChatClient instance
     * @param config - Configuration options for the client
     */
    constructor(config) {
        super({
            timeout: 300000,
            ...config,
        });
    }
    /**
     * Set tenant ID for multi-tenant environments
     * @param tenantId - Tenant identifier
     */
    setTenantId(tenantId) {
        this.tenantId = tenantId;
    }
    /**
     * Implementation of the abstract makeRequest method for WeChat clients
     * @param url - The URL to make the request to
     * @param options - Request options
     * @returns A promise that resolves to the response data
     */
    async makeRequest(url, options) {
        // Convert method to WeChat's expected type
        const methodStr = options?.method || "GET";
        const method = methodStr;
        // Build full URL with query parameters if provided
        let fullUrl = `${this.config.baseURL}${url}`;
        // Prepare headers
        const headers = {
            "Content-Type": "application/json",
            Authorization: `Bearer ${this.config.apiKey}`,
            ...this.config.headers,
            ...(options?.headers || {}),
        };
        if (this.tenantId) {
            headers["x-tenant-id"] = this.tenantId;
        }
        return this.request({
            url: fullUrl,
            method,
            data: options?.body,
        });
    }
    /**
     * Implementation of the abstract streamRequest method for WeChat clients
     */
    streamRequest(options, onEvent, onComplete, onError) {
        return this.streamRun(options, onEvent, onComplete, onError);
    }
    /**
     * Resume streaming from a known position
     * @param options - Options for resuming the stream
     * @param onEvent - Callback function that receives stream events
     * @param onComplete - Optional callback function called when streaming completes
     * @param onError - Optional callback function called when an error occurs
     * @returns A function that can be called to stop the stream
     */
    resumeStream(options, onEvent, onComplete, onError) {
        // Prepare headers
        const headers = {
            "Content-Type": "application/json",
            Authorization: `Bearer ${this.config.apiKey}`,
            ...this.config.headers,
        };
        if (this.tenantId) {
            headers["x-tenant-id"] = this.tenantId;
        }
        // Create a request task to track and potentially abort the request
        const requestTask = wx.request({
            url: `${this.config.baseURL}/api/resume_stream`,
            method: "POST",
            data: {
                thread_id: options.threadId,
                message_id: options.messageId,
                known_content: options.knownContent,
                poll_interval: options.pollInterval || 100,
            },
            header: headers,
            responseType: "text",
            enableChunked: true,
            success: () => {
                // This will be called when the connection is established
                // Actual data processing happens in the onChunkReceived callback
            },
            fail: (err) => {
                if (onError) {
                    onError(new Error(`Resume stream request failed: ${err.errMsg}`));
                }
            },
            complete: () => {
                if (onComplete) {
                    onComplete();
                }
            },
        });
        // Set up the chunk received handler
        requestTask.onChunkReceived((res) => {
            if (!res.data)
                return;
            // Process the raw data line by line
            const text = this.decodeUint8Array(res.data);
            const lines = text.split("\n");
            for (const line of lines) {
                if (line.trim().startsWith("data: ")) {
                    try {
                        const eventData = JSON.parse(line.trim().slice(6));
                        onEvent(eventData);
                    }
                    catch (error) {
                        if (onError) {
                            onError(error instanceof Error ? error : new Error(String(error)));
                        }
                    }
                }
            }
        });
        // Return a function to abort the request
        return () => {
            requestTask.abort();
        };
    }
    /**
     * Helper method to make WeChat HTTP requests
     * @private
     */
    async request(options) {
        const { url, method, data } = options;
        // Prepare headers
        const headers = {
            "Content-Type": "application/json",
            Authorization: `Bearer ${this.config.apiKey}`,
            ...this.config.headers,
        };
        if (this.tenantId) {
            headers["x-tenant-id"] = this.tenantId;
        }
        return new Promise((resolve, reject) => {
            wx.request({
                url,
                method,
                data,
                header: headers,
                timeout: this.config.timeout,
                success: (res) => {
                    const statusCode = res.statusCode;
                    if (statusCode >= 200 && statusCode < 300) {
                        resolve(res.data);
                    }
                    else if (statusCode === 401) {
                        reject(new AuthenticationError(res.data?.message || "Authentication failed"));
                    }
                    else {
                        reject(new ApiError(res.data?.message || "API Error", statusCode, res.data));
                    }
                },
                fail: (err) => {
                    reject(new NetworkError(`Request failed: ${err.errMsg}`));
                },
            });
        });
    }
    /**
     * Stream run results using WeChat's downloadFile API
     * @param options - Options for streaming run results
     * @param onEvent - Callback function that receives stream events
     * @param onComplete - Optional callback function called when streaming completes
     * @param onError - Optional callback function called when an error occurs
     * @returns A function that can be called to stop the stream
     */
    streamRun(options, onEvent, onComplete, onError) {
        // Prepare headers
        const headers = {
            "Content-Type": "application/json",
            Authorization: `Bearer ${this.config.apiKey}`,
            ...this.config.headers,
        };
        if (this.tenantId) {
            headers["x-tenant-id"] = this.tenantId;
        }
        // Get request parameters from the abstract method
        const requestParams = this.buildStreamRequestParams(options);
        // Create a request task to track and potentially abort the request
        const requestTask = wx.request({
            url: `${this.config.baseURL}${requestParams.url}`,
            method: requestParams.method,
            data: requestParams.body,
            header: headers,
            responseType: "text",
            enableChunked: true,
            success: () => {
                // This will be called when the connection is established
                // Actual data processing happens in the onChunkReceived callback
            },
            fail: (err) => {
                if (onError) {
                    onError(new Error(`Stream request failed: ${err.errMsg}`));
                }
            },
            complete: async () => {
                if (onComplete) {
                    // Fetch agent state before calling onComplete if enabled
                    if (options.enableReturnStateWhenSteamCompleted) {
                        try {
                            const state = await this.getAgentState(options.threadId);
                            onComplete(state);
                        }
                        catch (error) {
                            // If getting state fails, still call onComplete without state
                            onComplete();
                        }
                    }
                    else {
                        // Just call onComplete without state if not enabled
                        onComplete();
                    }
                }
            },
        });
        // Set up the chunk received handler
        requestTask.onChunkReceived((res) => {
            if (!res.data)
                return;
            // Process the raw data line by line
            const text = this.decodeUint8Array(res.data);
            const lines = text.split("\n");
            for (const line of lines) {
                if (line.trim().startsWith("data: ")) {
                    try {
                        const eventData = JSON.parse(line.trim().slice(6));
                        onEvent(eventData);
                    }
                    catch (error) {
                        if (onError) {
                            onError(error instanceof Error ? error : new Error(String(error)));
                        }
                    }
                }
            }
        });
        // Return a function to abort the request
        return () => {
            requestTask.abort();
        };
    }
    decodeUint8Array(uint8Array) {
        const data16 = this.buf2hex(uint8Array);
        const requestData = this.hexToStr(data16);
        return requestData;
    }
    buf2hex(arrayBuffer) {
        return Array.prototype.map
            .call(new Uint8Array(arrayBuffer), (x) => ("00" + x.toString(16)).slice(-2))
            .join("");
    }
    /**
     * Convert hexadecimal string to text (supports Chinese characters)
     * @param {String} hex - Hexadecimal string
     * @return {String} Decoded string including Chinese characters
     */
    hexToStr(hex) {
        // Remove leading and trailing whitespace
        let trimedStr = hex.trim();
        // Check if the first two characters are '0x', if so, remove them
        let rawStr = trimedStr.substr(0, 2).toLowerCase() === "0x"
            ? trimedStr.substr(2)
            : trimedStr;
        // Get the length of the raw string
        let len = rawStr.length;
        // If the length is not divisible by 2, the hex value is invalid, return empty string
        if (len % 2 !== 0) {
            return "";
        }
        let curCharCode; // Store character code from each iteration
        let resultStr = []; // Array to store converted decimal values
        for (let i = 0; i < len; i = i + 2) {
            curCharCode = parseInt(rawStr.substr(i, 2), 16);
            resultStr.push(curCharCode);
        }
        // Default encoding is utf-8
        let bytesView = new Uint8Array(resultStr); // Typed array of 8-bit unsigned integer values
        let str = new encoding.TextDecoder().decode(bytesView);
        return str;
    }
}
//# sourceMappingURL=wechat-client.js.map