import { createRoot } from 'react-dom/client'
import { editor, languages, Position } from 'monaco-editor'
import { Monaco } from '@monaco-editor/react'
import { MessageInstance } from 'antd/es/message/interface'
import { HookAPI } from 'antd/es/modal/useModal'
import { NotificationInstance } from 'antd/es/notification/interface'
import { Theme, css } from 'antd-style'
import { floor } from 'lodash'
import {
    STORAGE_COLLAPSE_SIDEBAR_KEY,
    STORAGE_THEME_MODE_KEY,
    STORAGE_TOOL_MENU_ITEM_KEY,
    THEME_DARK,
    THEME_FOLLOW_SYSTEM,
    THEME_LIGHT
} from '@/constants/common.constants'
import { getLocalStorage, setLocalStorage } from '@/util/browser'
import FullscreenLoadingMask from '@/components/common/FullscreenLoadingMask'

export type ThemeMode = typeof THEME_FOLLOW_SYSTEM | typeof THEME_LIGHT | typeof THEME_DARK

let message: MessageInstance
let notification: NotificationInstance
let modal: HookAPI

export const init = (
    messageInstance: MessageInstance,
    notificationInstance: NotificationInstance,
    modalInstance: HookAPI
) => {
    message = messageInstance
    notification = notificationInstance
    modal = modalInstance
}

export { message, notification, modal }

export const randomInt = (start: number, end: number) => {
    if (start > end) {
        const t = start
        start = end
        end = t
    }
    start = Math.ceil(start)
    end = Math.floor(end)
    return start + Math.floor(Math.random() * (end - start))
}

export const randomFloat = (start: number, end: number) => {
    return start + Math.random() * (end - start)
}

export const randomColor = (start: number, end: number) => {
    return `rgb(${randomInt(start, end)},${randomInt(start, end)},${randomInt(start, end)})`
}

export const floorNumber = (num: number, digits: number) => {
    if (digits > 0) {
        return Math.floor(num / Math.pow(10, digits - 1)) * Math.pow(10, digits - 1)
    } else {
        const regExpMatchArray = num.toString().match(new RegExp('^\\d\\.\\d{' + -digits + '}'))
        if (regExpMatchArray !== null) {
            return parseFloat(regExpMatchArray[0]).toFixed(-digits)
        } else {
            return num
        }
    }
}

export const showLoadingMask = (id: string) => {
    if (document.querySelector(`#${id}`)) {
        return
    }

    const container = document.createElement('div')
    document.body.appendChild(container)
    container.id = id
    container.setAttribute(
        'style',
        'position: fixed; width: 100vw; height: 100vh; z-index: 10000; left: 0; top: 0;'
    )

    return createRoot(container).render(<FullscreenLoadingMask />)
}

export const removeLoadingMask = (id: string) => {
    document.querySelectorAll(`#${id}`).forEach((value) => {
        value.parentNode?.removeChild(value)
    })
}

export enum ByteUnit {
    B = 'B',
    KiB = 'KiB',
    Mib = 'Mib',
    GiB = 'GiB',
    TiB = 'TiB',
    PiB = 'PiB',
    EiB = 'EiB',
    ZiB = 'ZiB',
    YiB = 'YiB'
}

export const formatByteSize = (byteSize: number): string => {
    const BASE = 1024
    if (byteSize <= -1) {
        return byteSize.toString()
    }

    if (floor(byteSize / BASE) <= 0) {
        return formatByte(byteSize, ByteUnit.B)
    }

    byteSize /= BASE
    if (floor(byteSize / BASE) <= 0) {
        return formatByte(byteSize, ByteUnit.KiB)
    }

    byteSize /= BASE
    if (floor(byteSize / BASE) <= 0) {
        return formatByte(byteSize, ByteUnit.Mib)
    }

    byteSize /= BASE
    if (floor(byteSize / BASE) <= 0) {
        return formatByte(byteSize, ByteUnit.GiB)
    }

    byteSize /= BASE
    if (floor(byteSize / BASE) <= 0) {
        return formatByte(byteSize, ByteUnit.TiB)
    }

    byteSize /= BASE
    if (floor(byteSize / BASE) <= 0) {
        return formatByte(byteSize, ByteUnit.PiB)
    }

    byteSize /= BASE
    if (floor(byteSize / BASE) <= 0) {
        return formatByte(byteSize, ByteUnit.EiB)
    }

    byteSize /= BASE
    if (floor(byteSize / BASE) <= 0) {
        return formatByte(byteSize, ByteUnit.ZiB)
    }

    byteSize /= BASE
    return formatByte(byteSize, ByteUnit.YiB)
}

const formatByte = (size: number, unit: ByteUnit): string => {
    let precision: number
    if ((size * 1000) % 10 > 0) {
        precision = 3
    } else if ((size * 100) % 10 > 0) {
        precision = 2
    } else if ((size * 10) % 10 > 0) {
        precision = 1
    } else {
        precision = 0
    }

    return `${size.toFixed(precision)}${unit}`
}

export const checkDesktop = () => import.meta.env.VITE_PLATFORM === 'DESKTOP'

export const saveToolMenuItem = (toolMenuItem: ToolMenuItem[]) => {
    setLocalStorage(STORAGE_TOOL_MENU_ITEM_KEY, JSON.stringify(toolMenuItem))
}

export const getToolMenuItem = (): ToolMenuItem[] => {
    const s = getLocalStorage(STORAGE_TOOL_MENU_ITEM_KEY)
    if (!s) {
        return []
    }
    return JSON.parse(s) as ToolMenuItem[]
}

export const omitText = (text: string, length: number) => {
    if (text.length <= length) {
        return text
    }
    return `${text.substring(0, length)}...`
}

const getByteLength = (str: string) => {
    let length = 0
    for (let i = 0; i < str.length; i++) {
        if (str.charCodeAt(i) > 255) {
            length += 2
        } else {
            length++
        }
    }

    return length
}

const substringByByte = (str: string, start: number, length: number) => {
    let byteLength = 0
    let result = ''

    for (let i = 0; i < str.length; i++) {
        const charCode = str.charCodeAt(i)
        byteLength += charCode > 255 ? 2 : 1

        if (byteLength > start + length) {
            break
        } else if (byteLength > start) {
            result += str[i]
        }
    }

    return result
}

export const omitTextByByte = (text: string, length: number) => {
    if (getByteLength(text) <= length) {
        return text
    }
    return `${substringByByte(text, 0, length)}...`
}

export const getSidebarCollapse = () => getLocalStorage(STORAGE_COLLAPSE_SIDEBAR_KEY) === 'true'

export const setSidebarCollapse = (isCollapse: boolean) => {
    setLocalStorage(STORAGE_COLLAPSE_SIDEBAR_KEY, isCollapse ? 'true' : 'false')
}

export const getThemeMode = (): ThemeMode => {
    switch (getLocalStorage(STORAGE_THEME_MODE_KEY)) {
        case THEME_FOLLOW_SYSTEM:
        case THEME_LIGHT:
        case THEME_DARK:
            return getLocalStorage(STORAGE_THEME_MODE_KEY) as ThemeMode
        default:
            return THEME_FOLLOW_SYSTEM
    }
}

export const setThemeMode = (themeMode: ThemeMode) => {
    setLocalStorage(STORAGE_THEME_MODE_KEY, themeMode)
}

const cssColors = [
    'blue',
    'purple',
    'cyan',
    'green',
    'magenta',
    'pink',
    'red',
    'orange',
    'yellow',
    'volcano',
    'geekblue',
    'gold',
    'lime'
].reduce((prev: string[], current) => {
    let temp: string[] = []
    for (let i = 1; i <= 10; i++) {
        temp = [...temp, `${current}${i}`]
    }
    return [...prev, current, ...temp]
}, [])

const cssVariables: string[] = [
    ...cssColors,
    'colorPrimary',
    'colorSuccess',
    'colorWarning',
    'colorError',
    'colorInfo',
    'colorLink',
    'colorTextBase',
    'colorBgBase',
    'fontFamily',
    'fontFamilyCode',
    'fontSize',
    'lineWidth',
    'lineType',
    'motionUnit',
    'motionBase',
    'motionEaseOutCirc',
    'motionEaseInOutCirc',
    'motionEaseOut',
    'motionEaseInOut',
    'motionEaseOutBack',
    'motionEaseInBack',
    'motionEaseInQuint',
    'motionEaseOutQuint',
    'borderRadius',
    'sizeUnit',
    'sizeStep',
    'sizePopupArrow',
    'controlHeight',
    'zIndexBase',
    'zIndexPopupBase',
    'opacityImage',
    'wireframe',
    'motion',
    'colorLinkHover',
    'colorText',
    'colorTextSecondary',
    'colorTextTertiary',
    'colorTextQuaternary',
    'colorFill',
    'colorFillSecondary',
    'colorFillTertiary',
    'colorFillQuaternary',
    'colorBgSolid',
    'colorBgSolidHover',
    'colorBgSolidActive',
    'colorBgLayout',
    'colorBgContainer',
    'colorBgElevated',
    'colorBgSpotlight',
    'colorBgBlur',
    'colorBorder',
    'colorBorderSecondary',
    'colorPrimaryBg',
    'colorPrimaryBgHover',
    'colorPrimaryBorder',
    'colorPrimaryBorderHover',
    'colorPrimaryHover',
    'colorPrimaryActive',
    'colorPrimaryTextHover',
    'colorPrimaryText',
    'colorPrimaryTextActive',
    'colorSuccessBg',
    'colorSuccessBgHover',
    'colorSuccessBorder',
    'colorSuccessBorderHover',
    'colorSuccessHover',
    'colorSuccessActive',
    'colorSuccessTextHover',
    'colorSuccessText',
    'colorSuccessTextActive',
    'colorErrorBg',
    'colorErrorBgHover',
    'colorErrorBgFilledHover',
    'colorErrorBgActive',
    'colorErrorBorder',
    'colorErrorBorderHover',
    'colorErrorHover',
    'colorErrorActive',
    'colorErrorTextHover',
    'colorErrorText',
    'colorErrorTextActive',
    'colorWarningBg',
    'colorWarningBgHover',
    'colorWarningBorder',
    'colorWarningBorderHover',
    'colorWarningHover',
    'colorWarningActive',
    'colorWarningTextHover',
    'colorWarningText',
    'colorWarningTextActive',
    'colorInfoBg',
    'colorInfoBgHover',
    'colorInfoBorder',
    'colorInfoBorderHover',
    'colorInfoHover',
    'colorInfoActive',
    'colorInfoTextHover',
    'colorInfoText',
    'colorInfoTextActive',
    'colorLinkActive',
    'colorBgMask',
    'colorWhite',
    'fontSizeSM',
    'fontSizeLG',
    'fontSizeXL',
    'fontSizeHeading1',
    'fontSizeHeading2',
    'fontSizeHeading3',
    'fontSizeHeading4',
    'fontSizeHeading5',
    'lineHeight',
    'lineHeightLG',
    'lineHeightSM',
    'lineHeightHeading1',
    'lineHeightHeading2',
    'lineHeightHeading3',
    'lineHeightHeading4',
    'lineHeightHeading5',
    'sizeXXL',
    'sizeXL',
    'sizeLG',
    'sizeMD',
    'sizeMS',
    'size',
    'sizeSM',
    'sizeXS',
    'sizeXXS',
    'controlHeightSM',
    'controlHeightXS',
    'controlHeightLG',
    'motionDurationFast',
    'motionDurationMid',
    'motionDurationSlow',
    'lineWidthBold',
    'borderRadiusXS',
    'borderRadiusSM',
    'borderRadiusLG',
    'borderRadiusOuter',
    'colorFillContent',
    'colorFillContentHover',
    'colorFillAlter',
    'colorBgContainerDisabled',
    'colorBorderBg',
    'colorSplit',
    'colorTextPlaceholder',
    'colorTextDisabled',
    'colorTextHeading',
    'colorTextLabel',
    'colorTextDescription',
    'colorTextLightSolid',
    'colorHighlight',
    'colorBgTextHover',
    'colorBgTextActive',
    'colorIcon',
    'colorIconHover',
    'colorErrorOutline',
    'colorWarningOutline',
    'fontSizeIcon',
    'lineWidthFocus',
    'controlOutlineWidth',
    'controlInteractiveSize',
    'controlItemBgHover',
    'controlItemBgActive',
    'controlItemBgActiveHover',
    'controlItemBgActiveDisabled',
    'controlOutline',
    'fontWeightStrong',
    'opacityLoading',
    'linkDecoration',
    'linkHoverDecoration',
    'linkFocusDecoration',
    'controlPaddingHorizontal',
    'controlPaddingHorizontalSM',
    'paddingXXS',
    'paddingXS',
    'paddingSM',
    'padding',
    'paddingMD',
    'paddingLG',
    'paddingXL',
    'paddingContentHorizontalLG',
    'paddingContentVerticalLG',
    'paddingContentHorizontal',
    'paddingContentVertical',
    'paddingContentHorizontalSM',
    'paddingContentVerticalSM',
    'marginXXS',
    'marginXS',
    'marginSM',
    'margin',
    'marginMD',
    'marginLG',
    'marginXL',
    'marginXXL',
    'boxShadow',
    'boxShadowSecondary',
    'boxShadowTertiary',
    'screenXS',
    'screenXSMin',
    'screenXSMax',
    'screenSM',
    'screenSMMin',
    'screenSMMax',
    'screenMD',
    'screenMDMin',
    'screenMDMax',
    'screenLG',
    'screenLGMin',
    'screenLGMax',
    'screenXL',
    'screenXLMin',
    'screenXLMax',
    'screenXXL',
    'screenXXLMin'
]

export const generateThemeCssVariable = (theme: Omit<Theme, 'prefixCls'>) => {
    const cssContent = cssVariables
        // eslint-disable-next-line @typescript-eslint/ban-ts-comment
        // @ts-expect-error
        .map((variable) => `--${variable}: ${theme[variable]};`)
        .join('\n')

    return css`
        :root {
            ${cssContent}
        }
    `
}

export const addExtraCssVariable = (monaco: Monaco) => {
    monaco.languages.registerCompletionItemProvider('css', {
        provideCompletionItems: (
            model: editor.ITextModel,
            position: Position
        ): languages.ProviderResult<languages.CompletionList> => {
            const textUntilPosition = model.getValueInRange({
                startLineNumber: 1,
                startColumn: 1,
                endLineNumber: position.lineNumber,
                endColumn: position.column
            })
            if (!textUntilPosition.match(/var\(([^)]*)$/)) {
                return { suggestions: [] }
            }

            const word = model.getWordUntilPosition(position)
            const range = new monaco.Range(
                position.lineNumber,
                word.startColumn,
                position.lineNumber,
                word.endColumn
            )

            return {
                suggestions: cssVariables.map(
                    (variable): languages.CompletionItem => ({
                        label: `--${variable}`,
                        kind: monaco.languages.CompletionItemKind.Variable,
                        insertText: `--${variable}`,
                        range,
                        detail: 'Oxygen Theme Variable'
                    })
                )
            }
        }
    })
}
