import { fetchRefreshToken } from '@/services/login';
import { formatToken, getToken, setToken } from '@/utils/authUtils';
import { stringMd5, stringSha1 } from '@/utils/crypto';
import { IRequestInterceptorAxios, RequestConfig } from '@/utils/request/types';
import { currentUnixTimestamp } from '@/utils/timeUtils';

class TokenRefresh {
    private static isRefreshing: boolean = false;
    private static requests: { (token: string): void }[] = [];

    private static retryOriginalRequest(config: RequestConfig) {
        return new Promise((resolve) => {
            TokenRefresh.requests.push((accessToken: string) => {
                config.headers = config.headers ?? {};
                config.headers['Authorization'] = formatToken(accessToken);
                resolve(config);
            });
        });
    }

    public static beforeRequestRefreshTokenFirst(config: RequestConfig, refreshToken: string) {
        return new Promise((resolve, reject) => {
            if (!TokenRefresh.isRefreshing) {
                TokenRefresh.isRefreshing = true;
                fetchRefreshToken({ headers: { Authorization: formatToken(refreshToken) } })
                    .then(
                        (res: API.RefreshTokenResult) => {
                            const info = res.data as API.UserInfo;
                            const accessToken = info.accessToken!;
                            setToken({
                                accessToken: accessToken,
                                accessExpires: info.accessExpires,
                                refreshToken: info.refreshToken,
                            });
                            config.headers = config.headers ?? {};
                            config.headers['Authorization'] = formatToken(accessToken);
                            TokenRefresh.requests.forEach((cb) => cb(accessToken));
                            TokenRefresh.requests = [];
                        },
                        (err: Error) => {
                            reject(err);
                        }
                    )
                    .finally(() => {
                        TokenRefresh.isRefreshing = false;
                    });
            }
            resolve(TokenRefresh.retryOriginalRequest(config));
        });
    }
}

export const authHeaderInterceptor: IRequestInterceptorAxios = (config: RequestConfig) => {
    config.headers = config.headers ?? {};
    const existingTs = config.headers['X-Request-Timestamp'];
    let ts: string | number;
    if (existingTs != null && existingTs !== '') {
        ts = String(existingTs);
    } else {
        ts = currentUnixTimestamp();
        config.headers['X-Request-Timestamp'] = ts;
    }
    config.headers['X-Request-Sign'] = stringSha1(
        String(ts) + stringMd5(navigator.userAgent ?? '')
    );
    if (config.requireToken === false) return config; // 默认所有接口都需要 token，如果接口不需要 token，需要显示设置 requireToken 为 false
    // if (config.requireToken !== true) return config; // 默认所有接口都不需要 token，如果接口需要 token，需要显示设置 requireToken 为 true

    const data = getToken();
    const expires = (data?.accessExpires ?? 0) - currentUnixTimestamp() > 0;
    if (data?.accessToken && expires) {
        config.headers['Authorization'] = formatToken(data.accessToken);
        return config;
    } else {
        return TokenRefresh.beforeRequestRefreshTokenFirst(config, data?.refreshToken || 'none'); // 如果 refreshToken 为空，接口会返回 401 错误，跳转到登录页
    }
};
