import Hashids from 'hashids';
import blake from 'blakejs';
import CryptoJS from 'crypto-js';

// 获取uuid
export function generateUUID() {
    var d = new Date().getTime();
    var d2 = ((typeof performance !== 'undefined') && performance.now && (performance.now() * 1000)) || 0;
    return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function (c) {
        var r = Math.random() * 16;
        if (d > 0) {
            r = (d + r) % 16 | 0;
            d = Math.floor(d / 16);
        } else {
            r = (d2 + r) % 16 | 0;
            d2 = Math.floor(d2 / 16);
        }
        return (c === 'x' ? r : (r & 0x3 | 0x8)).toString(16);
    });
}

/**
 * 过滤掉可信度(confidence)小于0.6的数据项
 * @param {Array} data - 要过滤的数组，数组项应包含confidence属性
 * @returns {Array} 过滤后的新数组
 */
export function filteConfidence(data) {
    // 检查输入是否为数组
    if (!Array.isArray(data)) {
        console.warn('filteConfidence: 输入参数不是数组');
        return [];
    }

    // 过滤并返回新数组
    return data.filter(item => {
        return item && typeof item === 'object' &&
            'confidence' in item &&
            Number(item.confidence) >= 0.6;
    });
}

/**
 * 根据检测结果截取图片中的多个区域
 * @param {HTMLImageElement|String} image - 图片元素或图片URL
 * @param {Array} detectionResults - 检测结果数组，包含box坐标
 * @param {Object} options - 可选配置
 * @param {Number} options.minConfidence - 最小置信度阈值，默认0.5
 * @param {String} options.outputFormat - 输出格式，默认'image/png'
 * @param {Number} options.outputQuality - 输出质量(0-1)，默认0.92
 * @returns {Promise<Array<{blob: Blob, data: Object}>>} 返回截取图片和相关数据的数组
 */
export async function cropDetectedRegions(image, detectionResults, options = {}) {
    // 合并默认配置
    const {
        minConfidence = 0.6,
        outputFormat = 'image/png',
        outputQuality = 0.92
    } = options;

    // 如果传入的是URL字符串，先加载图片
    const img = typeof image === 'string'
        ? await loadImage(image)
        : image;

    // 创建画布
    const canvas = document.createElement('canvas');
    const ctx = canvas.getContext('2d');

    // 存储所有截取结果
    const croppedResults = [];

    // 遍历所有检测结果
    for (const result of detectionResults) {
        // 检查置信度是否达标
        if (result.confidence < minConfidence) {
            console.log(`跳过置信度低于阈值(${minConfidence})的检测结果:`, result);
            continue;
        }

        const { x1, y1, x2, y2 } = result.box;
        const width = x2 - x1;
        const height = y2 - y1;

        // 检查坐标是否有效
        if (!isValidBox(x1, y1, x2, y2, img.width, img.height)) {
            console.warn('无效的坐标区域:', result.box);
            continue;
        }

        // 设置画布尺寸
        canvas.width = width;
        canvas.height = height;

        // 清空画布
        ctx.clearRect(0, 0, width, height);

        // 绘制截取区域
        ctx.drawImage(
            img,
            x1, y1,         // 源图像裁剪起始坐标
            width, height,   // 源图像裁剪宽高
            0, 0,            // 画布起始坐标
            width, height    // 画布绘制宽高
        );

        // 将画布内容转为Blob对象
        const blob = await new Promise((resolve) => {
            canvas.toBlob(
                (blob) => resolve(blob),
                outputFormat,
                outputQuality
            );
        });

        // 保存结果及相关数据
        croppedResults.push({
            blob,
            data: {
                ...result,
                cropInfo: {
                    x: x1,
                    y: y1,
                    width,
                    height
                }
            }
        });
    }

    return croppedResults;
}

/**
 * 加载图片
 * @param {String} url - 图片URL
 * @returns {Promise<HTMLImageElement>}
 */
export function loadImage(url) {
    return new Promise((resolve, reject) => {
        const img = new Image();
        img.crossOrigin = 'Anonymous'; // 处理跨域问题
        img.onload = () => resolve(img);
        img.onerror = reject;
        img.src = url;
    });
}

/**
 * 检查坐标框是否有效
 * @param {Number} x1 - 左上角x坐标
 * @param {Number} y1 - 左上角y坐标
 * @param {Number} x2 - 右下角x坐标
 * @param {Number} y2 - 右下角y坐标
 * @param {Number} imgWidth - 图片宽度
 * @param {Number} imgHeight - 图片高度
 * @returns {Boolean}
 */
function isValidBox(x1, y1, x2, y2, imgWidth, imgHeight) {
    return (
        x1 >= 0 && y1 >= 0 &&
        x2 > x1 && y2 > y1 &&
        x2 <= imgWidth && y2 <= imgHeight
    );
}


// 全局临时变量，用于存储最近一次请求的签名信息
let lastRequestKey = '';
let lastTimestamp = 0;
let lastUsedTimestamp = 0;
let collisionCount = 0;

/**
 * 生成请求唯一标识key
 * @param {number} timestamp - 时间戳
 * @param {string} method - 请求方法
 * @param {string} urlPath - API路径
 * @returns {string} 唯一标识
 */
function getRequestKey(timestamp, method, urlPath) {
    return `${timestamp}_${method}_${urlPath}`;
}

/**
 * 获取去重后的时间戳
 * 处理同一毫秒内相同接口的重复请求
 * @param {number} originalTimestamp - 原始时间戳
 * @param {string} method - 请求方法
 * @param {string} urlPath - API路径
 * @returns {number} 处理后的时间戳
 */
function getDeduplicatedTimestamp(originalTimestamp, method, urlPath) {
    const currentKey = getRequestKey(originalTimestamp, method, urlPath);

    // 如果当前请求与上一次请求的时间戳、方法、API路径完全相同
    if (currentKey === lastRequestKey && originalTimestamp === lastTimestamp) {
        // 碰撞次数增加
        collisionCount++;
        // 时间戳增加碰撞次数毫秒
        const adjustedTimestamp = originalTimestamp + collisionCount;
        console.warn(`检测到重复请求: ${method} ${urlPath} @ ${originalTimestamp}ms, 调整时间戳为: ${adjustedTimestamp}ms (碰撞次数: ${collisionCount})`);
        return adjustedTimestamp;
    } else {
        // 不同请求，重置碰撞计数
        collisionCount = 0;
        // 更新最后一次请求的记录
        lastRequestKey = currentKey;
        lastTimestamp = originalTimestamp;
        return originalTimestamp;
    }
}


/**
 * 生成sign签名
 * @param {Object} config - axios请求配置
 * @param {string} udid - 用户设备ID（22位短udid）
 * @param {number} timestamp - 时间戳（毫秒） - 由调用方传入，确保与生成salt时使用的时间一致，避免因时间差导致的sign不匹配问题·
 * @returns {Promise<string>} sign值
 */
export async function generateSign(config, udid, timestamp) {
    try {
        // 1. 准备模版字典
        const reqMap = new Map();

        // 获取请求方法
        const method = config.method?.toUpperCase() || 'GET';

        // 获取host（从baseURL或当前域名） 本地启动要求host为xsynergy.gxtech.ltd
        // let host = 'xsynergy.gxtech.ltd';
        let host = '';
        // console.log('请求配置:', config.baseURL)
        if (config.baseURL) {
            try {
                const urlObj = new URL(config.baseURL);
                // console.log('解析baseURL成功:', urlObj);
                host = urlObj.host;
            } catch (e) {
                // console.log('解析baseURL失败，使用window.location.host:',  window.location);
                host = window.location.host;
            }
        } else {
            host = window.location.host;
        }

        // console.log('请求host:', host);

        // 获取完整URL路径（不含域名和查询参数）
        let urlPath = config.url || '';
        // 移除baseURL部分
        if (config.baseURL && urlPath.startsWith(config.baseURL)) {
            urlPath = urlPath.substring(config.baseURL.length);
        }
        // 移除查询参数
        const queryIndex = urlPath.indexOf('?');
        if (queryIndex !== -1) {
            urlPath = urlPath.substring(0, queryIndex);
        }

        // 获取user-agent和token
        const userAgent = config.headers?.['X-User-Agent'];
        // const userAgent = config.headers?.['X-User-Agent'] || navigator.userAgent;
        const token = config.headers?.['Authorization'] || '';

        // 填充基础模板 - agent字段包含userAgent和token
        reqMap.set('host', [host]);
        reqMap.set('method', [method]);
        reqMap.set('agent', [userAgent, token]);
        reqMap.set('uri', [urlPath]);

        // 2. 处理URL查询参数（不包括sign本身）
        let fullUrl = config.url || '';
        if (config.baseURL && !fullUrl.startsWith('http')) {
            fullUrl = config.baseURL + fullUrl;
        }

        try {
            const urlObj = new URL(fullUrl, window.location.origin);
            const searchParams = urlObj.searchParams;

            // 遍历所有查询参数，排除sign
            for (const [key, value] of searchParams.entries()) {
                if (key !== 'sign') {
                    // 处理数组参数（key[]格式）
                    const normalizedKey = key.replace('[]', '');
                    if (reqMap.has(normalizedKey)) {
                        reqMap.get(normalizedKey).push(value);
                    } else {
                        reqMap.set(normalizedKey, [value]);
                    }
                }
            }
        } catch (e) {
            console.warn('解析URL参数失败:', e);
        }

        // 3. 处理GET请求的params参数
        if (config.method?.toLowerCase() === 'get' && config.params) {
            for (const [key, value] of Object.entries(config.params)) {
                if (key !== 'sign') {
                    const strValue = String(value);
                    const normalizedKey = key.replace('[]', '');
                    if (reqMap.has(normalizedKey)) {
                        reqMap.get(normalizedKey).push(strValue);
                    } else {
                        reqMap.set(normalizedKey, [strValue]);
                    }
                }
            }
        }

        // 4. 处理请求体（POST、PUT等）
        if (config.method?.toLowerCase() !== 'get' && config.data) {
            const contentType = config.headers?.['Content-Type'] || '';

            if (contentType.includes('application/json')) {
                // JSON数据 - 后端用"json"作为key
                const jsonStr = typeof config.data === 'object'
                    ? JSON.stringify(config.data)
                    : String(config.data);
                reqMap.set('json', [jsonStr]);

            } else if (contentType.includes('application/x-www-form-urlencoded')) {
                // form-urlencoded数据
                let formData = {};
                if (typeof config.data === 'object') {
                    formData = config.data;
                } else if (typeof config.data === 'string') {
                    const params = new URLSearchParams(config.data);
                    formData = Object.fromEntries(params.entries());
                }

                // 添加每个表单字段到reqMap
                for (const [key, value] of Object.entries(formData)) {
                    const strValue = String(value);
                    const normalizedKey = key.replace('[]', '');
                    if (reqMap.has(normalizedKey)) {
                        reqMap.get(normalizedKey).push(strValue);
                    } else {
                        reqMap.set(normalizedKey, [strValue]);
                    }
                }

            } else if (contentType.includes('multipart/form-data')) {
                // multipart/form-data
                if (config.data instanceof FormData) {
                    for (const [key, value] of config.data.entries()) {
                        if (!(value instanceof File || value instanceof Blob)) {
                            const strValue = String(value);
                            const normalizedKey = key.replace('[]', '');
                            if (reqMap.has(normalizedKey)) {
                                reqMap.get(normalizedKey).push(strValue);
                            } else {
                                reqMap.set(normalizedKey, [strValue]);
                            }
                        }
                    }
                }
            }
        }

        // 5. 生成salt - 完全匹配后端的genSalt函数
        // 后端: GenTokenSalt(uid, udid + signSalt)
        // signSalt固定为: publicSignSalt
        const signSalt = 'publicSignSalt';
        const salt = await generateSalt(udid, signSalt);

        // 6. 参数排序并进行HMAC-MD5哈希
        const sortedKeys = Array.from(reqMap.keys()).sort();
        // console.log('排序后的参数键:', sortedKeys);

        // 创建HMAC-MD5
        const hmac = CryptoJS.algo.HMAC.create(CryptoJS.algo.MD5, salt);

        // 按排序后的key拼接值
        let contentStr = '';
        for (const key of sortedKeys) {
            const values = reqMap.get(key);
            const joinedValue = values.join('');
            contentStr += joinedValue;
            hmac.update(joinedValue);
        }

        // 获取最终的hash字符串
        const hashStr = hmac.finalize().toString(CryptoJS.enc.Hex).toUpperCase();

        // 7. 分段拆散计算
        const ints = new Array(6).fill(0);

        // 拆封模版
        const parts = [
            [0, 7],
            [7, 7],
            [14, 4],
            [18, 7],
            [25, 7]
        ];

        // 使用模版将hashStr拆为5个hex字串并转为整数，再倒序排列
        for (let i = 0; i < parts.length; i++) {
            // console.log(`拆分hashStr - part ${i}:`, hashStr.substring(parts[i][0], parts[i][0] + parts[i][1]));
            const [start, length] = parts[i];
            const hexStr = hashStr.substring(start, start + length);
            const intValue = parseInt(hexStr, 16);
            // console.log(`转换为整数 - part ${i}:`, intValue);
            ints[4 - i] = intValue;
        }

        // 8. 获取调用接口时间戳（毫秒）并进行去重处理
        const now = timestamp || Date.now();

        // 对时间戳进行去重处理（处理同一毫秒内相同接口的重复请求）
        const adjustedTimestamp = getDeduplicatedTimestamp(now, method, urlPath);
        const ts = adjustedTimestamp.toString();

        // 将时间拆分为两部分
        const t0 = parseInt(ts.substring(0, ts.length - 9), 10);
        const t1 = parseInt(ts.substring(ts.length - 9), 10);

        ints[2] += t0;
        ints[5] = t1;

        // 记录实际使用的时间戳，用于后续的重复检测
        lastUsedTimestamp = adjustedTimestamp;

        // console.log('时间戳:', now, '调整后:', adjustedTimestamp, '拆分:', { t0, t1 });

        // 9. 使用hashids算法加密
        const alphabet = "GxJRk37QAe51FCsPW92uEOyq4Bg6Sp8YzVTmnU0liwDdHXLajZrfNhobIcMvKt";
        const hashids = new Hashids(salt, 0, alphabet);

        // 将ints数组编码为字符串
        const sign = hashids.encode(ints);

        // 打印调试信息
        // console.log('=== Sign生成调试信息 ===');
        // console.log('udid:', udid);
        // console.log('signSalt:', signSalt);
        // console.log('生成的salt:', salt);
        // console.log('参数映射:', Object.fromEntries(reqMap));
        // console.log('排序后的keys:', sortedKeys);
        // console.log('拼接字符串:', contentStr);
        // console.log('HMAC-MD5哈希:', hashStr);
        // console.log('分割后的ints:', ints);
        // console.log('原始时间戳:', now, '调整后时间戳:', adjustedTimestamp, '拆分:', { t0, t1 });
        // console.log('最终sign:', sign);
        // console.log('========================');

        return sign;

    } catch (error) {
        console.error('生成sign失败:', error);
        return '';
    }
}

/**
 * 重置去重状态（可选，用于测试或特殊场景）
 */
export function resetSignDeduplication() {
    lastRequestKey = '';
    lastTimestamp = 0;
    lastUsedTimestamp = 0;
    collisionCount = 0;
    console.log('Sign去重状态已重置');
}


/**
 * 生成salt - 匹配后端的genSalt函数
 * 后端逻辑: GenTokenSalt(uid, udid + signSalt)
 * 
 * @param {string} shortUdid - 22位短udid
 * @param {string} signSalt - 固定的signSalt值 'xspotter-s3'
 * @returns {Promise<string>} salt值
 */
async function generateSalt(shortUdid, signSalt) {
    try {
        // 1. 将22位短udid解压为完整UUID
        const fullUdid = await unpackUUID(shortUdid);

        // 2. 解析UUID并提取每个部分的第一个字符
        // UUID格式: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
        const uuidParts = fullUdid.split('-');
        if (uuidParts.length !== 5) {
            throw new Error('Invalid UUID format');
        }

        // 提取每个部分的第一个字符组成hexStr
        // 后端: for i, item := range strings.Split(udidStr, "-") { hexStr += string(item[i]) }
        let hexStr = '';
        for (let i = 0; i < uuidParts.length; i++) {
            if (uuidParts[i].length > i) {
                hexStr += uuidParts[i][i];  // 注意：取第i个字符，不是第一个
            } else {
                hexStr += uuidParts[i][0];  // 如果长度不够，取第一个
            }
        }

        // 3. 将hexStr解析为整数（16进制）
        const baseInt = parseInt(hexStr, 16);

        // 4. 转换为8进制字符串
        const base8Str = baseInt.toString(8);

        // 5. secret = udid + signSalt
        const secret = shortUdid + signSalt;

        // 6. 返回 base8 + secret
        const result = base8Str + secret;

        return result;

    } catch (error) {
        console.error('生成salt失败:', error);
        throw error;
    }
}


/**
 * 将22位短udid解压为完整UUID
 * 匹配后端的unpackUUID函数
 */
async function unpackUUID(shortUdid) {
    try {
        // 后端逻辑: shortUdid + "==" 然后base64解码
        const base64Str = shortUdid + '==';

        // 将url安全的base64转换为标准base64
        const standardBase64 = base64Str.replace(/-/g, '+').replace(/_/g, '/');

        // base64解码
        const binaryStr = atob(standardBase64);

        // 转换为16进制格式
        const hex = [];
        for (let i = 0; i < binaryStr.length; i++) {
            const hexByte = binaryStr.charCodeAt(i).toString(16).padStart(2, '0');
            hex.push(hexByte);
        }

        // 格式化为UUID: xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
        const hexStr = hex.join('');
        const uuid = `${hexStr.substring(0, 8)}-${hexStr.substring(8, 12)}-${hexStr.substring(12, 16)}-${hexStr.substring(16, 20)}-${hexStr.substring(20, 32)}`;

        return uuid;

    } catch (error) {
        console.error('解压UUID失败:', error);
        // 如果已经是完整UUID格式，直接返回
        if (shortUdid.includes('-') && shortUdid.length === 36) {
            return shortUdid;
        }
        throw error;
    }
}

/**
 * 将完整UUID压缩为22位短udid
 * 匹配后端的packUDID函数
 */
export function packUUID(fullUuid) {
    try {
        // 移除UUID中的连字符
        const hexStr = fullUuid.replace(/-/g, '');

        // 将16进制字符串转换为字节数组
        const bytes = [];
        for (let i = 0; i < hexStr.length; i += 2) {
            bytes.push(parseInt(hexStr.substring(i, i + 2), 16));
        }

        // 转换为二进制字符串
        let binary = '';
        for (let i = 0; i < bytes.length; i++) {
            binary += String.fromCharCode(bytes[i]);
        }

        // base64编码并转换为url安全格式
        const base64 = btoa(binary);
        const base64url = base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '');

        return base64url;

    } catch (error) {
        console.error('压缩UUID失败:', error);
        return fullUuid;
    }
}