import * as elIcons from '@element-plus/icons-vue'
import { useTitle } from '@vueuse/core'
import type { FormInstance } from 'element-plus'
import { isArray, isNull, trim, trimStart } from 'lodash-es'
import type { App } from 'vue'
import { nextTick } from 'vue'
import type { TranslateOptions } from 'vue-i18n'
import { i18n } from '../lang'
import { useSiteConfig } from '../stores/siteConfig'
import { getUrl } from './axios'
import Icon from '/@/components/icon/index.vue'
import router from '/@/router/index'
import { adminBaseRoutePath } from '/@/router/static/adminBase'
import { useMemberCenter } from '/@/stores/memberCenter'
import { useNavTabs } from '/@/stores/navTabs'

export function registerIcons(app: App) {
    /*
     * 全局注册 Icon
     * 使用方式: <Icon name="name" size="size" color="color" />
     * 详见<待完善>
     */
    app.component('Icon', Icon)

    /*
     * 全局注册element Plus的icon
     */
    const icons = elIcons as any
    for (const i in icons) {
        app.component(`el-icon-${icons[i].name}`, icons[i])
    }
}

/**
 * 加载网络css文件
 * @param url css资源url
 */
export function loadCss(url: string): void {
    const link = document.createElement('link')
    link.rel = 'stylesheet'
    link.href = url
    link.crossOrigin = 'anonymous'
    document.getElementsByTagName('head')[0].appendChild(link)
}

/**
 * 加载网络js文件
 * @param url js资源url
 */
export function loadJs(url: string): void {
    const link = document.createElement('script')
    link.src = url
    document.body.appendChild(link)
}

/**
 * 根据路由 meta.title 设置浏览器标题
 */
export function setTitleFromRoute() {
    nextTick(() => {
        if (typeof router.currentRoute.value.meta.title != 'string') {
            return
        }
        const webTitle = i18n.global.te(router.currentRoute.value.meta.title)
            ? i18n.global.t(router.currentRoute.value.meta.title)
            : router.currentRoute.value.meta.title
        const title = useTitle()
        const siteConfig = useSiteConfig()
        title.value = `${webTitle}${siteConfig.siteName ? ' - ' + siteConfig.siteName : ''}`
    })
}

/**
 * 设置浏览器标题
 * @param webTitle 新的标题
 */
export function setTitle(webTitle: string) {
    if (router.currentRoute.value) {
        router.currentRoute.value.meta.title = webTitle
    }
    nextTick(() => {
        const title = useTitle()
        const siteConfig = useSiteConfig()
        title.value = `${webTitle}${siteConfig.siteName ? ' - ' + siteConfig.siteName : ''}`
    })
}

/**
 * 是否是外部链接
 * @param path
 */
export function isExternal(path: string): boolean {
    return /^(https?|ftp|mailto|tel):/.test(path)
}

/**
 * 全局防抖
 * 与 _.debounce 不同的是，间隔期间如果再次传递不同的函数，两个函数也只会执行一次
 * @param fn 执行函数
 * @param ms 间隔毫秒数
 */
export const debounce = (fn: Function, ms: number) => {
    return (...args: any[]) => {
        if (window.lazy) {
            clearTimeout(window.lazy)
        }
        window.lazy = window.setTimeout(() => {
            fn(...args)
        }, ms)
    }
}

/**
 * 根据pk字段的值从数组中获取key
 * @param arr
 * @param pk
 * @param value
 */
export const getArrayKey = (arr: any, pk: string, value: any): any => {
    for (const key in arr) {
        if (arr[key][pk] == value) {
            return key
        }
    }
    return false
}

/**
 * 表单重置
 * @param formEl
 */
export const onResetForm = (formEl?: FormInstance | null) => {
    typeof formEl?.resetFields == 'function' && formEl.resetFields()
}

/**
 * 将数据构建为ElTree的data {label:'', children: []}
 * @param data
 */
export const buildJsonToElTreeData = (data: any): ElTreeData[] => {
    if (typeof data == 'object') {
        const childrens = []
        for (const key in data) {
            childrens.push({
                label: key + ': ' + data[key],
                children: buildJsonToElTreeData(data[key]),
            })
        }
        return childrens
    } else {
        return []
    }
}

/**
 * 是否在后台应用内
 * @param path 不传递则通过当前路由 path 检查
 */
export const isAdminApp = (path = '') => {
    const regex = new RegExp(`^${adminBaseRoutePath}`)
    if (path) {
        return regex.test(path)
    }
    if (regex.test(getCurrentRoutePath())) {
        return true
    }
    return false
}

/**
 * 是否为手机设备
 */
export const isMobile = () => {
    return !!navigator.userAgent.match(
        /android|webos|ip(hone|ad|od)|opera (mini|mobi|tablet)|iemobile|windows.+(phone|touch)|mobile|fennec|kindle (Fire)|Silk|maemo|blackberry|playbook|bb10\; (touch|kbd)|Symbian(OS)|Ubuntu Touch/i
    )
}

/**
 * 从一个文件路径中获取文件名
 * @param path 文件路径
 */
export const getFileNameFromPath = (path: string) => {
    const paths = path.split('/')
    return paths[paths.length - 1]
}

export function auth(node: string): boolean
export function auth(node: { name: string; subNodeName?: string }): boolean

/**
 * 鉴权
 * 提供 string 将根据当前路由 path 自动拼接和鉴权，还可以提供路由的 name 对象进行鉴权
 * @param node
 */
export function auth(node: string | { name: string; subNodeName?: string }) {
    const store = isAdminApp() ? useNavTabs() : useMemberCenter()
    if (typeof node === 'string') {
        const path = getCurrentRoutePath()
        if (store.state.authNode.has(path)) {
            const subNodeName = path + (path == '/' ? '' : '/') + node
            if (store.state.authNode.get(path)!.some((v: string) => v == subNodeName)) {
                return true
            }
        }
    } else {
        // 节点列表中没有找到 name
        if (!node.name || !store.state.authNode.has(node.name)) return false

        // 无需继续检查子节点或未找到子节点
        if (!node.subNodeName || store.state.authNode.get(node.name)?.includes(node.subNodeName)) return true
    }
    return false
}

/**
 * 获取资源完整地址
 * @param relativeUrl 资源相对地址
 * @param domain 指定域名
 */
export const fullUrl = (relativeUrl: string, domain = '') => {
    const siteConfig = useSiteConfig()
    if (!domain) {
        domain = siteConfig.cdnUrl ? siteConfig.cdnUrl : getUrl()
    }
    if (!relativeUrl) return domain

    const regUrl = new RegExp(/^http(s)?:\/\//)
    const regexImg = new RegExp(/^((?:[a-z]+:)?\/\/|data:image\/)(.*)/i)
    if (!domain || regUrl.test(relativeUrl) || regexImg.test(relativeUrl)) {
        return relativeUrl
    }

    let url = domain + relativeUrl
    if (domain === siteConfig.cdnUrl && siteConfig.cdnUrlParams) {
        const separator = url.includes('?') ? '&' : '?'
        url += separator + siteConfig.cdnUrlParams
    }
    return url
}

/**
 * 获取路由 path
 */
export const getCurrentRoutePath = () => {
    let path = router.currentRoute.value.path
    if (path == '/') path = trimStart(window.location.hash, '#')
    if (path.indexOf('?') !== -1) path = path.replace(/\?.*/, '')
    return path
}

/**
 * 获取根据当前路由路径动态加载的语言翻译
 * @param key 无需语言路径的翻译key，亦可使用完整路径
 * @param named — 命名插值的值
 * @param options — 其他翻译选项
 * @returns — Translated message
 */
export const __ = (key: string, named?: Record<string, unknown>, options?: TranslateOptions<string>) => {
    let langPath = ''
    const path = getCurrentRoutePath()
    if (isAdminApp()) {
        langPath = path.slice(path.indexOf(adminBaseRoutePath) + adminBaseRoutePath.length)
        langPath = trim(langPath, '/').replaceAll('/', '.')
    } else {
        langPath = trim(path, '/').replaceAll('/', '.')
    }
    langPath = langPath ? langPath + '.' + key : key
    return i18n.global.te(langPath)
        ? i18n.global.t(langPath, named ?? {}, options ? options : {})
        : i18n.global.t(key, named ?? {}, options ? options : {})
}

/**
 * 文件类型效验，前端根据服务端配置进行初步检查
 * @param fileName 文件名
 * @param fileType 文件 mimeType，不一定存在
 */
export const checkFileMimetype = (fileName: string, fileType: string) => {
    if (!fileName) return false
    const siteConfig = useSiteConfig()
    const allowedSuffixes = isArray(siteConfig.upload.allowedSuffixes)
        ? siteConfig.upload.allowedSuffixes
        : siteConfig.upload.allowedSuffixes.toLowerCase().split(',')

    const allowedMimeTypes = isArray(siteConfig.upload.allowedMimeTypes)
        ? siteConfig.upload.allowedMimeTypes
        : siteConfig.upload.allowedMimeTypes.toLowerCase().split(',')

    const forbiddenSuffixes = siteConfig.upload.forbiddenSuffixes
        ? isArray(siteConfig.upload.forbiddenSuffixes)
            ? siteConfig.upload.forbiddenSuffixes
            : siteConfig.upload.forbiddenSuffixes.toLowerCase().split(',')
        : []

    const nameParts = fileName.toLowerCase().split('.')
    if (nameParts.length > 1) {
        for (let i = 0; i < nameParts.length - 1; i++) {
            if (forbiddenSuffixes.includes(nameParts[i])) {
                return false
            }
        }
    }

    const fileSuffix = fileName.substring(fileName.lastIndexOf('.') + 1).toLowerCase()
    if (forbiddenSuffixes.includes(fileSuffix)) {
        return false
    }
    if (allowedSuffixes.includes(fileSuffix) || allowedSuffixes.includes('.' + fileSuffix)) {
        return true
    }
    if (fileType && allowedMimeTypes.includes(fileType)) {
        return true
    }
    return false
}

/**
 * 获取一组资源的完整地址
 * @param relativeUrls 资源相对地址
 * @param domain 指定域名
 */
export const arrayFullUrl = (relativeUrls: string | string[], domain = '') => {
    if (typeof relativeUrls === 'string') {
        relativeUrls = relativeUrls == '' ? [] : relativeUrls.split(',')
    }
    for (const key in relativeUrls) {
        relativeUrls[key] = fullUrl(relativeUrls[key], domain)
    }
    return relativeUrls
}

/**
 * 格式化时间戳
 * @param dateTime 时间戳，默认使用当前时间戳
 * @param fmt 格式化方式，默认：yyyy-mm-dd hh:MM:ss
 */
export const timeFormat = (dateTime: string | number | null = null, fmt = 'yyyy-mm-dd hh:MM:ss') => {
    if (dateTime == 'none') {
        return i18n.global.t('None')
    }

    if (isNull(dateTime)) {
        dateTime = Number(new Date())
    }

    /**
     * 1. 秒级时间戳（10位）需要转换为毫秒级，才能供 Date 对象直接使用
     * 2. yyyy-mm-dd 也是10位，使用 isFinite 进行排除
     */
    if (String(dateTime).length === 10 && isFinite(Number(dateTime))) {
        dateTime = +dateTime * 1000
    }

    let date = new Date(dateTime)
    if (isNaN(date.getTime())) {
        date = new Date(Number(dateTime))
        if (isNaN(date.getTime())) {
            return 'Invalid Date'
        }
    }

    let ret
    const opt: anyObj = {
        'y+': date.getFullYear().toString(), // 年
        'm+': (date.getMonth() + 1).toString(), // 月
        'd+': date.getDate().toString(), // 日
        'h+': date.getHours().toString(), // 时
        'M+': date.getMinutes().toString(), // 分
        's+': date.getSeconds().toString(), // 秒
    }
    for (const k in opt) {
        ret = new RegExp('(' + k + ')').exec(fmt)
        if (ret) {
            fmt = fmt.replace(ret[1], ret[1].length == 1 ? opt[k] : padStart(opt[k], ret[1].length, '0'))
        }
    }
    return fmt
}

/**
 * 字符串补位
 */
const padStart = (str: string, maxLength: number, fillString = ' ') => {
    if (str.length >= maxLength) return str

    const fillLength = maxLength - str.length
    let times = Math.ceil(fillLength / fillString.length)
    while ((times >>= 1)) {
        fillString += fillString
        if (times === 1) {
            fillString += fillString
        }
    }
    return fillString.slice(0, fillLength) + str
}

/**
 * 根据当前时间生成问候语
 */
export const getGreet = () => {
    const now = new Date()
    const hour = now.getHours()
    let greet = ''

    if (hour < 5) {
        greet = i18n.global.t('utils.Late at night, pay attention to your body!')
    } else if (hour < 9) {
        greet = i18n.global.t('utils.good morning!') + i18n.global.t('utils.welcome back')
    } else if (hour < 12) {
        greet = i18n.global.t('utils.Good morning!') + i18n.global.t('utils.welcome back')
    } else if (hour < 14) {
        greet = i18n.global.t('utils.Good noon!') + i18n.global.t('utils.welcome back')
    } else if (hour < 18) {
        greet = i18n.global.t('utils.good afternoon') + i18n.global.t('utils.welcome back')
    } else if (hour < 24) {
        greet = i18n.global.t('utils.Good evening') + i18n.global.t('utils.welcome back')
    } else {
        greet = i18n.global.t('utils.Hello!') + i18n.global.t('utils.welcome back')
    }
    return greet
}
