/**
 * @desc 触发文件下载。优先拉取 Blob 后在本地触发保存，避免 IE/Edge IE 模式下对跨域直链 `a.click()` 新开页签。
 * @remarks 阿里云 OSS 等跨域地址若未配置带 Cookie 的 CORS，须使用 omit 凭证，否则预检会失败。
 */

type NavigatorWithMsSave = Navigator & {
    msSaveOrOpenBlob?: (blob: Blob, defaultName?: string) => boolean;
};

/**
 * 将含中文、空格等非 ASCII 的路径规范为百分号编码，便于 XHR/fetch/iframe 在各浏览器中一致请求。
 * 网关拼接的预览 URL 若含未编码空格，`new URL` 可能失败，会先尝试把空格替换为 %20 再解析。
 */
export function normalizeHttpUrlForRequest(raw: string): string {
    const trimmed = raw.trim();
    try {
        return new URL(trimmed).href;
    } catch {
        const spaced = trimmed.replace(/ /g, '%20');
        try {
            return new URL(spaced).href;
        } catch {
            return spaced;
        }
    }
}

function shouldSendCredentialsForUrl(url: string): boolean {
    try {
        if (typeof window === 'undefined' || !window.location?.origin)
            return false;
        return new URL(url).origin === window.location.origin;
    } catch {
        return false;
    }
}

function getBlobViaXhr(url: string): Promise<Blob> {
    return new Promise((resolve, reject) => {
        const xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.responseType = 'blob';
        xhr.withCredentials = shouldSendCredentialsForUrl(url);
        xhr.onload = () => {
            if (xhr.status >= 200 && xhr.status < 300) {
                resolve(xhr.response as Blob);
            } else {
                reject(new Error(String(xhr.status)));
            }
        };
        xhr.onerror = () => reject(new Error('xhr'));
        xhr.send();
    });
}

async function getBlobFromUrl(url: string): Promise<Blob> {
    const normalized = normalizeHttpUrlForRequest(url);
    const docMode =
        typeof document !== 'undefined'
            ? (document as Document & { documentMode?: number }).documentMode
            : undefined;
    if (typeof docMode === 'number' && docMode > 0) {
        return getBlobViaXhr(normalized);
    }
    if (typeof fetch === 'function') {
        const res = await fetch(normalized, {
            credentials: shouldSendCredentialsForUrl(normalized)
                ? 'include'
                : 'omit',
            mode: 'cors',
        });
        if (!res.ok) throw new Error(String(res.status));
        return res.blob();
    }
    return getBlobViaXhr(normalized);
}

function downloadBlobInBrowser(blob: Blob, filename: string): void {
    const safeName = filename.trim() || 'download';
    const nav = navigator as NavigatorWithMsSave;
    if (typeof nav.msSaveOrOpenBlob === 'function') {
        nav.msSaveOrOpenBlob(blob, safeName);
        return;
    }
    const objectUrl = URL.createObjectURL(blob);
    try {
        const link = document.createElement('a');
        link.href = objectUrl;
        link.rel = 'noopener noreferrer';
        link.setAttribute('download', safeName);
        link.style.display = 'none';
        document.body.appendChild(link);
        link.click();
        document.body.removeChild(link);
    } finally {
        URL.revokeObjectURL(objectUrl);
    }
}

/**
 * 依赖服务端 Content-Disposition；不再对远程 URL 使用 `a.click()`，以免 IE 新开页签。
 */
function openUrlInHiddenIframe(url: string): void {
    const normalized = normalizeHttpUrlForRequest(url);
    const iframe = document.createElement('iframe');
    iframe.style.cssText =
        'position:fixed;left:-9999px;top:-9999px;width:0;height:0;border:0;';
    iframe.src = normalized;
    document.body.appendChild(iframe);
    window.setTimeout(() => {
        try {
            document.body.removeChild(iframe);
        } catch {
            /* noop */
        }
    }, 120000);
}

export async function triggerBrowserDownload(
    url: string,
    filename: string
): Promise<void> {
    const safeName = (filename || 'download').trim() || 'download';

    try {
        const blob = await getBlobFromUrl(url);
        downloadBlobInBrowser(blob, safeName);
        return;
    } catch {
        openUrlInHiddenIframe(url);
    }
}
