import { getNodePath, Media, Selector, SimpleSelector, Declaration, UnknownAtRule, Node, NodeType } from '../parser/cssNodes.js';
import { getEntryDescription } from '../languageFacts/entry.js';
import '../languageFacts/colors.js';
import { SelectorPrinting } from './selectorPrinting.js';
import { startsWith } from '../utils/strings.js';
import '../cssLanguageTypes.js';
import { isDefined } from '../utils/objects.js';
import { MarkupKind, Range } from '../../../../vscode-languageserver-types/lib/esm/main.js';

/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
class CSSHover {
    constructor(clientCapabilities, cssDataManager) {
        this.clientCapabilities = clientCapabilities;
        this.cssDataManager = cssDataManager;
        this.selectorPrinting = new SelectorPrinting(cssDataManager);
    }
    configure(settings) {
        this.defaultSettings = settings;
    }
    doHover(document, position, stylesheet, settings = this.defaultSettings) {
        function getRange(node) {
            return Range.create(document.positionAt(node.offset), document.positionAt(node.end));
        }
        const offset = document.offsetAt(position);
        const nodepath = getNodePath(stylesheet, offset);
        /**
         * nodepath is top-down
         * Build up the hover by appending inner node's information
         */
        let hover = null;
        let flagOpts;
        for (let i = 0; i < nodepath.length; i++) {
            const node = nodepath[i];
            if (node instanceof Media) {
                const regex = /@media[^\{]+/g;
                const matches = node.getText().match(regex);
                flagOpts = {
                    isMedia: true,
                    text: matches?.[0],
                };
            }
            if (node instanceof Selector) {
                hover = {
                    contents: this.selectorPrinting.selectorToMarkedString(node, flagOpts),
                    range: getRange(node),
                };
                break;
            }
            if (node instanceof SimpleSelector) {
                /**
                 * Some sass specific at rules such as `@at-root` are parsed as `SimpleSelector`
                 */
                if (!startsWith(node.getText(), '@')) {
                    hover = {
                        contents: this.selectorPrinting.simpleSelectorToMarkedString(node),
                        range: getRange(node),
                    };
                }
                break;
            }
            if (node instanceof Declaration) {
                const propertyName = node.getFullPropertyName();
                const entry = this.cssDataManager.getProperty(propertyName);
                if (entry) {
                    const contents = getEntryDescription(entry, this.doesSupportMarkdown(), settings);
                    if (contents) {
                        hover = {
                            contents,
                            range: getRange(node),
                        };
                    }
                    else {
                        hover = null;
                    }
                }
                continue;
            }
            if (node instanceof UnknownAtRule) {
                const atRuleName = node.getText();
                const entry = this.cssDataManager.getAtDirective(atRuleName);
                if (entry) {
                    const contents = getEntryDescription(entry, this.doesSupportMarkdown(), settings);
                    if (contents) {
                        hover = {
                            contents,
                            range: getRange(node),
                        };
                    }
                    else {
                        hover = null;
                    }
                }
                continue;
            }
            if (node instanceof Node && node.type === NodeType.PseudoSelector) {
                const selectorName = node.getText();
                const entry = selectorName.slice(0, 2) === '::' ? this.cssDataManager.getPseudoElement(selectorName) : this.cssDataManager.getPseudoClass(selectorName);
                if (entry) {
                    const contents = getEntryDescription(entry, this.doesSupportMarkdown(), settings);
                    if (contents) {
                        hover = {
                            contents,
                            range: getRange(node),
                        };
                    }
                    else {
                        hover = null;
                    }
                }
                continue;
            }
        }
        if (hover) {
            hover.contents = this.convertContents(hover.contents);
        }
        return hover;
    }
    convertContents(contents) {
        if (!this.doesSupportMarkdown()) {
            if (typeof contents === 'string') {
                return contents;
            }
            // MarkupContent
            else if ('kind' in contents) {
                return {
                    kind: 'plaintext',
                    value: contents.value,
                };
            }
            // MarkedString[]
            else if (Array.isArray(contents)) {
                return contents.map((c) => {
                    return typeof c === 'string' ? c : c.value;
                });
            }
            // MarkedString
            else {
                return contents.value;
            }
        }
        return contents;
    }
    doesSupportMarkdown() {
        if (!isDefined(this.supportsMarkdown)) {
            if (!isDefined(this.clientCapabilities)) {
                this.supportsMarkdown = true;
                return this.supportsMarkdown;
            }
            const hover = this.clientCapabilities.textDocument && this.clientCapabilities.textDocument.hover;
            this.supportsMarkdown = hover && hover.contentFormat && Array.isArray(hover.contentFormat) && hover.contentFormat.indexOf(MarkupKind.Markdown) !== -1;
        }
        return this.supportsMarkdown;
    }
}

export { CSSHover };
