import {BaseUrl, HtyAuthToken, HtySudoToken, HtyHostHeader, WebViewEntryUrl} from '~/utils';
import axios, {AxiosRequestConfig} from "axios";
import {closeToast, showFailToast} from "vant";
import useUser from "~/store/user";
import useLoading from "~/store/loading";
import {UnauthorizedError} from "~/types";
import {getHtyErrUserMessage} from "~/utils/htyErrMessage";

const axiosInstance = axios.create({
    baseURL: BaseUrl,
    withCredentials: true,
})

const RetryPool: {[key: string]: boolean} = {

}

axiosInstance.defaults.headers.post['Content-Type'] = 'application/json';
/**
 * 给header加上token
 */
axiosInstance.interceptors.request.use((options) => {
    let {headers = {}, url, ...rest} = options;
    let headersObj: any = headers;
    let auth = window.localStorage.getItem(HtyAuthToken), sudo = window.localStorage.getItem(HtySudoToken);
    
    // 检查是否是coze接口且在local环境下
    const isCozeApi = url?.startsWith('/coze');
    // @ts-ignore
    const isLocalEnv = AI_SERVER === 'http://localhost:5000';
    
    // 在local环境下的coze接口使用Basic认证而不是HtyToken
    if (isCozeApi && isLocalEnv) {
        headersObj['Authorization'] = 'Basic aHVpd2luZzo5dm9EOVNSMVNMS3h3SnhNeVFNVQ==';
        // 清除可能存在的HtyToken相关头部
        delete headersObj[HtyAuthToken];
        delete headersObj[HtySudoToken];
    } else {
        // 因为又拍云也使用Authorization来传递校验token，防止在上传又拍云时token被错误替代
        if (auth) {
            headersObj[HtyAuthToken] = headersObj[HtyAuthToken] || auth.toString();
            if (sudo) {
                headersObj[HtySudoToken] = sudo.toString();
            }
        }
    }
    if (!headersObj[HtyHostHeader]) {
        // @ts-ignore
        headersObj[HtyHostHeader] = HOST || window.location.host;
    }
    if (url?.startsWith('/api/ngx')) {
        // @ts-ignore
        url = NGX_SERVER + url
    } else if (url?.startsWith('/api/v1/uc')) {
        // @ts-ignore
        url = UC_SERVER + url
    } else if (url?.startsWith('/api/v1/ws')) {
        // @ts-ignore
        url = WS_SERVER + url
    } else if (url?.startsWith('/api/v2/ts')) {
        // @ts-ignore
        url = TS_SERVER + url
    } else if (url?.startsWith('/api/v1/ai')) {
        // @ts-ignore
        url = AI_SERVER + url;
    } else if (url?.startsWith('/coze')) {
        // 开发环境走 Vite 代理避免 CORS；生产请求直连 AI 服务
        const cozePath = '/api/v1/coze' + url.slice(5);
        // @ts-ignore
        url = import.meta.env.DEV ? cozePath : (AI_SERVER + cozePath);
    } else if (url?.startsWith('/api/v1/clazz')) {
        // 必须用绝对 URL：否则在 wx/static 等域打开 H5 时 axios 会把 /api/v1/clazz 发到当前域名，落到 SPA index.html（字符串），触发排课 normalize 报错
        // @ts-ignore vite define
        const kcBase = KC_SERVER || UC_SERVER;
        if (kcBase) {
            url = new URL(url, kcBase.endsWith('/') ? kcBase : `${kcBase}/`).href;
        }
    }

    return {...rest, url, headers: headersObj};
});


axiosInstance.interceptors.response.use(res => res, function (error) {
    const {response, message} = error;
    if (response && response.status) {
        return Promise.resolve(response)
    }
    // clean loading status
    const {load_done} = useLoading()
    load_done();
    closeToast(true);
    return Promise.reject({r: false, e: message});
})


export default function request({url = '', method = 'get', data, params, ...rest}: AxiosRequestConfig): Promise<{statusCode: number, headers: any, r: boolean, d: any, e: string}> {
    if (method.toLowerCase() === "get" && data && !params) {
        params = data;
    }

    return axiosInstance.request({url, method, data, params, ...rest}).then(response => {
        let {statusText, status, data: res, headers, config} = response;
        if (status === 401) {
            const isCozeRequest = url?.startsWith('/coze');
            const rawErr = (res && typeof res === 'object' && (res.e || (res.error && res.error.message))) || 'Login failed';
            const errMsg = res?.hty_err ? getHtyErrUserMessage(res.hty_err, rawErr) : rawErr;
            if (isCozeRequest) {
                return Promise.resolve({ ...res, r: false, e: errMsg, statusCode: 401 });
            }
            const { sudo2 } = useUser();
            if (res.e === UnauthorizedError.HtySudoerTokenErr) {
                window.localStorage.removeItem(HtySudoToken);
                let key = url + method + JSON.stringify(data || {});
                if (!RetryPool[key]) {
                    RetryPool[key] = true;
                    return sudo2().then(() => request({url, method, data, ...rest}));
                }
                delete RetryPool[key];
            }
            window.localStorage.removeItem(HtyAuthToken);
            if (!(res.hty_err && res.hty_err.code == UnauthorizedError.InternalErr)) {
                showFailToast(statusText)
                let retried = Number(window.localStorage.getItem("UnauthorizedRetried") || 0);
                console.log('retry...', retried)
                if (retried < 3) {
                    window.location.href = window.localStorage.getItem(WebViewEntryUrl) || window.location.href;
                    window.localStorage.setItem("UnauthorizedRetried", (retried + 1).toString());
                }
            }
            return Promise.resolve({ ...res, r: false, e: errMsg, statusCode: 401 });
        }
        window.localStorage.removeItem("UnauthorizedRetried")

        const baseErrText = statusText || '网络异常，请联系管理员';
        let resolved: any = {
            r: status >= 200 && status < 300,
            e: baseErrText,
            statusCode: status,
            headers,
        }

        if (typeof res !== 'object' || res === null || Array.isArray(res)) {
            resolved = {...resolved, d: res}
        } else if (typeof res === 'object') {
            resolved = {...resolved, ...res}
        }

        if (status === 413) {
            resolved.e = '文件尺寸太大！'
        } else if (!resolved.r && res?.hty_err) {
            resolved.e = getHtyErrUserMessage(res.hty_err, resolved.e ?? baseErrText);
        }

        // 网关未配置 /api/v1/clazz/ 时 SPA 返回 HTML，axios 会得到 string，原先会当作 d 传入业务层触发「排课数据格式异常」
        const rawBody = resolved.d;
        if (typeof rawBody === 'string' && /^\s*</.test(rawBody)) {
            resolved.r = false;
            resolved.d = undefined;
            resolved.e =
                '接口返回了页面而非 JSON（请确认 Nginx 已将 /api/v1/clazz/ 反代到 htykc）';
        }

        const finalUrl = (config && typeof config.url === 'string' ? config.url : '') || url || '';
        const isClazzApi =
            finalUrl.includes('/api/v1/clazz') ||
            (typeof url === 'string' && url.includes('/api/v1/clazz'));
        if (isClazzApi && (!resolved.r || status >= 400)) {
            let bodySnippet = '';
            if (typeof res === 'string') {
                bodySnippet = res.slice(0, 240);
            } else if (res && typeof res === 'object') {
                try {
                    bodySnippet = JSON.stringify(res).slice(0, 500);
                } catch {
                    bodySnippet = '[object]';
                }
            }
            console.warn('[ClazzApi]', {
                method: (config?.method || method || 'get').toUpperCase(),
                url: finalUrl,
                httpStatus: status,
                statusText,
                businessOk: resolved.r,
                toastOrErr: resolved.e,
                hty_err: resolved.hty_err,
                bodySnippet,
            });
        }

        return Promise.resolve(resolved);
    })
};
