/**
 * 详细使用配置请访问文档地址：https://jianfv.top/ChatAreaDoc/
 * */
export interface DeviceInfo {
    isTablet: boolean;
    isPhone: boolean;
    isAndroid: boolean;
    isIOS: boolean;
    isPc: boolean;
    isIPhone: boolean;
    isIPad: boolean;
    isWindows: boolean;
    isMacOS: boolean;
    isLinux: boolean;
    isWechat: boolean;
    isMobile: boolean;
}

export interface UserInfo {
    id: string;
    name: string;
    avatar?: string | URL;
    pinyin?: string;
}

export interface UserProps {
    id?: string;
    name?: string;
    avatar?: string;
    pinyin?: string;
}

export interface TagInfo {
    id: string;
    name: string;
    pinyin?: string;
}

export interface SelectItem {
    id: string;
    name: string;
    preview?: string | URL;
}

export interface UploadImage {
    (file: File): Promise<string | URL>
}

export interface CustomTag {
    dialogTitle: string;
    prefix: string;
    keyMap?: string[];
    tagList: TagInfo[];
}

export interface SelectTag {
    dialogTitle: string;
    key: string;
    options: SelectItem[];
}

export interface UpdateOption<T> {
    userList?: T[];
    placeholder?: string;
    customTrigger?: CustomTag[];
    selectList?: SelectTag[];
    maxLength?: number;
    copyType?: CopyType[];
    uploadImage?: UploadImage;
    needCallEvery?: boolean;
    needCallSpace?: boolean;
    userProps?: UserProps;
    wrapKeyFun?: { (event: KeyboardEvent): boolean };
    sendKeyFun?: { (event: KeyboardEvent): boolean };
}

export interface Options<T> extends UpdateOption<T> {
    elm: HTMLElement;
    device?: Device; // default auto
    autoFocus?: boolean; // default true
    needDialog?: boolean;  // default true
    needDebounce?: boolean; // default true
    asyncMatch?: boolean; // default false
    dialogLabels?: {
        pcPointDialog?: PCPointDialogLabel;
        pcPCheckDialog?: PCPCheckDialogLabel;
        h5Dialog?: H5DialogLabel;
    }
}

export interface PCPointDialogLabel {
    title?: string;
    callEveryLabel?: string;
    checkLabel?: string;
    emptyLabel?: string;
}

export interface PCPCheckDialogLabel {
    title?: string;
    searchPlaceholder?: string;
    searchEmptyLabel?: string;
    userTagTitle?: string;
    checkAllLabel?: string;
    checkEmptyLabel?: string;
    confirmLabel?: string;
    cancelLabel?: string;
}

export interface H5DialogLabel {
    title?: string;
    callEveryLabel?: string;
    searchPlaceholder?: string;
    searchEmptyLabel?: string;
    confirmLabel?: string;
    cancelLabel?: string;
}

export interface ReduceNodeConfig {
    saveTagData?: boolean;
    needUserId?: boolean;
    needTagId?: boolean;
    needSelectId?: boolean;
    needTipKey?: boolean;
    wrapClassName?: string;
    rowClassName?: string;
    imgToText?: boolean;
    identifyLink?: boolean;
}

export interface TipOptions {
    tagLabel?: string;
    popoverLabel?: string;
    codeLabel?: string;
}

export type ChatEventName =
    'enterSend'
    | 'operate'
    | 'defaultAction'
    | 'atMatch'
    | 'atCheck'
    | 'tagCheck'
    | 'selectCheck'
    | 'afterAtCheck'
    | 'afterTagCheck'
    | 'afterSelectCheck'
    | 'showAtDialog'
    | 'showTagDialog'
    | 'showSelectDialog'
    | 'elementClicked'
    | 'tipTagState'
export type CopyType = 'text' | 'image'
export type Device = 'pc' | 'h5' | 'auto'

declare class ChatArea<T = UserInfo> {
    static version: string; // 版本信息
    readonly richText: HTMLElement; // 文本输入元素
    readonly textLength: number; // 当设置配置项maxLength读取到的文本框字符长度
    readonly deviceInfo: Readonly<DeviceInfo>; // 当前设备类型信息
    readonly options: Readonly<Options<T>>; // 实例配置项
    chatElement: Record<string, any>; // 私有实体类，内部方法不推荐直接使用故ts类型暂未提供定义
    chatEvent: Record<string, any>; //  私有实体类，内部方法不推荐直接使用故tts类型暂未提供定义
    chatInput: Record<string, any>; //  私有实体类，内部方法不推荐直接使用故tts类型暂未提供定义

    constructor(options: Options<T>)

    // 修改多个配置项
    updateConfig(updateOption: UpdateOption<T>): void

    // 修改人员列表
    updateUserList(userList: T[]): void

    // 搜索人员列表
    searchUserList(value: string): T[]

    // 获取精简后的文本框dom
    getReduceNode(config?: ReduceNodeConfig): HTMLElement

    // 获取发送文本
    getText(config?: ReduceNodeConfig): string

    // 获取发送的html内容
    getHtml(config?: ReduceNodeConfig): string

    // 逆向解析
    reverseAnalysis(html: string, needConnect?: boolean): Promise<void>

    // 光标处插入html代码
    insertHtml(html: string): Promise<HTMLElement>

    // 插入自定义文本
    insertText(text: string): Promise<void>

    // 获取@人员列表
    getCallUserList(): T[]

    // 获取@人员标签列表
    getCallUserTagList(): T[]

    // 获取自定义触发符元素已选择的数据列表
    getCustomTagList(): Record<string, TagInfo[]>

    // 获取标签选择元素已选择的数据列表
    getSelectTagList(): Record<string, SelectTag[]>

    // 获取输入元素的输入内容
    getInputTagList(): Record<string, (string | null)[]>

    // 置空输入框
    clear(defaultText?: string): Promise<void>

    // 在当前光标处进行换行操作
    lineBreak(): void

    // 文本框是否为空
    isEmpty(unSpace: boolean): boolean

    // 卸载元素，并释放实例
    dispose(): void

    // 在光标处插入传入的@人员
    setUserTag(user: T): Promise<void>

    // 在光标处插入自定义标签元素
    setCustomTag(tag: TagInfo, prefix: string): Promise<void>

    // 在光标出插入选择标签元素
    setSelectTag(tag: SelectItem, key: string): Promise<void>

    // 在光标处插入输入标签元素
    setInputTag(key: string, placeholder: string, value?: string): Promise<void>

    // 唤起PC人员光标选择弹窗
    showPCPointDialog(): void

    // 唤起PC人员多选弹窗
    showPCCheckDialog(): void

    // 唤起PC自定义触发符选择弹窗
    showPCCustomTagDialog(prefix: string): void

    // 唤起H5@人员弹窗
    showH5Dialog(): void

    // 唤起PC选择标签弹窗
    showPCSelectDialog(key: string, elm?: HTMLElement): void

    // 私有api-匹配光标选择弹窗内容插入@标签
    matchSetTag(user: T): Promise<void>

    // 私有api-单向插入
    onceSetTag(user: T): Promise<void>

    // 私有api-多项插入
    batchSetTag(users: T[]): Promise<void>

    // 私有api-单向插入自定义标签
    onceSetCustomTag(tag: TagInfo): Promise<void>

    // 私有api-匹配光标选择弹窗内容插入自定义标签
    matchSetCustomTag(tag: TagInfo): Promise<void>

    // 删除聊天框内的@人员标签
    delUserTags(ids?: string[] | number[]): Promise<void>

    // 删除聊天框内的自定义标签
    delCustomTags(prefix: string, ids?: string[] | number[]): Promise<void>

    // 删除聊天框内选择标签
    delSelectTags(key: string, ids?: string[] | number): Promise<void>

    // 删除聊天框内输入标签
    delInputTags(keys?: string[]): Promise<void>

    // 禁止聊天框编辑
    disabled(): void

    // 允许聊天框编辑
    enable(): void

    // 撤销
    undo(): Promise<void>

    // 恢复
    redo(): Promise<void>

    // 清空撤销重做栈
    clearHistory(): void

    // 光标移动
    cursorMove(count: number)

    // 光标删除
    cursorDel(count: number): Promise<void>

    // 将光标定位到最后一次记录的位置
    cursorToLastRecord()

    // 打开前置提示标签
    openTipTag(options: TipOptions)

    // 打开自定义前置标签元素
    openCustomTipTag(html: string): HTMLElement

    // 关闭前置提示标签
    closeTipTag(): Promise<void>

    // 修改PC光标选择弹窗默认文案
    revisePCPointDialogLabel(config: PCPointDialogLabel): void

    // 修改PC多选选择弹窗默认文案
    revisePCCheckDialogLabel(config: PCPCheckDialogLabel): void

    // 修改H5选择弹窗默认文案
    reviseH5DialogLabel(config: H5DialogLabel): void

    // 添加事件订阅
    addEventListener(eventName: ChatEventName, callBack: Function): void

    // 移除事件订阅
    removeEventListener(eventName: ChatEventName, callBack: Function): void

    // 创建OperateNode实例
    createOperateNode(): ChatOperateNode

    // 创建ChatComponent实例
    createChatComponent(): ChatComponent
}


export type ChatNodeType = 'gridBox' | 'gridInput' | 'userTag' | 'customTag' | 'selectTag' | 'inputTag' | 'htmlTag'
export type DatasetByType = {
    userTag: { id?: string, name?: string, [keyName: string]: string }; // 用户标签数据集
    customTag: { id: string, name: string, prefix: string }; // 自定义标签数据集
    selectTag: { id: string, name: string, key: string }; // 选择标签数据集
    inputTag: { key: string, placeholder: string, value?: string }; // 输入标签数据集
    gridBox: undefined; // 网格框数据集
    gridInput: undefined; // 网格输入框数据集
    htmlTag: undefined; // html标签数据集
}

export interface ChatNode<T extends ChatNodeType> {
    type: T; // 节点类型
    rank?: string; // rank码
    text?: string; // 文本内容
    html?: string; // html内容
    dataset?: DatasetByType[ChatNodeType[T]]; // 数据集
    children?: ChatNode<T>[]; // 子节点
}

export class ChatOperateNode {
    target: ChatArea; // 聊天框实例
    rankLen: number; // rank长度

    // 获取当前聊天框内容的Node结构集合
    getCurrentNodes(): ChatNode<ChatNodeType>[]

    // 获取rank码
    getRank(index: number): string

    // 根据rank获取对应的Node
    getNodeByRank(rank: string): ChatNode<ChatNodeType> | null

    // 根据rank获取对应的DOM元素
    getElmByRank(rank: string): HTMLElement | null

    // 根据聊天框元素获取对应的rank值
    getRankByElm(elm: HTMLElement): string

    // 修改单个Node数据
    updateNode(node: ChatNode<ChatNodeType>): Promise<void>

    // 插入单个Node数据
    insertNode(node: ChatNode<ChatNodeType>): Promise<void>

    // 根据rank移除对应的Node
    delNodeByRank(rank: string): Promise<void>

    // 覆盖聊天框内的Node结构
    coverNodes(nodes: ChatNode<ChatNodeType>[]): Promise<void>

    // 将光标移动到当前Node
    setCursorNode(node: ChatNode<ChatNodeType>, offset?: number)

    // 选中目标节点
    setSelectNodes(startNode: ChatNode<ChatNodeType>, endNode: ChatNode<ChatNodeType>, startOffset?: number, endOffset?: number)

    // 获取当前光标所处Node
    getCursorNode(): { node: ChatNode<ChatNodeType>, offset: number }

    // 渲染更新下次执行方法
    nextTick(callback: Function): Promise<void>

    // 获取光标对象方法
    getSelection(): Selection | null
}


export interface ChatComponentEvent {
    componentElm: HTMLElement; // 组件元素
    targetElm: HTMLElement; // 目标元素
    data: TagComponent['props']; // 组件数据
    eventType: string; // 事件类型
    eventName: string; // 事件名称
    stopPropagation: () => void; // 阻止事件冒泡
}

export interface TagComponent {
    name: string; // 组件名称
    props: Record<string, string>; // 组件属性
    methods: Record<string, (chatEvent: ChatComponentEvent, protoEvent: Event) => void>; // 组件方法
    template: string; // 组件模板
}

export class ChatComponent {
    target: ChatArea; // 聊天框实例
    components: Record<string, TagComponent> = {}; // 组件集合

    // 注册组件
    useComponent(component: TagComponent)

    // 在聊天框光标区域插入组件
    insertComponent(name: string, props: TagComponent['props']): Promise<HTMLElement | void>

    // 删除目标组件已渲染的元素
    removeComponentElm (htmlTag: HTMLElement): Promise<void>

    // 更新目标组件已渲染的props内容
    updateComponentElm (htmlTag: HTMLElement, props: TagComponent['props']): Promise<void>

    // 获取目标组件已经在聊天框渲染的元素集合
    getComponentElms (componentName: string): HTMLElement[]
}

export default ChatArea
