/* eslint-disable no-unused-vars */
/* eslint-disable no-use-before-define */
// For TS consumers who use Node and don't have dom in their tsconfig lib, import the necessary types here.
/// <reference lib="dom" />

/* Public API */

// eslint-disable-next-line
declare const hljs : HLJSApi;

type HLJSApi = PublicApi & ModesAPI

interface VuePlugin {
    install: (vue: any) => void
}

interface PublicApi {
    highlight: (codeOrlanguageName: string, optionsOrCode: string | HighlightOptions, ignoreIllegals?: boolean, continuation?: Mode) => HighlightResult
    highlightAuto: (code: string, languageSubset?: string[]) => AutoHighlightResult
    fixMarkup: (html: string) => string
    highlightBlock: (element: HTMLElement) => void
    highlightElement: (element: HTMLElement) => void
    configure: (options: Partial<HLJSOptions>) => void
    initHighlighting: () => void
    initHighlightingOnLoad: () => void
    highlightAll: () => void
    registerLanguage: (languageName: string, language: LanguageFn) => void
    unregisterLanguage: (languageName: string) => void
    listLanguages: () => string[]
    registerAliases: (aliasList: string | string[], { languageName } : {languageName: string}) => void
    getLanguage: (languageName: string) => Language | undefined
    requireLanguage: (languageName: string) => Language | never
    autoDetection: (languageName: string) => boolean
    inherit: <T>(original: T, ...args: Record<string, any>[]) => T
    addPlugin: (plugin: HLJSPlugin) => void
    debugMode: () => void
    safeMode: () => void
    versionString: string
    vuePlugin: () => VuePlugin
}

interface ModesAPI {
    SHEBANG: (mode?: Partial<Mode> & {binary?: string | RegExp}) => Mode
    BACKSLASH_ESCAPE: Mode
    QUOTE_STRING_MODE: Mode
    APOS_STRING_MODE: Mode
    PHRASAL_WORDS_MODE: Mode
    COMMENT: (begin: string | RegExp, end: string | RegExp, modeOpts?: Mode | {}) => Mode
    C_LINE_COMMENT_MODE: Mode
    C_BLOCK_COMMENT_MODE: Mode
    HASH_COMMENT_MODE: Mode
    NUMBER_MODE: Mode
    C_NUMBER_MODE: Mode
    BINARY_NUMBER_MODE: Mode
    CSS_NUMBER_MODE: Mode
    REGEXP_MODE: Mode
    TITLE_MODE: Mode
    UNDERSCORE_TITLE_MODE: Mode
    METHOD_GUARD: Mode
    END_SAME_AS_BEGIN: (mode: Mode) => Mode
    // built in regex
    IDENT_RE: string
    UNDERSCORE_IDENT_RE: string
    MATCH_NOTHING_RE: string
    NUMBER_RE: string
    C_NUMBER_RE: string
    BINARY_NUMBER_RE: string
    RE_STARTERS_RE: string
}

type LanguageFn = (hljs?: HLJSApi) => Language
type CompilerExt = (mode: Mode, parent: Mode | Language | null) => void

interface HighlightResult {
    relevance : number
    value : string
    language? : string
    emitter : Emitter
    illegal : boolean
    top? : Language | CompiledMode
    illegalBy? : illegalData
    sofar? : string
    errorRaised? : Error
    // * for auto-highlight
    second_best? : Omit<HighlightResult, 'second_best'>
    code?: string
}
interface AutoHighlightResult extends HighlightResult {}

interface illegalData {
    msg: string
    context: string
    mode: CompiledMode
}

type BeforeHighlightContext = {
  code: string,
  language: string,
  result?: HighlightResult
}
type PluginEvent = keyof HLJSPlugin;
type HLJSPlugin = {
    'after:highlight'?: (result: HighlightResult) => void,
    'before:highlight'?: (context: BeforeHighlightContext) => void,
    'after:highlightElement'?: (data: { el: Element, result: HighlightResult, text: string}) => void,
    'before:highlightElement'?: (data: { el: Element, language: string}) => void,
    // TODO: Old API, remove with v12
    'after:highlightBlock'?: (data: { block: Element, result: HighlightResult, text: string}) => void,
    'before:highlightBlock'?: (data: { block: Element, language: string}) => void,
}

interface EmitterConstructor {
    new (opts: any): Emitter
}

interface HighlightOptions {
    language: string
    ignoreIllegals?: boolean
}

interface HLJSOptions {
   noHighlightRe: RegExp
   languageDetectRe: RegExp
   classPrefix: string
   tabReplace?: string
   useBR: boolean
   languages?: string[]
   __emitter: EmitterConstructor
}

interface CallbackResponse {
    data: Record<string, any>
    ignoreMatch: () => void
    isMatchIgnored: boolean
}

/************
 PRIVATE API
 ************/

/* for jsdoc annotations in the JS source files */

type AnnotatedError = Error & {mode?: Mode | Language, languageName?: string, badRule?: Mode}

type ModeCallback = (match: RegExpMatchArray, response: CallbackResponse) => void
type HighlightedHTMLElement = HTMLElement & {result?: object, second_best?: object, parentNode: HTMLElement}
type EnhancedMatch = RegExpMatchArray & {rule: CompiledMode, type: MatchType}
type MatchType = "begin" | "end" | "illegal"

 interface Emitter {
    addKeyword(text: string, kind: string): void
    addText(text: string): void
    toHTML(): string
    finalize(): void
    closeAllNodes(): void
    openNode(kind: string): void
    closeNode(): void
    addSublanguage(emitter: Emitter, subLanguageName: string): void
 }

/* modes */

 interface ModeCallbacks {
     "on:end"?: Function,
     "on:begin"?: ModeCallback
 }

interface Mode extends ModeCallbacks, ModeDetails {

}

interface LanguageDetail {
    name?: string
    rawDefinition?: () => Language
    aliases?: string[]
    disableAutodetect?: boolean
    contains: (Mode)[]
    case_insensitive?: boolean
    keywords?: Record<string, any> | string
    isCompiled?: boolean,
    exports?: any,
    classNameAliases?: Record<string, string>
    compilerExtensions?: CompilerExt[]
    supersetOf?: string
}

type Language = LanguageDetail & Partial<Mode>

interface CompiledLanguage extends LanguageDetail, CompiledMode {
    isCompiled: true
    contains: CompiledMode[]
    keywords: Record<string, any>
}

type KeywordData = [string, number];
type KeywordDict = Record<string, KeywordData>

type CompiledMode = Omit<Mode, 'contains'> &
    {
        contains: CompiledMode[]
        keywords: KeywordDict
        data: Record<string, any>
        terminatorEnd: string
        keywordPatternRe: RegExp
        beginRe: RegExp
        endRe: RegExp
        illegalRe: RegExp
        matcher: any
        isCompiled: true
        starts?: CompiledMode
        parent?: CompiledMode
    }

interface ModeDetails {
    begin?: RegExp | string
    match?: RegExp | string
    end?: RegExp | string
    className?: string
    contains?: ("self" | Mode)[]
    endsParent?: boolean
    endsWithParent?: boolean
    endSameAsBegin?: boolean
    skip?: boolean
    excludeBegin?: boolean
    excludeEnd?: boolean
    returnBegin?: boolean
    returnEnd?: boolean
    __beforeBegin?: Function
    parent?: Mode
    starts?:Mode
    lexemes?: string | RegExp
    keywords?: Record<string, any> | string
    beginKeywords?: string
    relevance?: number
    illegal?: string | RegExp | Array<string | RegExp>
    variants?: Mode[]
    cachedVariants?: Mode[]
    // parsed
    subLanguage?: string | string[]
    isCompiled?: boolean
    label?: string
}

// deprecated API since v10
// declare module 'highlight.js/lib/highlight.js';

declare module 'highlight.js' {
    export = hljs;
}

declare module 'highlight.js/lib/core' {
    export = hljs;
}

declare module 'highlight.js/lib/core.js' {
    export = hljs;
}

declare module 'highlight.js/lib/languages/*' {
    export default function(hljs?: HLJSApi): LanguageDetail;
}
