import {
    inject,
    ref,
    toValue,
    watch,
    type MaybeRefOrGetter,
    type Ref,
} from 'vue';
import type { InjectionKey } from 'vue';

/** 图标库的默认 iconfont CSS 地址 */
export const HB_ICON_DEFAULT_CSS_URL =
    '//at.alicdn.com/t/c/font_5174416_x116sg1gb1q.css';

export const HB_ICON_DEFAULT_SYMBOL_CSS_URL =
    '//at.alicdn.com/t/c/font_5174416_x116sg1gb1q.js';

/** 图标渲染模式：字体 class 或多色 SVG Symbol */
export type HbLibIconMode = 'font' | 'symbol';

/** 通过 app.use(HbLibIconList, options) 注入的全局配置 */
export interface HbLibIconGlobalOptions {
    /** iconfont 资源地址，支持 `.css`（字体图标）或 `.js`（Symbol / 彩色 SVG） */
    cssUrl?: string;
}

/** provide/inject 的 key */
export const HB_ICON_GLOBAL_KEY: InjectionKey<HbLibIconGlobalOptions> =
    Symbol('HB_ICON_GLOBAL_KEY');

// app.use(...) 注入的全局默认配置
const globalOptions = inject<HbLibIconGlobalOptions>(HB_ICON_GLOBAL_KEY, {});

export interface UseIconfontResult {
    /** 字体模式下为 @font-face 的 font-family；Symbol 模式下为空字符串 */
    iconBaseClass: Ref<string>;
    /** 字体模式下为图标 class 公共前缀；Symbol 模式下为 symbol id 的公共前缀（常见为 `icon-`） */
    iconPrefix: Ref<string>;
    /** 去掉公共前缀后的名称列表，可作为 `HbLibIcon` 的 name  */
    iconClasses: Ref<string[]>;
    /** `font`：class + 字体；`symbol`：`<use href="#前缀+name">`，保留路径中的多色填充 */
    iconMode: Ref<HbLibIconMode>;
}

interface IconfontParseResult {
    baseClass: string;
    prefix: string;
    names: string[];
}

/** 是否为 iconfont「Symbol（彩色 SVG）」类型的 `.js` 地址 */
export function isIconfontSymbolJsUrl(url: string): boolean {
    const path = url.trim().split(/[?#]/)[0].toLowerCase();
    return path.endsWith('.js');
}

function stableIdPart(url: string): string {
    let h = 2166136261;
    for (let i = 0; i < url.length; i++) {
        h ^= url.charCodeAt(i);
        h = Math.imul(h, 16777619);
    }
    return (h >>> 0).toString(36);
}

/**
 * 解析 iconfont 的 CSS 文本，提取组件渲染和图标选择所需的信息。
 *
 * 解析规则：
 * 1. 从 @font-face 中取 font-family 作为基础 class，例如 "strongQ"。
 * 2. 读取所有 `.xxx:before { content: "\eXXX" }` 规则中的 xxx，作为完整图标 class。
 * 3. 计算完整图标 class 的最长公共前缀，例如 "strong-"。
 * 4. 将公共前缀从完整 class 中移除，得到更适合使用者传入的图标 name。
 */
function parseIconfontCss(css: string): IconfontParseResult {
    const fontFaceMatch = css.match(
        /@font-face\s*\{[\s\S]*?font-family\s*:\s*["']([^"']+)["']/
    );
    const baseClass = fontFaceMatch?.[1] ?? '';

    const iconClassRegex =
        /\.([\w-]+)\s*:before\s*\{\s*content\s*:\s*["']\\[\da-fA-F]+["']\s*;?\s*\}/g;
    const fullClasses: string[] = [];
    let match: RegExpExecArray | null;
    while ((match = iconClassRegex.exec(css)) !== null) {
        fullClasses.push(match[1]);
    }

    const prefix = findCommonPrefix(fullClasses);
    const names = prefix
        ? fullClasses.map((className) => className.slice(prefix.length))
        : fullClasses.slice();

    return { baseClass, prefix, names };
}

/**
 * 从 iconfont 生成的 `.js` 文本中提取 `<symbol id="...">`，规则与 CSS 模式一致，
 * 用最长公共前缀拆出简短的 `iconClasses` name。
 */
function parseIconfontJs(
    js: string
): Pick<IconfontParseResult, 'prefix' | 'names'> {
    const fullIds: string[] = [];
    const symbolIdRe = /<symbol\b[^>]*?\bid\s*=\s*["']([^"']+)["']/gi;
    let m: RegExpExecArray | null;
    while ((m = symbolIdRe.exec(js)) !== null) {
        fullIds.push(m[1]);
    }
    const prefix = findCommonPrefix(fullIds);
    const names = prefix
        ? fullIds.map((id) => id.slice(prefix.length))
        : fullIds.slice();
    return { prefix, names };
}

/**
 * 计算字符串数组的最长公共前缀。
 */
function findCommonPrefix(list: string[]): string {
    if (list.length === 0) return '';
    let prefix = list[0];
    for (let i = 1; i < list.length; i++) {
        while (prefix && !list[i].startsWith(prefix)) {
            prefix = prefix.slice(0, -1);
        }
        if (!prefix) break;
    }
    return prefix;
}

/**
 * 将 iconfont CSS 注入到页面。
 */
function injectCss(cssText: string, key: string) {
    if (typeof document === 'undefined' || document.getElementById(key)) return;

    const style = document.createElement('style');
    style.id = key;
    style.textContent = cssText;
    document.head.appendChild(style);
}

/**
 * 执行 iconfont `.js`（内部为 Symbol 精灵图脚本）。同一 resolvedUrl 只注入一次；
 * 使用单次 fetch 得到的文本 + Blob URL 执行，避免重复请求。
 */
function injectIconfontJs(jsText: string, resolvedUrl: string): Promise<void> {
    if (typeof document === 'undefined') return Promise.resolve();

    const id = `hb-iconfont-js-${stableIdPart(resolvedUrl)}`;
    if (document.getElementById(id)) return Promise.resolve();

    return new Promise((resolve, reject) => {
        const blob = new Blob([jsText], { type: 'text/javascript' });
        const blobUrl = URL.createObjectURL(blob);
        const script = document.createElement('script');
        script.id = id;
        script.async = true;
        script.onload = () => {
            URL.revokeObjectURL(blobUrl);
            resolve();
        };
        script.onerror = () => {
            URL.revokeObjectURL(blobUrl);
            reject(
                new Error(
                    `[HbLibIcon] iconfont JS 脚本执行失败：${resolvedUrl}`
                )
            );
        };
        script.src = blobUrl;
        document.head.appendChild(script);
    });
}

/**
 * 加载并解析 iconfont 资源（CSS 字体或 Symbol JS）的组合式函数。
 *
 * @param cssUrl 资源地址：`.css` 为字体图标，`.js` 为 Symbol 彩色 SVG。
 */
export function useIconfont(
    cssUrl?: MaybeRefOrGetter<string>
): UseIconfontResult {
    if (!cssUrl) {
        cssUrl = globalOptions?.cssUrl || HB_ICON_DEFAULT_CSS_URL;
    }

    const iconBaseClass = ref('');
    const iconPrefix = ref('');
    const iconClasses = ref<string[]>([]);
    const iconMode = ref<HbLibIconMode>('font');
    let requestId = 0;

    const loadIconfont = async (url: string) => {
        const currentRequestId = ++requestId;
        const resolvedUrl =
            url || globalOptions?.cssUrl || HB_ICON_DEFAULT_CSS_URL;

        if (!resolvedUrl) {
            iconBaseClass.value = '';
            iconPrefix.value = '';
            iconClasses.value = [];
            iconMode.value = 'font';
            return;
        }

        if (isIconfontSymbolJsUrl(resolvedUrl)) {
            try {
                const res = await fetch(resolvedUrl);
                const jsText = await res.text();
                if (currentRequestId !== requestId) return;

                const { prefix, names } = parseIconfontJs(jsText);
                await injectIconfontJs(jsText, resolvedUrl);
                if (currentRequestId !== requestId) return;

                iconMode.value = 'symbol';
                iconBaseClass.value = '';
                iconPrefix.value = prefix;
                iconClasses.value = names;
            } catch (err) {
                console.error(
                    '[HbLibIcon] 加载 iconfont Symbol 失败：',
                    resolvedUrl,
                    err
                );
            }
            return;
        }

        try {
            const res = await fetch(resolvedUrl);
            const cssText = await res.text();
            injectCss(cssText, 'hb-iconfont-' + resolvedUrl);

            const { baseClass, prefix, names } = parseIconfontCss(cssText);
            if (currentRequestId !== requestId) return;

            iconMode.value = 'font';
            iconBaseClass.value = baseClass;
            iconPrefix.value = prefix;
            iconClasses.value = names;
        } catch (err) {
            console.error(
                '[HbLibIcon] 加载 iconfont CSS 失败：',
                resolvedUrl,
                err
            );
        }
    };

    watch(() => toValue(cssUrl), loadIconfont, { immediate: true });
    return {
        iconBaseClass,
        iconPrefix,
        iconClasses,
        iconMode,
    };
}
