class WxRequest {
    constructor(options = {}) {
        this.baseUrl = options.baseUrl || '';
        this.header = options.header || {};
        this.interceptors = {
            request: (config) => config,
            response: (response) => response
        };
    }

    /**
     * Set request interceptor
     * @param {Function} callback 
     */
    setRequestInterceptor(callback) {
        this.interceptors.request = callback;
    }

    /**
     * Set response interceptor
     * @param {Function} callback 
     */
    setResponseInterceptor(callback) {
        this.interceptors.response = callback;
    }

    /**
     * Core request method
     * @param {Object} options 
     */
    request(options = {}) {
        // Merge headers
        const header = { ...this.header, ...options.header };

        // Prepare config
        let config = {
            url: (options.url.startsWith('http') ? options.url : this.baseUrl + options.url),
            method: options.method || 'GET',
            data: options.data || {},
            header: header,
            timeout: options.timeout || 60000,
            skipToken: options.skipToken || false
        };

        return new Promise(async (resolve, reject) => {
            // Apply request interceptor (Async)
            try {
                config = await Promise.resolve(this.interceptors.request(config));
            } catch (ignore) {
                // If interceptor fails/rejects, likely means we shouldn't proceed
                // But for now let's just log or reject
                console.warn('Interceptor warning', ignore);
            }

            wx.request({
                ...config,
                success: (res) => {
                    // Apply response interceptor (pass the whole wx response)
                    const processedResponse = this.interceptors.response(res);
                    const { statusCode, data } = processedResponse;

                    // Auto-refresh token if 401 (One-time retry)
                    if ((statusCode === 401 || data.code === 401) && !options.skipToken && !options._retry) {
                        const app = getApp();
                        if (app && app.forceRefreshLogin) {

                            app.forceRefreshLogin().then(() => {
                                // Retry Original Request
                                this.request({ ...options, _retry: true })
                                    .then(resolve)
                                    .catch(reject);
                            }).catch(err => {
                                console.error('Token refresh failed', err);
                                reject(data);
                            });
                            return;
                        }
                    }

                    // 1. Check HTTP Status Code
                    if (statusCode >= 200 && statusCode < 300) {
                        // 2. Check Business Logic Code (Assuming 200 is success based on common Go patterns, 
                        // matching the user's struct: Code, Data, Msg)
                        // If data.code exists, we check it. If not (maybe not JSON), we pass through or handle.
                        const businessCode = data.code;

                        // Strict check: if code is 200, success.
                        if (businessCode === 200) {
                            resolve(data.data); // Return the inner Data object
                        } else {
                            // Business logic error
                            this.handleError({
                                errMsg: data.msg || 'Error',
                                code: businessCode
                            });
                            reject(data);
                        }
                    } else {
                        // Handle non-200 HTTP errors
                        this.handleError({ ...res, errMsg: `HTTP Error: ${statusCode}` });
                        reject(res);
                    }
                },
                fail: (err) => {
                    this.handleError({ ...err, errMsg: 'Network Error' });
                    reject(err);
                }
            });
        });
    }

    /**
     * Handle global errors (Toast)
     * @param {Object} error 
     */
    handleError(error) {
        const message = error.errMsg || error.msg || '请求失败，请稍后重试';
        wx.showToast({
            title: message,
            icon: 'none',
            duration: 3000
        });
        console.error('API Error:', error);
    }

    /**
     * Upload file
     * @param {string} filePath Local file path
     * @param {string} name Form field name (default: file)
     * @param {Object} formData Additional form data
     */
    upload(filePath, name = 'file', formData = {}) {
        // Prepare config
        let config = {
            url: this.baseUrl + '/file/upload',
            header: { ...this.header }, // Copy default headers
            filePath: filePath,
            name: name,
            formData: formData
        };

        // Apply request interceptor (Async)
        return new Promise(async (resolve, reject) => {
            try {
                config = await Promise.resolve(this.interceptors.request(config));
            } catch (ignore) {
                console.warn('Interceptor warning', ignore);
            }

            wx.uploadFile({
                url: config.url,
                filePath: config.filePath,
                name: config.name,
                formData: config.formData,
                header: config.header,
                success: (res) => {
                    // wx.uploadFile returns data as String, need to parse
                    let data;
                    try {
                        data = JSON.parse(res.data);
                    } catch (e) {
                        data = { code: 500, msg: 'Response parse error', data: res.data };
                    }

                    // Reconstruct a response object for interceptor
                    const responseObj = { ...res, data: data };

                    // Apply response interceptor
                    const processedResponse = this.interceptors.response(responseObj);
                    const { statusCode, data: finalData } = processedResponse;

                    // Status Code check (uploadFile also returns statusCode)
                    if (statusCode >= 200 && statusCode < 300) {
                        const businessCode = finalData.code;
                        if (businessCode === 200) {
                            resolve(finalData.data);
                        } else {
                            this.handleError({
                                errMsg: finalData.msg || 'Upload Error',
                                code: businessCode
                            });
                            reject(finalData);
                        }
                    } else {
                        this.handleError({ ...res, errMsg: `HTTP Error: ${statusCode}` });
                        reject(res);
                    }
                },
                fail: (err) => {
                    this.handleError({ ...err, errMsg: 'Upload Network Error' });
                    reject(err);
                }
            });
        });
    }

    /**
     * Upload file to a specific URL path
     * @param {string} urlPath API path (e.g. '/classify/plant')
     * @param {string} filePath Local file path
     * @param {string} name Form field name (default: file)
     * @param {Object} formData Additional form data
     */
    uploadToUrl(urlPath, filePath, name = 'file', formData = {}) {
        let config = {
            url: this.baseUrl + urlPath,
            header: { ...this.header },
            filePath: filePath,
            name: name,
            formData: formData
        };

        return new Promise(async (resolve, reject) => {
            try {
                config = await Promise.resolve(this.interceptors.request(config));
            } catch (ignore) {
                console.warn('Interceptor warning', ignore);
            }

            wx.uploadFile({
                url: config.url,
                filePath: config.filePath,
                name: config.name,
                formData: config.formData,
                header: config.header,
                success: (res) => {
                    let data;
                    try {
                        data = JSON.parse(res.data);
                    } catch (e) {
                        data = { code: 500, msg: 'Response parse error', data: res.data };
                    }

                    const responseObj = { ...res, data: data };
                    const processedResponse = this.interceptors.response(responseObj);
                    const { statusCode, data: finalData } = processedResponse;

                    if (statusCode >= 200 && statusCode < 300) {
                        const businessCode = finalData.code;
                        if (businessCode === 200) {
                            resolve(finalData.data);
                        } else {
                            this.handleError({
                                errMsg: finalData.msg || 'Upload Error',
                                code: businessCode
                            });
                            reject(finalData);
                        }
                    } else {
                        this.handleError({ ...res, errMsg: `HTTP Error: ${statusCode}` });
                        reject(res);
                    }
                },
                fail: (err) => {
                    this.handleError({ ...err, errMsg: 'Upload Network Error' });
                    reject(err);
                }
            });
        });
    }

    get(url, data = {}, header = {}) {
        return this.request({ url, method: 'GET', data, header });
    }

    post(url, data = {}, header = {}) {
        return this.request({ url, method: 'POST', data, header });
    }

    /**
     * SSE stream request with chunked transfer.
     * Reuses baseUrl + token interceptor.
     * @param {string} url  API path, e.g. '/plant/chat/stream'
     * @param {Object} data  Query params (GET) or body (POST)
     * @param {Object} callbacks  { onChunk, onDone, onError }
     * @returns {Object} wx.request task (call task.abort() to cancel)
     */
    stream(url, data = {}, callbacks = {}) {
        const fullUrl = url.startsWith('http')
            ? url
            : this.baseUrl + url;

        // Build query string for GET
        const qs = Object.keys(data)
            .map(k => `${encodeURIComponent(k)}=${encodeURIComponent(data[k])}`)
            .join('&');
        const reqUrl = qs ? `${fullUrl}?${qs}` : fullUrl;

        // Resolve token via same logic as interceptor
        let token = wx.getStorageSync('token');

        const mergedHeader = {
            ...this.header,
            'Accept': 'text/event-stream',
        };
        if (token) {
            mergedHeader['Authorization'] = `Bearer ${token}`;
        }

        const task = wx.request({
            url: reqUrl,
            method: 'GET',
            enableChunked: true,
            header: mergedHeader,
            success: () => {
                if (callbacks.onDone) callbacks.onDone();
            },
            fail: (err) => {
                if (callbacks.onError) callbacks.onError(err);
            },
        });

        if (callbacks.onChunk) {
            task.onChunkReceived(callbacks.onChunk);
        }

        return task;
    }
}

// Initialize with default instance
const request = new WxRequest({
    baseUrl: 'http://192.168.100.2:8888/api',
    //baseUrl: 'https://go.sundynix.cn/api',
    header: {
        'Content-Type': 'application/json'
    }
});

// Example: Setup default interceptors
request.setRequestInterceptor(async (config) => {
    // Skip checking token for login API itself
    if (config.url.includes('/auth/miniLogin') || config.skipToken) {
        return config;
    }

    let token = wx.getStorageSync('token');

    // If no token, attempt to wait for login
    if (!token) {
        const app = getApp();
        if (app && app.ensureLogin) {
            try {
                token = await app.ensureLogin();
            } catch (e) {
                // Login failed
                console.warn('Auto-login failed in interceptor', e);
            }
        }
    }

    // Inject token if available
    if (token) {
        config.header['Authorization'] = `Bearer ${token}`;
    }
    return config;
});

request.setResponseInterceptor((response) => {
    // Handle global token expiration (e.g., HTTP 401 or Business Code 401)
    if (response.statusCode === 401 || response.data?.code === 401) {
        wx.removeStorageSync('token');
        // Redirect to login if needed, or just warn
        // wx.reLaunch({ url: '/pages/login/index' });
    }
    return response;
});

export default request;
export { WxRequest };
