// Import necessary modules, components, and models from Vue and the project
import { Component, Vue, Prop } from "vue-facing-decorator";
import VsInput from "@/components/vs-input/vs-input.vue";
import VsResult from "@/components/vs-result/vs-result.vue";
import FacetCategory from "@/components/face-category/facet-category.vue";
import ActiveFacetCategory from "@/components/active-facet-category/active-facet-category.vue";

// Import models and services
// import { SolrSettings } from "@/models/solr";
import { OpenSettings } from "@/models/solr";
import DatasetService from "../../services/dataset.service";
import { Suggestion, Dataset, SearchType } from "@/models/dataset";
// import { SolrResponse, FacetFields, FacetItem, FacetResults, FacetInstance } from "@/models/headers";
// import { SolrResponse, FacetFields, FacetItem, FacetResults, FacetInstance, OpenSearchResponse, HitHighlight } from "@/models/headers";
import { FacetItem, FacetResults, OpenSearchResponse } from "@/models/headers";
import { ActiveFilterCategories } from "@/models/solr";
// import { SOLR_HOST, SOLR_CORE } from "@/constants";
import { IPagination } from "@/models/pagination";
import PaginationComponent from "@/components/PaginationComponent.vue";

import { OPEN_HOST, OPEN_CORE } from "@/constants";

// Define the Vue component, its name, and child components
@Component({
    name: "SearchViewComponent",
    components: {
        VsInput,
        VsResult,
        FacetCategory,
        ActiveFacetCategory,
        PaginationComponent,
    },
})

// Export the default class for the component
export default class SearchViewComponent extends Vue {
    // Define props passed from the parent component
    @Prop() 
    display!: string; // Search display string
    @Prop() 
    type!: string; // Search type
    
    // Declare variables used in the component
    results: Array<Dataset> = []; // Array to hold search results
    facets: FacetResults = new FacetResults(); // Object to hold facet results
    searchTerm: string | Suggestion = ""; // The search term input
    activeFilterCategories: ActiveFilterCategories = new ActiveFilterCategories(); // Active filter categories for search
    pagination: IPagination = { // Pagination data for the results
        total: 0,
        perPage: 10,
        currentPage: 1,
        data: [],
    };
    loaded = false; // Boolean to track whether data has been loaded
    numFound!: number; // Number of results found

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

    // Define settings for the OpenSearch API (core and host information)
    private open: OpenSettings = {
        core: OPEN_CORE, //
        host: OPEN_HOST, //
    };

    private error = "";

    // Computed property to get search term as string
    get stringSearchTerm(): string {
        // If searchTerm is a string, return it directly
        if (typeof this.searchTerm === "string") {
            return this.searchTerm;
        // If searchTerm is a Suggestion, return its value and type alias
        } else if (this.searchTerm instanceof Suggestion) {
            return this.searchTerm.value + " (" + this.getTypeAlias(this.searchTerm.type) + ")";
            // return this.searchTerm.value + " (" + this.searchTerm.type + ")";
        // Default to empty string
        } else {
            return "";
        }
    }

    /**
     * The alias for the search term 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;
        }
    }

    // Method to check if a search term is present
    hasSearchTerm(): boolean {
        if (typeof this.searchTerm === "string" && this.searchTerm !== "") {
            return true;
        } else if (this.searchTerm instanceof Suggestion && this.searchTerm.value !== "") {
            return true;
        } else {
            return false;
        }
    }

    // Method to get enum key by enum value
    getEnumKeyByEnumValue<T extends { [index: string]: string }>(myEnum: T, enumValue: string): keyof T | null {
        const keys = Object.keys(myEnum).filter((x) => myEnum[x] == enumValue);
        return keys.length > 0 ? keys[0] : null;
        // return keys[0];
    }

    // Lifecycle hook: executed before the component is mounted
    beforeMount(): void {
        // console.log("beforeMount!");
        
        // Trigger search based on provided display and type props
        if (this.display != "" && this.type != undefined) {
            const enumKey: "Title" | "Author" | "Subject" | "Doctype" | null = this.getEnumKeyByEnumValue(SearchType, this.type);
            if (enumKey) {
                const suggestion = new Suggestion(this.display, "NO-IDEA", SearchType[enumKey]);
                // const suggestion = new Suggestion(this.display, "" , SearchType[enumKey]);
                this.onSearch(suggestion);
            } else {
                this.onSearch(this.display);
            }
        } else if (this.display != "" && this.type == undefined) {
            this.onSearch(this.display);
        } else {
            this.onSearch("");
        }
    }

    // Method to trigger a search
    onSearch(suggestion: Suggestion | string): void {
        // console.log("ONSEARCH");
        
        // Reset active filter categories and facet results
        this.activeFilterCategories = new ActiveFilterCategories();
        this.facets = new FacetResults();
        this.searchTerm = suggestion;
        
        // /* Perform faceted search. The method returns an Observable, and the code subscribes to this Observable to handle the response. If the response is successful, it calls the dataHandler method 
        // with the Solr response as a parameter. If there is an error, it calls the errorHandler method with the error message as a parameter  */
        // DatasetService.facetedSearchSOLR(suggestion, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe({
        //     next: (res: SolrResponse) => this.dataHandler(res),
        //     error: (error: string) => this.errorHandler(error),
        // });

        DatasetService.facetedSearch(suggestion, this.activeFilterCategories, this.open.core, this.open.host, undefined).subscribe({
            next: (res: OpenSearchResponse) => this.dataHandler(res),
            error: (error: string) => this.errorHandler(error),
        });

    }

    // Handle the search results
    private dataHandler(res: OpenSearchResponse, filterItem?: FacetItem): void {
        this.results = res.hits.hits.map(hit => hit._source);
        this.numFound = res.hits.total.value;
        this.pagination.total = res.hits.total.value;
        this.pagination.perPage = 10;
        this.pagination.data = this.results;
        this.pagination.lastPage = Math.ceil(this.pagination.total / this.pagination.perPage);

        if (res.aggregations) {
            const facet_fields = res.aggregations;
    
            let prop: keyof typeof facet_fields;
    
            // Iterate through facet fields
            for (prop in facet_fields) {
                const facetCategory = facet_fields[prop];
                if (facetCategory.buckets) {
                    const facetItems = facetCategory.buckets.map(bucket => new FacetItem(bucket.key, bucket.doc_count));
    
                    let facetValues = facetItems.map((facetItem) => {
                        let rObj: FacetItem;
                        // Check if current facet item matches filter item
                        if (filterItem?.val == facetItem.val) {
                            rObj = filterItem;
                        } else if (this.facets[prop]?.some((e) => e.val === facetItem.val)) {
                            const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val);
                            rObj = this.facets[prop][indexOfFacetValue];
                            rObj.count = facetItem.count;
                        } else {
                            // Create new facet item
                            rObj = new FacetItem(facetItem.val, facetItem.count);
                        }
                        return rObj;
                    });
    
                    // Filter out null values and values with count <= 0
                    facetValues = facetValues.filter(el => el.count > 0);
                    this.facets[prop] = facetValues;
                }
            }
        }
    }

    // // Method to handle search response
    // private dataHandlerSOLR(res: SolrResponse, filterItem?: FacetItem): void {
    //     // console.log("dataHandlerSOLR (docs, numFound):");
    //     // console.log(res.response.docs);
    //     // console.log(res.response.numFound);

    //     // Update results
    //     this.results = res.response.docs;
    //     this.numFound = res.response.numFound;

    //     // Update pagination
    //     this.pagination["total"] = res.response.numFound;
    //     this.pagination["perPage"] = res.responseHeader.params.rows as number;
    //     this.pagination["data"] = res.response.docs;
    //     this.pagination.lastPage = Math.ceil(this.pagination.total / this.pagination.perPage);

    //     const facet_fields: FacetFields = res.facets;
        
    //     /* This code declares a variable prop with a type of keys of the facet_fields object. The keyof typeof facet_fields type represents the keys of the facet_fields object. 
    //     This means that prop can only hold values that are keys of the facet_fields object. */
    //     let prop: keyof typeof facet_fields;
        
    //     // Iterate through facet fields
    //     for (prop in facet_fields) {
    //         const facetCategory = facet_fields[prop];
    //         if (facetCategory.buckets) {
    //             const facetItems: Array<FacetItem> = facetCategory.buckets;

    //             let facetValues = facetItems.map((facetItem) => {
    //                 let rObj: FacetItem;
    //                 // Check if current facet item matches filter item
    //                 if (filterItem?.val == facetItem.val) {
    //                     rObj = filterItem;
    //                 } else if (this.facets[prop]?.some((e) => e.val === facetItem.val)) {
    //                     // console.log(facetValue + " is included")
    //                     // Update existing facet item with new count
    //                     const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val);
    //                     // console.log(indexOfFacetValue);
    //                     rObj = this.facets[prop][indexOfFacetValue];
    //                     rObj.count = facetItem.count;
    //                     // rObj = new FacetItem(val, count);
    //                 } else {
    //                     // Create new facet item
    //                     rObj = new FacetItem(facetItem.val, facetItem.count);
    //                 }
    //                 return rObj;
    //             });

    //             // Filter out null values and values with count <= 0
    //             facetValues = facetValues.filter(function (el) {
    //                 return el != null && el.count > 0;
    //             });
    //             // this.facets[prop] = facetCategory;
    //             // Update facet values
    //             this.facets[prop] = facetValues;
    //         }
    //     }
    // }

    // Method to handle search errors
    private errorHandler(err: string): void {
        this.error = err;
    }

    // Method to handle pagination
    onMenuClick(page: number) {
        console.log("onMenuClick");
        
        this.pagination.currentPage = page;
        const start = page * this.pagination.perPage - this.pagination.perPage;

        // // Trigger new search with updated pagination parameters
        // DatasetService.facetedSearchSOLR(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, start.toString()).subscribe(
        //     (res: SolrResponse) => this.dataHandler(res),
        //     (error: string) => this.errorHandler(error),
        // );

        DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.open.core, this.open.host, start.toString()).subscribe({
            next: (res: OpenSearchResponse) => this.dataHandler(res),
            error: (error: string) => this.errorHandler(error),
        });
    }

    // Method to handle facet filtering
    onFilter(facetItem: FacetItem): void {
        console.log("onFilter");
        
        // Reset current page
        this.pagination.currentPage = 1;

        // Check if filter item already exists
        if (!Object.prototype.hasOwnProperty.call(this.activeFilterCategories, facetItem.category)) {
            this.activeFilterCategories[facetItem.category] = new Array<string>();
        }

        // Check if filter item is not already applied
        if (!this.activeFilterCategories[facetItem.category].some((e) => e === facetItem.val)) {
            // Add filter item to active filter categories
            this.activeFilterCategories[facetItem.category].push(facetItem.val);

            // DatasetService.facetedSearchSOLR(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe(
            //     (res: SolrResponse) => this.dataHandler(res, facetItem),
            //     (error: string) => this.errorHandler(error),
            // );

            // Trigger new search with updated filter
            DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.open.core, this.open.host, undefined).subscribe({
                next: (res: OpenSearchResponse) => this.dataHandler(res, facetItem),
                error: (error: string) => this.errorHandler(error),
            });
        }
    }

    // // // Method to clear facet category filter
    // onClearFacetCategorySOLR(categoryName: string): void {
    //     console.log("onClearFacetCategory");
        
    //     delete this.activeFilterCategories[categoryName];

    //     // Trigger new search with updated filter
    //     DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.solr.core, this.solr.host, undefined).subscribe({
    //         next: (res: SolrResponse) => {
    //             this.results = res.response.docs;
    //             this.numFound = res.response.numFound;

    //             // pagination
    //             this.pagination["total"] = res.response.numFound;
    //             this.pagination["perPage"] = res.responseHeader.params.rows as number;
    //             this.pagination["currentPage"] = 1;
    //             this.pagination["data"] = res.response.docs;

    //             const facet_fields: FacetFields = res.facets;
    //             let prop: keyof typeof facet_fields;
    //             for (prop in facet_fields) {
    //                 const facetCategory: FacetInstance = facet_fields[prop];
    //                 if (facetCategory.buckets) {
    //                     const facetItems: Array<FacetItem> = facetCategory.buckets;

    //                     const facetValues = facetItems.map((facetItem) => {
    //                         let rObj: FacetItem;
    //                         if (this.facets[prop]?.some((e) => e.val === facetItem.val)) {
    //                             // console.log(facetValue + " is included")
    //                             // Update existing facet item with new count
    //                             const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val);
    //                             // console.log(indexOfFacetValue);
    //                             rObj = this.facets[prop][indexOfFacetValue];
    //                             rObj.count = facetItem.count;
    //                             // rObj = new FacetItem(val, count);
    //                             // if facet ccategory is reactivated category, deactivate all filter items
    //                             if (prop == categoryName) {
    //                                 rObj.active = false;
    //                             }
    //                         } else {
    //                             // Create new facet item
    //                             rObj = new FacetItem(facetItem.val, facetItem.count);
    //                         }
    //                         return rObj;
    //                     });
    //                     this.facets[prop] = facetValues;
    //                 }
    //             }
    //         },
    //         error: (error: string) => this.errorHandler(error),
    //         complete: () => console.log("clear facet category completed"),
    //     });
    // }

    // Method to clear facet category filter
    onClearFacetCategory(categoryName: string): void {
        // console.log("onClearFacetCategory");
        delete this.activeFilterCategories[categoryName];

        // Trigger new search with updated filter
        DatasetService.facetedSearch(this.searchTerm, this.activeFilterCategories, this.open.core, this.open.host, undefined).subscribe({
            next: (res: OpenSearchResponse) => {
                this.results = res.hits.hits.map(hit => hit._source);
                this.numFound = res.hits.total.value;
    
                // Update pagination
                this.pagination.total = res.hits.total.value;
                this.pagination.perPage = 10;
                this.pagination.currentPage = 1;
                this.pagination.data = this.results;
                this.pagination.lastPage = Math.ceil(this.pagination.total / this.pagination.perPage);
    
                if (res.aggregations) {
                    const facet_fields = res.aggregations;
    
                    let prop: keyof typeof facet_fields;
    
                    for (prop in facet_fields) {
                        const facetCategory = facet_fields[prop];
                        if (facetCategory.buckets) {
                            const facetItems = facetCategory.buckets.map(bucket => new FacetItem(bucket.key, bucket.doc_count));
    
                            const facetValues = facetItems.map((facetItem) => {
                                let rObj: FacetItem;
                                if (this.facets[prop]?.some((e) => e.val === facetItem.val)) {
                                    // Update existing facet item with new count
                                    const indexOfFacetValue = this.facets[prop].findIndex((i) => i.val === facetItem.val);
                                    rObj = this.facets[prop][indexOfFacetValue];
                                    rObj.count = facetItem.count;
                                    // if facet category is reactivated category, deactivate all filter items
                                    if (prop === categoryName) {
                                        rObj.active = false;
                                    }
                                } else {
                                    // Create new facet item
                                    rObj = new FacetItem(facetItem.val, facetItem.count);
                                }
                                return rObj;
                            }).filter(el => el.count > 0); // Filter out items with count <= 0
    
                            this.facets[prop] = facetValues;
                        }
                    }
                }
            },
            error: (error: string) => this.errorHandler(error),
            complete: () => console.log("Clear facet category completed"),
        });
    }

}
