type Func = (...args: any[]) => any;
/**
 * 防抖函数
 * @param { Function } func 函数
 * @param { Number } delay 防抖时间
 * @param { Boolean } immediate 是否立即执行
 * @param { Function } resultCallback
 */
export function debounce(
    func: Func,
    delay: number = 500,
    immediate?: boolean,
    resultCallback?: Func
) {
    let timer: null | ReturnType<typeof setTimeout> = null;
    let isInvoke = false;
    const _debounce = function (this: unknown, ...args: any[]) {
        return new Promise((resolve, reject) => {
            if (timer) clearTimeout(timer);
            if (immediate && !isInvoke) {
                try {
                    const result = func.apply(this, args);
                    if (resultCallback) resultCallback(result);
                    resolve(result);
                } catch (e) {
                    reject(e);
                }
                isInvoke = true;
            } else {
                timer = setTimeout(() => {
                    try {
                        const result = func.apply(this, args);
                        if (resultCallback) resultCallback(result);
                        resolve(result);
                    } catch (e) {
                        reject(e);
                    }
                    isInvoke = false;
                    timer = null;
                }, delay);
            }
        });
    };
    _debounce.cancel = function () {
        if (timer) clearTimeout(timer);
        isInvoke = false;
        timer = null;
    };
    return _debounce;
}

/**
 * 节流函数
 * @param { Function } func
 * @param { Boolean } interval
 * @param { Object } options
 * leading:初始 trailing:结尾
 */
export function throttle(
    func: Func,
    interval: number,
    options = { leading: false, trailing: true }
) {
    let timer: null | ReturnType<typeof setTimeout> = null;
    let lastTime = 0;
    const { leading, trailing } = options;
    const _throttle = function (this: unknown, ...args: any[]) {
        const nowTime = Date.now();
        if (!lastTime && !leading) lastTime = nowTime;
        const remainTime = interval - (nowTime - lastTime);
        if (remainTime <= 0) {
            if (timer) {
                clearTimeout(timer);
                timer = null;
            }
            lastTime = nowTime;
            func.apply(this, args);
        }
        if (trailing && !timer) {
            timer = setTimeout(() => {
                lastTime = !leading ? 0 : Date.now();
                timer = null;
                func.apply(this, args);
            }, remainTime);
        }
    };
    _throttle.cancel = function () {
        if (timer) clearTimeout(timer);
        timer = null;
        lastTime = 0;
    };
    return _throttle;
}

/**
 * 驼峰转换下划线
 * @param { String } name
 */
export function toLine(name: string) {
    return name.replace(/([A-Z])/g, '_$1').toLowerCase();
}

/*
  自定义保留 precision 位小数，并使用 separator 分隔符进行数字格式化
  value：格式化目标数字
  precision：精度，保留小数点后几位，默认2位
  separator：千分位分隔符，默认为','
  decimal：小数点符号，默认'.'
  prefix：前缀字符，默认''
  suffix：后缀字符，默认''
  formatNumber(123456789.87654321, 2, ',') // 123,456,789.88
*/
export function formatNumber(
    value: number | string,
    precision = 2,
    separator = ',',
    decimal = '.',
    prefix = '',
    suffix = ''
): string {
    if (Number(value) === 0) {
        return Number(value).toFixed(precision);
    }
    if (!value) {
        return '';
    }
    value = Number(value).toFixed(precision);
    value += '';
    const nums = value.split('.');
    let integer = nums[0];
    const decimals = nums.length > 1 ? decimal + nums[1] : '';
    const reg = /(\d+)(\d{3})/;
    function isNumber(value: any) {
        return Object.prototype.toString.call(value) === '[object Number]';
    }
    if (separator && !isNumber(separator)) {
        while (reg.test(integer)) {
            integer = integer.replace(reg, '$1' + separator + '$2');
        }
    }
    return prefix + integer + decimals + suffix;
}

/**
 * PC / 移动端设备判断
 * 用于路由划分、样式适配等
 */

/** 视口宽度 >= 此值视为 PC 端（px） */
export const PC_BREAKPOINT = 768;

export function isPC(): boolean {
    if (typeof window === 'undefined') return true;
    return window.innerWidth >= PC_BREAKPOINT;
}

export function isMobile(): boolean {
    return !isPC();
}

export type DeviceType = 'pc' | 'mobile';

export function getDeviceType(): DeviceType {
    return isPC() ? 'pc' : 'mobile';
}

export interface HbFileItem_Intf {
    //  文件名称
    fileName: string;
    //  文件预览URL
    previewUrl: string;
    //  文件下载URL
    downloadUrl: string;
    //  文件描述
    attachmentDesc?: string;
    //  文件ID
    attachmentId?: string;
    //  文件上传时间
    uploadDate?: string;

    [key: string]: unknown;
}

/** 可从文件名或完整 URL 解析扩展名（忽略 ?# 后参数） */
export const getFileExtensionFromName = (nameOrUrl: string): string => {
    const s = nameOrUrl.trim().split(/[?#]/)[0];
    const match = s.match(/\.([^.]+)$/);
    return match ? match[1].toLowerCase() : '';
};

const PREVIEW_IMAGE_EXT = new Set([
    'png',
    'jpg',
    'jpeg',
    'gif',
    'bmp',
    'webp',
    'svg',
]);

/** 是否为可内嵌预览的图片类型（依据文件名或 URL） */
export const isAttachmentImagePreview = (fileNameOrUrl: string): boolean => {
    return PREVIEW_IMAGE_EXT.has(getFileExtensionFromName(fileNameOrUrl));
};

/** 供应商附件：仅 PDF 与常见图片支持内嵌预览 */
export const isAttachmentPreviewable = (fileName: string): boolean => {
    const ext = getFileExtensionFromName(fileName);
    return ext === 'pdf' || PREVIEW_IMAGE_EXT.has(ext);
};

export const getAttachmentDownloadUrl = <T extends HbFileItem_Intf>(
    row: T
): string => {
    return row.downloadUrl;
};
/**
 * @desc 在 `attachmentInfos` 中定位被点击的附件下标。
 * 仅依赖 fileUuid 会在接口缺省、全空或重复时始终命中第一项，需结合列表下标与预览/下载地址兜底。
 */
export const resolveAttachmentIndexInList = <T extends HbFileItem_Intf>(
    list: T[],
    item: T,
    options?: {
        /** 列表 v-for 下标，优先使用 */
        preferredIndex?: number;
    }
): number => {
    const getUrl = (row: T) => row.previewUrl;

    const preferredIndex = options?.preferredIndex;
    if (
        typeof preferredIndex === 'number' &&
        preferredIndex >= 0 &&
        preferredIndex < list.length
    ) {
        const row = list[preferredIndex];
        if (row === item) return preferredIndex;
        const rowDate = 'uploadDate' in row ? row.uploadDate || '' : '';
        const itemDate = 'uploadDate' in item ? item.uploadDate || '' : '';

        if (
            getUrl(row) === getUrl(item) &&
            (row.attachmentDesc || '') === (item.attachmentDesc || '') &&
            rowDate === itemDate
        ) {
            return preferredIndex;
        }
    }

    const uid = item.attachmentId;
    if (uid) {
        const idx = list.findIndex((a) => {
            const s = a as T;
            const id = s.attachmentId;
            return id === uid;
        });
        if (idx >= 0) return idx;
    }

    const previewUrl = getUrl(item);
    if (previewUrl) {
        const idx = list.findIndex((a) => getUrl(a) === previewUrl);
        if (idx >= 0) return idx;
    }

    const dl = getAttachmentDownloadUrl<HbFileItem_Intf>(item);
    if (dl) {
        const idx = list.findIndex((a) => getAttachmentDownloadUrl(a) === dl);
        if (idx >= 0) return idx;
    }

    return list.indexOf(item);
};
