// import Vue from "vue";
// import debounce from 'lodash/debounce';
// import { DatasetService } from "../../services/dataset.service";
import DatasetService from "../../services/dataset.service";
// import { SolrSettings } from "@/models/solr";  // PENDING USE

import { OpenSettings } from "@/models/solr";
import { Component, Vue, Prop, Emit } from "vue-facing-decorator";
import { Dataset, Suggestion, SearchType } from "@/models/dataset";
// import { SOLR_HOST, SOLR_CORE } from "@/constants";

import { OPEN_HOST, OPEN_CORE } from "@/constants"; // PENDING USE
import { HitHighlight } from "@/models/headers";

import DOMPurify from 'dompurify'; // To sanitize the HTML content to prevent XSS attacks!

@Component({
    name: "VsInput",
})
export default class VsInput extends Vue {
    // @Prop()
    // private title!: string;

    // Define the placeholder text for the input field
    @Prop({ default: "Search" })
    readonly placeholder!: string;

    private display = ""; // Input display value

    @Prop()
    private propDisplay = "";

    private value!: Suggestion | string;
    private error = "";
    private results: Array<Dataset> = []; // Array to store search results
    private highlights: Array<HitHighlight> = [];

    private loading = false; // Loading state indicator
    private selectedIndex = -1; // Index of the currently selected suggestion

    // private solr: SolrSettings = {
    //     core: SOLR_CORE, //"rdr_data", // SOLR.core;
    //     host: SOLR_HOST, //"tethys.at",
    // };

    private openSearch: OpenSettings = {
        core: OPEN_CORE, //"rdr_data", // SOLR.core;
        host: OPEN_HOST, //"tethys.at",
        // core: "test_data", // SOLR.core;
        // host: "repository.geologie.ac.at",
    };

    // private rdrAPI!: DatasetService;
    itemRefs!: Array<Element>; // Array to store references to suggestion items
    emits = ["filter"]; // Emits filter event

    // Set reference for each item
    setItemRef(el: Element): void {
        this.itemRefs.push(el);
    }

    beforeUpdate(): void {
        this.itemRefs = [];
    }

    mounted(): void {
        // this.rdrAPI = new DatasetService();
    }

    get showResults(): boolean {
        return this.results.length > 0;
    }

    get noResults(): boolean {
        return Array.isArray(this.results) && this.results.length === 0;
    }

    get isLoading(): boolean {
        return this.loading === true;
    }

    get hasError(): boolean {
        return this.error !== null;
    }

    // Computed property to generate suggestions based on search results
    get suggestions(): Suggestion[] {

        const suggestions = new Array<Suggestion>();

        // console.log("getSuggestions > Display:", this.display);
        // console.log("results:", this.results );
        // console.log("highlights:", this.highlights);
        
        //The method checks if there are any highlighted titles in the highlight object. If found, it joins the highlighted fragments into a single string
        // Generate suggestions based on search results
        this.results.forEach((dataset, index) => {

            const highlight = this.highlights[index];

            // console.log("get suggestions:id", dataset.id);
            // console.log("get suggestions:title_output", dataset.title_output);
            // console.log("get suggestions:author", dataset.author);
            // console.log("get suggestions:subjects", dataset.subjects);

            // Checks if a suggestion with the same title and type already exists in the suggestions array. If not, it creates a new Suggestion object and adds it to the suggestions array.
            if (highlight.title && highlight.title.length > 0) {
                /** This line checks if the highlight object has a title property and if that property is an array with at least one element.
                 * The highlight object contains highlighted fragments of the search term in various fields (e.g., title, author, subjects) as returned by the OpenSearch API.
                 * This check ensures that we only process results that have highlighted titles. */
                const highlightedTitle = highlight.title.join(" ");
                /**
                 * The highlight.title property is an array of strings, where each string is a highlighted fragment of the title. join(" ") combines these fragments into a single string with spaces between them.
                 * This step constructs a full highlighted title from the individual fragments.
                 * OpenSearch can return multiple fragments of a field (like the title) in its response, especially when the field contains multiple terms that match the search query. 
                 * This can happen because OpenSearch's highlighting feature is designed to provide context around each match within the field, which can result in multiple highlighted fragments.
                 */
                const hasTitleSuggestion = suggestions.some((suggestion) => suggestion.highlight.toLowerCase() === highlightedTitle.toLowerCase() && suggestion.type == SearchType.Title);
                if (!hasTitleSuggestion) {
                    const suggestion = new Suggestion(dataset.title_output, highlightedTitle, SearchType.Title);
                    suggestions.push(suggestion);
                }
            }
            if (highlight.author && highlight.author.length > 0) {
                const highlightedAuthor = highlight.author.join(" ");
                const datasetAuthor = this.find(dataset.author, this.display.toLowerCase());
                const hasAuthorSuggestion = suggestions.some((suggestion) => suggestion.highlight.toLowerCase() === highlightedAuthor.toLowerCase() && suggestion.type == SearchType.Author);
                if (!hasAuthorSuggestion) {
                    const suggestion = new Suggestion(datasetAuthor, highlightedAuthor, SearchType.Author);
                    suggestions.push(suggestion);
                }
            }
            if (highlight.subjects && highlight.subjects.length > 0) {
                const highlightedSubject = highlight.subjects.join(" ");
                const datasetSubject = this.find(dataset.subjects, this.display.toLowerCase());
                const hasSubjectSuggestion = suggestions.some((suggestion) => suggestion.highlight.toLowerCase() === highlightedSubject.toLowerCase() && suggestion.type == SearchType.Subject);
                if (!hasSubjectSuggestion) {
                    const suggestion = new Suggestion(datasetSubject, highlightedSubject, SearchType.Subject);
                    suggestions.push(suggestion);
                }
            }

            // To allow search by doctype
            if (highlight.doctype && highlight.doctype.length > 0) {
                const highlightedDoctype = highlight.doctype.join(" ");

                const hasDoctypeSuggestion = suggestions.some((suggestion) => suggestion.highlight.toLowerCase() === highlightedDoctype.toLowerCase() && suggestion.type == SearchType.Doctype);
                if (!hasDoctypeSuggestion) {
                    const suggestion = new Suggestion(dataset.doctype, highlightedDoctype, SearchType.Doctype);
                    suggestions.push(suggestion);
                }
            }

            // ORIGINAL SOLR ===================================================================================================
            // if (dataset.title_output.toLowerCase().includes(this.display.toLowerCase())) {
            //     const title = dataset.title_output;
            //     // Check if there is already a suggestion with this title and type
            //     const hasTitleSuggestion = suggestions.some((suggestion) => suggestion.value === title && suggestion.type == SearchType.Title);
            //     if (!hasTitleSuggestion) {
            //         // If there is no such suggestion, create a new one and add it to the suggestions array
            //         const suggestion = new Suggestion(title, SearchType.Title);
            //         suggestions.push(suggestion);
            //     }
            // }
            // if (this.find(dataset.author, this.display.toLowerCase()) !== "") {
            //     const author = this.find(dataset.author, this.display.toLowerCase());
            //     // Check if there is already a suggestion with this author and type
            //     const hasAuthorSuggestion = suggestions.some((suggestion) => suggestion.value === author && suggestion.type == SearchType.Author);
            //     if (!hasAuthorSuggestion) {
            //         const suggestion = new Suggestion(author, SearchType.Author);
            //         suggestions.push(suggestion);
            //     }
            // }
            // if (this.find(dataset.subjects, this.display.toLowerCase()) != "") {
            //     const subject = this.find(dataset.subjects, this.display.toLowerCase());
            //     const hasSubjectSuggestion = suggestions.some((suggestion) => suggestion.value === subject && suggestion.type == SearchType.Subject);
            //     if (!hasSubjectSuggestion) {
            //         const suggestion = new Suggestion(subject, SearchType.Subject);
            //         suggestions.push(suggestion);
            //     }
            // }

        });

        return suggestions;
    }

    /**
     * This method combines the suggestion value and type into a single HTML string. It also sanitizes the HTML content using DOMPurify to prevent XSS attacks.
     * The vue file uses the v-html directive to bind the combined HTML string to the label element. This ensures that the HTML content (e.g., <em>Wien</em>) is rendered correctly in the browser.
     */
    formatSuggestion(result: Suggestion): string {
        const sanitizedValue = DOMPurify.sanitize(result.highlight);
        // Replacing the predefined format for highlights given by OpenSearch from <em> emphasys to <b> bold
        const replacedValue = sanitizedValue.replace(/<em>/g, '<b>').replace(/<\/em>/g, '</b>');
        // return `${replacedValue} <em>| ${result.type}</em>`;
        return `${replacedValue} <em>| ${this.getTypeAlias(result.type)}</em>`;
    }

    /**
     * The alias for the result type will be set depending on the name of the type. 
     * This will allow to display the customised terms instead of the values currently used in the OpenSearch index.
     * TODO: This should be corrected directly in the index
     */
    getTypeAlias(type: string): string {
        switch (type) {
            case "author":
                return "creator";
            case "subjects":
                return "keyword";
            case "doctype":
                return "data type";
            default:
                return type;
        }
    }

    /**
     *  Clear all values, results and errors
     **/
    clear(): void {
        console.log("clear");
        this.display = "";
        // this.value = null;
        this.results = [];
        this.error = "";
        // this.$emit("clear");
    }

    /* When the search button is clicked or the search input is changed, it updates the value property of the component with the current value of display, 
    and emits a search-change event with the current value of display as the argument. */
    @Emit("search-change")
    search(): string {
        console.log("search");
        this.results = [];
        // this.$emit("search", this.display)
        this.value = this.display; //(obj["title_output"]) ? obj["title_output"] : obj.id
        return this.display;
    }

    // Handler for search input change
    searchChanged(): void {
        // console.log("Search changed!");
        this.selectedIndex = -1;
        // Let's warn the parent that a change was made
        // this.$emit("input", this.display);
        if (this.display.length >= 2) {
            this.loading = true;
            this.resourceSearch();
        } else {
            this.results = [];
        }
    }

    // Perform the search request
    private resourceSearch() {
        // console.log("resourceSearch");
        if (!this.display) {
            this.results = [];
            return;
        }
        this.loading = true;
        // this.setEventListener();
        this.request();
    }

    // Make the API request to search for datasets
    private request(): void {
        console.log("request()");
        // DatasetService.searchTerm(this.display, this.solr.core, this.solr.host).subscribe({
        DatasetService.searchTerm(this.display, this.openSearch.core, this.openSearch.host).subscribe({
            // next: (res: Dataset[]) => this.dataHandler(res),
            next: (res: { datasets: Dataset[], highlights: HitHighlight[] }) => this.dataHandler(res.datasets, res.highlights),
            error: (error: string) => this.errorHandler(error),
            complete: () => (this.loading = false),
        });
    }

    // Handle the search results
    private dataHandler(datasets: Dataset[], highlights: HitHighlight[]): void {
        this.results = datasets;
        this.highlights = highlights; // Store highlights
        // console.log(datasets);
        
    }

    // Handle errors from the search request
    private errorHandler(err: string): void {
        this.error = err;
    }

    /**
     * Is this item selected?
     * @param {Object}
     * @return {Boolean}
     */
    isSelected(key: number): boolean {
        return key === this.selectedIndex;
    }

    // Handle arrow down key press to navigate suggestions
    onArrowDown(ev: Event): void {
        console.log("onArrowDown");
        ev.preventDefault();
        if (this.selectedIndex === -1) {
            this.selectedIndex = 0;
            return;
        }
        this.selectedIndex = this.selectedIndex === this.suggestions.length - 1 ? 0 : this.selectedIndex + 1;
        this.fixScrolling();
    }

    // Scroll the selected suggestion into view
    private fixScrolling() {
        const currentElement = this.itemRefs[this.selectedIndex];
        currentElement.scrollIntoView({
            behavior: "smooth",
            block: "nearest",
            inline: "start",
        });
    }

    // Handle arrow up key press to navigate suggestions
    onArrowUp(ev: Event): void {
        console.log("onArrowUp");
        ev.preventDefault();
        if (this.selectedIndex === -1) {
            this.selectedIndex = this.suggestions.length - 1;
            return;
        }
        this.selectedIndex = this.selectedIndex === 0 ? this.suggestions.length - 1 : this.selectedIndex - 1;
        this.fixScrolling();
    }

    // Handle enter key press to select a suggestion
    onEnter(): void {
        console.log("onEnter");
        
        if (this.selectedIndex === -1) {
            // this.$emit("nothingSelected", this.display);
            this.display && this.search();
        } else {
            this.select(this.suggestions[this.selectedIndex]);
        }

        // this.$emit("enter", this.display);
    }

    @Emit("search-change")
    private select(obj: Suggestion): Suggestion {
        console.log("select:");
        this.value = obj;   
        console.log(obj);
        
        this.display = obj.value;

        this.close();

        return this.value;
    }

    // Find a search term in an array
    private find(myarray: Array<string>, searchterm: string): string {
        for (let i = 0, len = myarray.length; i < len; i += 1) {
            if (typeof myarray[i] === "string" && myarray[i].toLowerCase().indexOf(searchterm) !== -1) {
                // print or whatever
                return myarray[i];
            }
        }
        return "";
    }

    /**
     *  Close the results list. If nothing was selected clear the search
     */
    close(): void {
        console.log("close");
        if (!this.value) {
            this.clear();
        }
        this.results = [];
        this.error = "";
    }
}
