import compose from 'ramda/es/compose';
import filter from 'ramda/es/filter';
import isNil from 'ramda/es/isNil';
import map from 'ramda/es/map';
import split from 'ramda/es/split';
import trim from 'ramda/es/trim';

/**
 * 移除字符串首尾的指定字符（仅各移除一个，若首/尾等于 char）。
 * 支持柯里化：仅传 char 时返回 (str) => string。
 * @param char 要移除的单个字符
 * @param str 原字符串；省略时返回柯里化函数
 * @returns 处理后的字符串，或柯里化后的函数
 */
function trimChar(char: string, str: string): string;
function trimChar(char: string): (str: string) => string;
function trimChar(char: string, str?: string) {
    if (isNil(str)) {
        return (str1: string) => {
            let result = str1;
            if (result.charAt(0) === char) {
                result = result.slice(1);
            }
            if (result.charAt(result.length - 1) === char) {
                result = result.slice(0, -1);
            }
            return result;
        };
    } else {
        let result = str;
        if (result.charAt(0) === char) {
            result = result.slice(1);
        }
        if (result.charAt(result.length - 1) === char) {
            result = result.slice(0, -1);
        }
        return result;
    }
}

export { trimChar };

/**
 * 将字符串的首字母转为小写，其余不变；空字符串原样返回。
 * @param str 原字符串
 * @returns 首字母小写后的字符串
 */
export function toLowerCaseFirstLetter(str: string) {
    if (!str) return str;
    return str.charAt(0).toLowerCase() + str.slice(1);
}

/**
 * 将字符串的首字母转为大写，其余不变；空字符串原样返回。
 * @param str 原字符串
 * @returns 首字母大写后的字符串
 */
export function toUpperCaseFirstLetter(str: string) {
    if (!str) return str;
    return str.charAt(0).toUpperCase() + str.slice(1);
}

/**
 * 账号脱敏：前二后四保留，中间用 '**' 代替；长度 ≤8 时仅保留后四位并前缀 '**'。空串返回 ''。
 * @param str 原始账号字符串
 * @returns 脱敏后的字符串，如 'us**com.'、'**5678'
 */
export function maskAccount(str: string): string {
    if (!str) return '';
    const len = str.length;
    if (len <= 8) return '**' + str.slice(-4);
    return str.slice(0, 2) + '**' + str.slice(-4);
}

/**
 * 判断字符串是否为常见邮箱格式（本地部分 + @ + 域名 + 至少两位 TLD）。
 * @param str 待检测字符串
 * @returns 符合邮箱格式返回 true，否则 false
 */
export function isEmail(str: string): boolean {
    const regex = /^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$/;
    return regex.test(str);
}

/**
 * 判断字符串是否为 Gmail 邮箱（xxx@gmail.com）。
 * @param str 待检测字符串
 * @returns 为 Gmail 格式返回 true，否则 false
 */
export function isGmail(str: string): boolean {
    const regex = /^[a-zA-Z0-9._%+-]+@gmail([.])com$/;
    return regex.test(str);
}

/**
 * 判断字符串是否为合法 IPv4 地址（四段、每段 0–255）。
 * @param str 待检测字符串
 * @returns 合法 IPv4 返回 true，否则 false
 */
export function isIp(str: string): boolean {
    const octet = '(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9]?[0-9])';
    const regex = new RegExp(`^${octet}\\.${octet}\\.${octet}\\.${octet}$`);
    return regex.test(str);
}

/**
 * 生成具备加密强度的唯一标识（基于 crypto.getRandomValues + 时间戳，base36）。
 * 同一时刻多次调用不重复、不可预测，但较慢（依赖底层加密 API）。
 * @returns 唯一字符串，适合用作防篡改或安全场景的 sign
 */
export function getCryptoUniqueSign() {
    const v = Array.from(crypto.getRandomValues(new Uint32Array(3)))
        .map((num) => num.toString().padStart(10, '0'))
        .join('');
    return BigInt(`${Date.now()}${v}`).toString(36);
}

/**
 * 生成唯一标识（基于 Math.random + 时间戳，base36）。同一时刻多次调用不重复，可预测、效率较高，不适用于安全场景。
 * @returns 唯一字符串，适合用作非安全场景的 ID 或 sign
 */
export function getUniqueSign() {
    const v = [Math.floor(Math.random() * 2 ** 32), Math.floor(Math.random() * 2 ** 32)]
        .map((num) => num.toString().padStart(10, '0'))
        .join('');
    return BigInt(`${Date.now()}${v}`).toString(36);
}

/**
 * 将 IPv4 字符串转为 32 位无符号整数，语义与 MySQL INET_ATON 一致。非法或空串返回 0。
 * @param ip 点分十进制 IPv4 字符串
 * @returns 无符号整数，或 0（格式错误/空）
 */
export function ipToNum(ip: string): number {
    if (!ip) return 0;
    const parts = ip.split('.');
    if (parts.length !== 4) return 0;

    const num0 = parseInt(parts[0], 10);
    const num1 = parseInt(parts[1], 10);
    const num2 = parseInt(parts[2], 10);
    const num3 = parseInt(parts[3], 10);

    if (
        isNaN(num0) ||
        num0 < 0 ||
        num0 > 255 ||
        isNaN(num1) ||
        num1 < 0 ||
        num1 > 255 ||
        isNaN(num2) ||
        num2 < 0 ||
        num2 > 255 ||
        isNaN(num3) ||
        num3 < 0 ||
        num3 > 255
    ) {
        return 0;
    }

    return ((num0 << 24) | (num1 << 16) | (num2 << 8) | num3) >>> 0;
}

/**
 * 将 32 位无符号整数转为 IPv4 点分十进制字符串，语义与 MySQL INET_NTOA 一致。超出 [0, 0xffffffff] 返回 ''。
 * @param num 无符号整数
 * @returns 点分十进制 IP 字符串，或 ''（越界）
 */
export function numToIp(num: number): string {
    if (num < 0 || num > 0xffffffff) {
        return '';
    }

    const byte3 = (num >>> 24) & 0xff;
    const byte2 = (num >>> 16) & 0xff;
    const byte1 = (num >>> 8) & 0xff;
    const byte0 = num & 0xff;

    return `${byte3}.${byte2}.${byte1}.${byte0}`;
}

/**
 * 将字符串按逗号、空白（含换行）拆分为非空字符串数组；先 trim 再按 /[,\s]+/ 拆分，并对每项 trim、过滤空串。
 * @param str 原字符串（由 compose 管道传入，调用方式 toStrArray(str)）
 * @returns 非空字符串数组
 */
export const toStrArray = compose(
    filter((v) => v !== ''),
    map((v) => trim(v)),
    split(/[,\s]+/),
    trimChar(','),
    trim
);

/**
 * 根据字符组成检测文本主要语言：中文（含 CJK 等）与英文（拉丁字母）数量比较，空或仅空白返回 'unknown'。
 * @param text 待检测文本
 * @returns 'zh-CN' | 'en' | 'unknown'
 */
export function detectLanguage(text: string): 'zh-CN' | 'en' | 'unknown' {
    if (!text || !text.trim()) {
        return 'unknown';
    }

    const trimmedText = text.trim();

    const chineseRegex =
        /[\u4e00-\u9fff\u3400-\u4dbf\uf900-\ufaff\u3040-\u309f\u30a0-\u30ff\uff00-\uffef]/;
    const englishRegex = /[a-zA-Z]/;

    const hasChineseChars = chineseRegex.test(trimmedText);
    const hasEnglishChars = englishRegex.test(trimmedText);

    const chineseMatches = trimmedText.match(/[\u4e00-\u9fff]/g) || [];
    const englishMatches = trimmedText.match(/[a-zA-Z]/g) || [];

    const chineseCount = chineseMatches.length;
    const englishCount = englishMatches.length;

    if (hasChineseChars && (!hasEnglishChars || chineseCount > englishCount)) {
        return 'zh-CN';
    }

    if (hasEnglishChars && (!hasChineseChars || englishCount > chineseCount)) {
        return 'en';
    }

    return 'unknown';
}

/**
 * 根据标题文本检测语言并返回源语言代码；与 TranslateSourceLang 的 value 对应，未知时默认 'zh-CN'。
 * @param title 标题文本
 * @returns 'zh-CN' | 'en'（检测为英文时返回 'en'，其余返回 'zh-CN'）
 */
export function getSourceLanguageFromTitle(title: string): string {
    const detectedLang = detectLanguage(title);

    switch (detectedLang) {
        case 'zh-CN':
            return 'zh-CN';
        case 'en':
            return 'en';
        default:
            return 'zh-CN';
    }
}
