export const kebabCase = (key: string) => {
    const result: string = key.replace(/([A-Z])/g, ' $1').trim()
    return result.split(' ').join('-').toLowerCase()
}

export type Awaitable<T> = T | PromiseLike<T>

export interface ImportInfo {
    as?: string
    name?: string
    from: string
}

export type SideEffectsInfo = (ImportInfo | string)[] | ImportInfo | string | undefined

export interface ComponentInfo extends ImportInfo {
    sideEffects?: SideEffectsInfo
}

export type ComponentResolveResult = Awaitable<string | ComponentInfo | null | undefined | void>

export type ComponentResolverFunction = (name: string) => ComponentResolveResult

export interface ComponentResolverObject {
    type: 'component' | 'directive'
    resolve: ComponentResolverFunction
}

export type ComponentResolver = ComponentResolverFunction | ComponentResolverObject

interface IMatcher {
    pattern: RegExp
    styleDir: string
}

const matchComponents: IMatcher[] = [
    {
        pattern: /^Avatar/,
        styleDir: 'avatar'
    },
    {
        pattern: /^AutoComplete/,
        styleDir: 'auto-complete'
    },
    {
        pattern: /^Anchor/,
        styleDir: 'anchor'
    },

    {
        pattern: /^Badge/,
        styleDir: 'badge'
    },
    {
        pattern: /^Breadcrumb/,
        styleDir: 'breadcrumb'
    },
    {
        pattern: /^Button/,
        styleDir: 'button'
    },
    {
        pattern: /^Checkbox/,
        styleDir: 'checkbox'
    },
    {
        pattern: /^Card/,
        styleDir: 'card'
    },
    {
        pattern: /^Collapse/,
        styleDir: 'collapse'
    },
    {
        pattern: /^Descriptions/,
        styleDir: 'descriptions'
    },
    {
        pattern: /^RangePicker|^WeekPicker|^MonthPicker/,
        styleDir: 'date-picker'
    },
    {
        pattern: /^Dropdown/,
        styleDir: 'dropdown'
    },

    {
        pattern: /^Form/,
        styleDir: 'form'
    },
    {
        pattern: /^InputNumber/,
        styleDir: 'input-number'
    },

    {
        pattern: /^Input|^Textarea/,
        styleDir: 'input'
    },
    {
        pattern: /^Statistic/,
        styleDir: 'statistic'
    },
    {
        pattern: /^CheckableTag/,
        styleDir: 'tag'
    },
    {
        pattern: /^TimeRangePicker/,
        styleDir: 'time-picker'
    },
    {
        pattern: /^Layout/,
        styleDir: 'layout'
    },
    {
        pattern: /^Menu|^SubMenu/,
        styleDir: 'menu'
    },

    {
        pattern: /^Table/,
        styleDir: 'table'
    },
    {
        pattern: /^TimePicker|^TimeRangePicker/,
        styleDir: 'time-picker'
    },
    {
        pattern: /^Radio/,
        styleDir: 'radio'
    },

    {
        pattern: /^Image/,
        styleDir: 'image'
    },

    {
        pattern: /^List/,
        styleDir: 'list'
    },

    {
        pattern: /^Tab/,
        styleDir: 'tabs'
    },
    {
        pattern: /^Mentions/,
        styleDir: 'mentions'
    },
    {
        pattern: /^QRCode/,
        styleDir: 'qr-code'
    },
    {
        pattern: /^Step/,
        styleDir: 'steps'
    },
    {
        pattern: /^Skeleton/,
        styleDir: 'skeleton'
    },

    {
        pattern: /^Select/,
        styleDir: 'select'
    },
    {
        pattern: /^TreeSelect/,
        styleDir: 'tree-select'
    },
    {
        pattern: /^Tree|^DirectoryTree/,
        styleDir: 'tree'
    },
    {
        pattern: /^Typography/,
        styleDir: 'typography'
    },
    {
        pattern: /^Timeline/,
        styleDir: 'timeline'
    },
    {
        pattern: /^Upload/,
        styleDir: 'upload'
    }
]

export interface AntDesignResolverOptions {
    /**
     * exclude components that do not require automatic import
     *
     * @default []
     */
    exclude?: string[]
    /**
     * import style along with components
     *
     * @default 'css'
     */
    importStyle?: boolean | 'css' | 'less'
    /**
     * resolve `antd' icons
     *
     * requires package `@ant-design/icons-vue`
     *
     * @default false
     */
    resolveIcons?: boolean

    /**
     * @deprecated use `importStyle: 'css'` instead
     */
    importCss?: boolean
    /**
     * @deprecated use `importStyle: 'less'` instead
     */
    importLess?: boolean

    /**
     * use commonjs build default false
     */
    cjs?: boolean

    /**
     * rename package
     *
     * @default 'antd'
     */
    packageName?: string
}

const getStyleDir = (compName: string): string => {
    for (const matchComponent of matchComponents) {
        if (compName.match(matchComponent.pattern)) {
            return matchComponent.styleDir
        }
    }
    return kebabCase(compName)
}

const getSideEffects = (compName: string, options: AntDesignResolverOptions): SideEffectsInfo => {
    const { importStyle = true } = options

    if (!importStyle) {
        return
    }

    const lib = options.cjs ? 'lib' : 'es'
    const packageName = options?.packageName || 'antd'

    const styleDir = getStyleDir(compName)
    return `${packageName}/${lib}/${styleDir}/style`
}

const primitiveNames = [
    'Affix',
    'Anchor',
    'AnchorLink',
    'AutoComplete',
    'AutoCompleteOptGroup',
    'AutoCompleteOption',
    'Alert',
    'Avatar',
    'AvatarGroup',
    'BackTop',
    'Badge',
    'BadgeRibbon',
    'Breadcrumb',
    'BreadcrumbItem',
    'BreadcrumbSeparator',
    'Button',
    'ButtonGroup',
    'Calendar',
    'Card',
    'CardGrid',
    'CardMeta',
    'Collapse',
    'CollapsePanel',
    'Carousel',
    'Cascader',
    'Checkbox',
    'CheckboxGroup',
    'Col',
    'Comment',
    'ConfigProvider',
    'DatePicker',
    'MonthPicker',
    'WeekPicker',
    'RangePicker',
    'QuarterPicker',
    'Descriptions',
    'DescriptionsItem',
    'Divider',
    'Dropdown',
    'DropdownButton',
    'Drawer',
    'Empty',
    'FloatButton',
    'Form',
    'FormItem',
    'FormItemRest',
    'Grid',
    'Input',
    'InputGroup',
    'InputPassword',
    'InputSearch',
    'Textarea',
    'Image',
    'ImagePreviewGroup',
    'InputNumber',
    'Layout',
    'LayoutHeader',
    'LayoutSider',
    'LayoutFooter',
    'LayoutContent',
    'List',
    'ListItem',
    'ListItemMeta',
    'Menu',
    'MenuDivider',
    'MenuItem',
    'MenuItemGroup',
    'SubMenu',
    'Mentions',
    'MentionsOption',
    'Modal',
    'Statistic',
    'StatisticCountdown',
    'PageHeader',
    'Pagination',
    'Popconfirm',
    'Popover',
    'Progress',
    'Radio',
    'RadioButton',
    'RadioGroup',
    'Rate',
    'Result',
    'Row',
    'QRCode',
    'Select',
    'SelectOptGroup',
    'SelectOption',
    'Skeleton',
    'SkeletonButton',
    'SkeletonAvatar',
    'SkeletonInput',
    'SkeletonImage',
    'Slider',
    'Space',
    'Spin',
    'Steps',
    'Step',
    'Switch',
    'Table',
    'TableColumn',
    'TableColumnGroup',
    'TableSummary',
    'TableSummaryRow',
    'TableSummaryCell',
    'Transfer',
    'Tree',
    'TreeNode',
    'DirectoryTree',
    'TreeSelect',
    'TreeSelectNode',
    'Tabs',
    'TabPane',
    'Tag',
    'CheckableTag',
    'TimePicker',
    'TimeRangePicker',
    'Timeline',
    'TimelineItem',
    'Tooltip',
    'Typography',
    'TypographyLink',
    'TypographyParagraph',
    'TypographyText',
    'TypographyTitle',
    'Upload',
    'UploadDragger',
    'LocaleProvider'
]

const prefix = 'Antd'

let antdNames: Set<string>

const genAntdNames = (primitiveNames: string[]): void => {
    antdNames = new Set(primitiveNames.map((name) => `${prefix}${name}`))
}

genAntdNames(primitiveNames)

const isAntd = (compName: string): boolean => {
    return antdNames.has(compName)
}

export const AntDesignResolver = (options: AntDesignResolverOptions = {}): ComponentResolver => {
    return {
        type: 'component',
        resolve: (name: string) => {
            if (options.resolveIcons && name.match(/(Outlined|Filled|TwoTone)$/)) {
                return {
                    name,
                    from: '@ant-design/icons'
                }
            }

            if (isAntd(name) && !options?.exclude?.includes(name)) {
                const importName = name.slice(prefix.length)
                const { cjs = false, packageName = 'antd' } = options
                const path = `${packageName}/${cjs ? 'lib' : 'es'}`
                return {
                    name: importName,
                    from: path,
                    sideEffects: getSideEffects(importName, options)
                }
            }
            return undefined
        }
    }
}
