// 引用  https://web.tianyunperfect.cn/simple/js/util.js
function lt(obj) {
    console.table(JSON.parse(JSON.stringify(obj)));
}

/**
 * 深度克隆
 * @param obj
 * @returns {any}
 */
function deepClone(obj) {
    return JSON.parse(JSON.stringify(obj))
}

function debounce(fn, wait = 50) {
    // 通过闭包缓存一个定时器 id
    let timer = null
    // 将 debounce 处理结果当作函数返回
    // 触发事件回调时执行这个返回函数
    return function (...args) {
        // 如果已经设定过定时器就清空上一次的定时器
        if (timer) clearTimeout(timer)

        // 开始设定一个新的定时器，定时器结束后执行传入的函数 fn
        timer = setTimeout(() => {
            fn.apply(this, args)
        }, wait)
    }
}

// 日期工具类
const dataUtil = {
    // 日期 -> 字符串：formatDate(date, 'yyyy-MM-dd hh:mm:ss');
    formatDate: function (date, format) {
        const pad = (n) => (n < 10 ? '0' + n : n);
        const replacements = {
            'yyyy': date.getFullYear(),
            'MM': pad(date.getMonth() + 1),
            'dd': pad(date.getDate()),
            'hh': pad(date.getHours()),
            'mm': pad(date.getMinutes()),
            'ss': pad(date.getSeconds()),
            'qq': Math.floor((date.getMonth() + 3) / 3), //季度
            'SSS': pad(date.getMilliseconds(), 3)    //毫秒
        };
        let result = format;
        for (const key in replacements) {
            result = result.replace(key, replacements[key]);
        }
        return result;
    },
    // 日期字符串 -> 日期:  parseDate("2022-10-30 16:13:49")
    parseDate: function (dateString) {
        const date = new Date(Date.parse(dateString));
        return date;
    },
    // 当前日期
    getNowStr: function () {
        return this.formatDate(new Date(), 'yyyy-MM-dd hh:mm:ss');
    },

}

/**
 * 远程请求
 * @type {{async: (function(*, *, *, *): Promise<unknown>), xhr_send: request.xhr_send, sync: (function(*, *, *, *): any)}}
 */
const requestUtil = {
    xhr_send(xhr, method, headers, data) {
        if (headers) {
            for (const key in headers) {
                xhr.setRequestHeader(key, headers[key]);
            }
        }

        if (method.match(/^(POST|PUT|DELETE)$/i)) {
            if (!headers || !headers.hasOwnProperty("Content-Type")) {
                xhr.setRequestHeader("Content-Type", "application/json");
            }
            xhr.send(JSON.stringify(data));
        } else {
            xhr.send();
        }
    },

    /**
     * 异步请求：requestUtil.async('https://jsonplaceholder.typicode.com/posts/1', 'GET', null, null)
     *     .then(data => console.log(data))
     *     .catch(error => console.error(error));
     */
    async(url, method, data = {}, headers = {}) {
        return new Promise((resolve, reject) => {
            const xhr = new XMLHttpRequest();
            xhr.open(method, url, true);
            this.xhr_send(xhr, method, headers, data);
            xhr.onload = () => {
                resolve(JSON.parse(xhr.responseText));
            };
            xhr.onerror = () => reject(xhr.statusText);
        });
    },
    /**
     * 拼接 url
     */
    buildUrl(url, params) {
        const urlObj = new URL(url);
        // @ts-ignore
        for (const key in params) {
            urlObj.searchParams.set(key, params[key]);
        }
        return urlObj.toString();
    },

    /**
     * 同步请求 let a = request.sync("https://httpbin.tianyunperfect.cn/ip","GET",null,null)
     */
    sync(url, method, data = {}, headers = {}) {
        const xhr = new XMLHttpRequest();
        xhr.open(method, url, false);
        this.xhr_send(xhr, method, headers, data);
        return JSON.parse(xhr.responseText);
    },
};


/**
 * 休眠一段时间： await sleep(2000)
 * @param time
 * @returns {Promise<unknown>}
 */
function sleep(time) {
    return new Promise((resolve) => setTimeout(resolve, time));
}

/**
 * 根据选择器 选择某一个dom，10秒钟内
 * @param sel
 * @returns {Promise<*>}
 */
async function getDom(sel) {
    for (let i = 0; i < 100; i++) {
        let dom = document.querySelector(sel);
        if (dom) {
            return dom;
        } else {
            await sleep(100);
        }
    }
}

/**
 * 根据选择器 选择所有dom，10秒钟内
 * @param sel
 * @returns {Promise<*>}
 */
async function getDomAll(sel) {
    for (let i = 0; i < 100; i++) {
        let dom = document.querySelectorAll(sel);
        if (dom.length > 0) {
            return dom;
        } else {
            await sleep(100);
        }
    }
}

/**
 * 添加全局样式： addGlobalStyle('.box {height: 100px !important;}');
 */
function addGlobalStyle(newStyle) {
    let styleElement = document.getElementById('styles_js');
    if (!styleElement) {
        styleElement = document.createElement('style');
        styleElement.type = 'text/css';
        styleElement.id = 'styles_js';
        document.getElementsByTagName('head')[0].appendChild(styleElement);
    }
    styleElement.appendChild(document.createTextNode(newStyle));
}

/**
 * 获取 指定 name 的 url 参数
 * @param url
 * @param name
 * @returns {string|string}
 */
function getQueryStringByUrl(url, name) {
    let reg = new RegExp("(^|&)" + name + "=([^&]*)(&|$)", "i");
    let r = url.substring(url.indexOf('?') + 1).match(reg); //获取url中"?"符后的字符串并正则匹配
    let context = "";
    if (r != null)
        context = r[2];
    reg = null;
    r = null;
    return context == null || context === "" || context === "undefined" ? "" : decodeURI(context);
}


/**
 * 获取 指定 name 的 url 参数
 * @param name
 * @returns {string}
 */
function getQueryString(name) {
    return getQueryStringByUrl(location.href, name);
}

// 随机数
const randomUtil = {
    /**
     * 获取随机数
     * @param min
     * @param max
     * @returns {number}
     */
    getInt: function (min, max) {
        min = Math.ceil(min);
        max = Math.floor(max);
        return Math.floor(Math.random() * (max - min) + min); //The maximum is exclusive and the minimum is inclusive
    },
    /**
     * 获取随机的一个值
     * @param arr
     * @returns {*}
     */
    getOneFromArray: function (arr) {
        return arr[this.getInt(0, arr.length)];
    }
}


/**
 * 创建一个<eleName k="attrs[k]">text</eleName>样式的页面元素
 * @param eleName
 * @param text
 * @param attrs
 * @returns {*}
 */
function createEle(eleName, text, attrs) {
    let ele = document.createElement(eleName);
    // innerText 也就是 <p>text会被添加到这里</p>
    ele.innerText = text;
    // attrs 的类型是一个 map
    for (let k in attrs) {
        // 遍历 attrs, 给节点 ele 添加我们想要的属性
        ele.setAttribute(k, attrs[k]);
    }
    // 返回节点
    return ele;
}


/**
 * 自动关闭提示框
 * @param str 提示文本
 * @param sec 时间（秒）
 */
function showMsg(str, sec) {
    const borderColor = "#336699"; //提示窗口的边框颜色
    const sWidth = document.body.offsetWidth;
    const sHeight = document.body.offsetHeight;
    //背景div
    const bgObj = document.createElement("div");
    let alertBgDiv = 'alertBgDiv';
    bgObj.setAttribute('id', alertBgDiv);
    bgObj.style.cssText = `position: fixed; top: 0; background: #E8E8E8; filter: progid:DXImageTransform.Microsoft.Alpha(style=3,opacity=25,finishOpacity=75; opacity: 0.6; left: 0; width: ${sWidth}px; height: ${sHeight}px; z-index: 10000`;
    document.body.appendChild(bgObj);
    //创建提示窗口的div
    const msgObj = document.createElement("div");
    let alertMsgDiv = "alertMsgDiv";
    msgObj.setAttribute("id", alertMsgDiv);
    msgObj.setAttribute("align", "center");
    msgObj.style.cssText = `background: white; border: 1px solid ${borderColor}; position: fixed; left: 50%; font: 15px/1.6em Verdana, Geneva, Arial, Helvetica, sans-serif; margin-left: -225px; top: ${document.body.scrollTop + (window.screen.availHeight / 2) - 150}px; text-align: center; line-height: 25px; z-index: 10001; min-width: 300px`;
    document.body.appendChild(msgObj);
    //提示信息标题
    const title = document.createElement("h4");
    let alertMsgTitle = "alertMsgTitle";
    title.setAttribute("id", alertMsgTitle);
    title.setAttribute("align", "left");
    title.style.cssText = `margin:0; padding:3px; background:${borderColor}; filter:progid:DXImageTransform.Microsoft.Alpha(startX=20, startY=20, finishX=100, finishY=100,style=1,opacity=75,finishOpacity=100); opacity:0.75; border:1px solid ${borderColor}; font:12px Verdana, Geneva, Arial, Helvetica, sans-serif; color:white`;
    title.innerHTML = "提示信息";
    document.getElementById(alertMsgDiv).appendChild(title);
    //提示信息
    const txt = document.createElement("p");
    txt.setAttribute("id", "msgTxt");
    txt.style.margin = "16px 0";
    txt.innerHTML = str;
    document.getElementById(alertMsgDiv).appendChild(txt);

    //设置关闭时间
    window.setTimeout(() => {
        document.body.removeChild(document.getElementById(alertBgDiv));
        document.getElementById(alertMsgDiv).removeChild(document.getElementById(alertMsgTitle));
        document.body.removeChild(document.getElementById(alertMsgDiv));
    }, sec * 1000);
}


/**
 * 打印信息
 * @param obj
 */
function log(obj) {
    console.table(JSON.parse(JSON.stringify(obj)));
}

// 添加静态资源
const staticLoader = {
    /**
     * 添加js引用 ： addRemoteJs("https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js");
     */
    addRemoteJs: function (jsUrl) {
        if (document.querySelector(`script[src="${jsUrl}"]`)) {
            return;
        }

        const script = document.createElement('script');
        script.src = jsUrl;
        script.type = 'text/javascript';
        document.head.appendChild(script);
    },

    addRemoteCss: function (cssUrl) {
        if (document.querySelector(`link[href="${cssUrl}"]`)) {
            return;
        }

        const link = document.createElement('link');
        link.href = cssUrl;
        link.rel = 'stylesheet';
        link.type = 'text/css';
        document.head.appendChild(link);
    },
    /**
     * 添加 Jq
     */
    addJq: function () {
        const jqUrl = 'https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js';
        this.addRemoteJs(jqUrl);
    }

}

// 复制文本工具类
const copyUtil = {
    /**
     * 复制，支持复制html： copyHtml('#wish_search_list .wish_s_item');
     * @param css_selector
     * @returns {Promise<void>}
     */
    copyFromSelector: async function (css_selector) {
        const el = document.querySelector(css_selector);
        const html = el.outerHTML;
        await this.copyFromHtml(html);
    },
    copyFromHtml: async function (html) {
        try {
            await navigator.clipboard.write([
                new ClipboardItem({
                    'text/html': new Blob([html], {type: 'text/html'})
                })
            ]);
            console.log('复制成功');
        } catch (err) {
            console.error('复制失败', err);
        }
    },
    copyText: async function (text) {
        if (navigator.clipboard) {
            // clipboard api 复制
            await navigator.clipboard.writeText(text);
        } else {
            const textarea = document.createElement('textarea');
            document.body.appendChild(textarea);
            // 隐藏此输入框
            textarea.style.position = 'fixed';
            textarea.style.clip = 'rect(0 0 0 0)';
            textarea.style.top = '10px';
            // 赋值
            textarea.value = text;
            // 选中
            textarea.select();
            // 复制
            document.execCommand('copy', true);
            // 移除输入框
            document.body.removeChild(textarea);
        }
    },
    getClipboardText: async function () {
        try {
            const text = await navigator.clipboard.readText();
            console.log("剪贴板内容:", text);
            return text;
        } catch (err) {
            console.error("无法读取剪贴板内容:", err);
        }
    }
}

/**
 * base64图片转为webp格式
 * @param base64Image
 * @returns {Promise<string|*>}
 */
async function convertToWebPAsync(base64Image) {
    // 检查图像格式是否为WebP
    if (base64Image.startsWith('data:image/webp')) {
        // 如果是WebP格式，直接返回原始的base64字符串
        return base64Image;
    } else {
        // 将图像转换为WebP格式
        const image = new Image();
        image.src = base64Image;

        await new Promise((resolve, reject) => {
            image.onload = resolve;
            image.onerror = reject;
        });

        const canvas = document.createElement('canvas');
        canvas.width = image.width;
        canvas.height = image.height;

        const ctx = canvas.getContext('2d');
        ctx.drawImage(image, 0, 0);

        return canvas.toDataURL('image/webp');
    }
}


function closeWindow() {
    const userAgent = navigator.userAgent;
    if (userAgent.indexOf("Firefox") !== -1 || userAgent.indexOf("Chrome") !== -1) {
        window.location.href = "about:blank";
        window.close();
    } else {
        window.opener = null;
        window.open("", "_self");
        window.close();
    }
}


function getTimeStamp() {
    const currentTimeStamp = Date.now();
    const targetTimeStamp = new Date('2024-01-01').getTime();
    return currentTimeStamp - targetTimeStamp;
}


/** 用于监控值的变化并执行相应的操作
 * monitorAndAct(generateRandomValue, logValueChange);
 *
 * @param func1 入参：无 出参：值 generateRandomValue()
 * @param func2 入参：旧值、新值 出参：无  logValueChange(oldValue, newValue)
 */
function monitorAndAct(func1, func2) {
    let oldValue = func1();

    function checkValue() {
        const newValue = func1();
        if (newValue !== oldValue) {
            // 值已改变，执行func2并传入旧值和新值
            func2(oldValue, newValue);
            // 更新旧值为新值
            oldValue = newValue;
        }
        // 持续检查，这里假设每隔1秒检查一次
        setTimeout(checkValue, 1000);
    }

    // 启动检查过程
    checkValue();
}

function showTextOnTopRight(text) {
    let id1 = "showTextOnTopRight";
    // 删除之前的
    let oldDiv = document.getElementById(id1);
    if (oldDiv) {
        document.body.removeChild(oldDiv);
    }
    // 在屏幕右上角显示一个文本
    let div = document.createElement("div");
    // 设置id
    div.id = id1;
    div.style.position = "fixed";
    div.style.top = "8px";
    div.style.right = "8px";
    div.style.padding = "5px";
    div.style.backgroundColor = "#fff";
    div.style.border = "1px solid #ccc";
    div.style.borderRadius = "5px";
    div.style.zIndex = "9999";
    // 字体大小
    div.style.fontSize = "12px";
    div.innerHTML = text;
    document.body.appendChild(div);
}

function showBusyText() {
    showTextOnTopRight("同步中...");
}

function showDoneText() {
    showTextOnTopRight("✅");
}


// dispatchEventClick('.btn') 触发点击事件
function dispatchEventClick(cssSelector) {
    let dom = document.querySelector(cssSelector);
    if (!dom) {
        console.log('dom is null');
        return;
    }
    const event = new Event('click');
    dom.dispatchEvent(event);
}