import { IgniteHtml } from '../ignite-html/ignite-html.js';
import { IgniteElement } from "../ignite-html/ignite-element.js";
import { IgniteTemplate, list, div, input, button, h4, span } from "../ignite-html/ignite-template.js";
import { Chip } from "./chip.js";
import { Popper } from "./popper.js";
import { LinearProgress } from "./linear-progress.js";

class ChipList extends IgniteElement {
    constructor() {
        super();
    }

    get styles() {
        return /*css*/`
            mt-chip-list {
                display: flex;
                flex-direction: row;
                flex-wrap: wrap;
                gap: 12px;
                border: solid 0.13rem #ced4da;
                border-radius: 0.3rem;
                padding: 0.4rem;
            }

            mt-chip-list.no-border {
                border-color: transparent;
            }

            mt-chip-list:focus {
                outline: none;
            }

            mt-chip-list:hover {
                cursor: pointer;
            }

            mt-chip-list.editing {
                border: solid 0.13rem rgba(0,0,0,0.1);
                border-radius: 0.3rem;
            }

            mt-chip-list.placeholder {
                border: solid 0.13rem #ced4da;
                border-radius: 0.3rem;
                color: rgba(0,0,0,0.6);
            }

            mt-chip-list > .input-container {
                display: flex;
                flex-direction: column;
                justify-content: center;
                flex: 1;
                flex-basis: auto;
                flex-shrink: 1;
            }

            mt-chip-list > .input-container > .input {
                outline: none;
            }

            mt-chip-list .search-result:hover {
                background-color: #e0e0e0;
                border-radius: 0.3em;
            }
        `;
    }

    get properties() {
        return {
            items: [],
            itemsMax: Number.MAX_SAFE_INTEGER,
            placeholder: null,
            stopEditingOnBlur: true,
            editing: false,
            input: null,
            searchBox: null,
            search: true,
            searching: false,
            showSearchResults: true,
            searchLoading: false,
            searchResults: null,
            searchPlaceholder: "No results found.",
            searchFooter: null,
            onSearch: null,
            onSearchDelay: 200,
            onSearchCallback: null,
            blurTimeout: null,
            documentListener: null,
            freeForm: true,
            chipBackground: null,
            chipColor: null,
            changed: false,
            border: false,
            readOnly: false,
        };
    }

    render() {
        return this.template
            .style("position", "relative")
            .class(this.border, value => value ? null : "no-border")
            .class(this.editing, value => value ? "editing" : null)
            .attribute("tabindex", "0")
            .onFocus(e => this.onFocus())
            .class([this.editing, this.items], (editing, items) => {
                return !editing && (items == null || items.length == 0) ? "placeholder" : null;
            })
            .child(
                new span(this.placeholder).hide([this.editing, this.items], (editing, items) => {
                    return editing || (items != null && items.length > 0);
                }),
                new list(this.items, (item) => {
                    return new Chip()
                        .id(item.id)
                        .property("color", item.chipColor ? item.chipColor : this.chipColor)
                        .property("background", item.chipBackground ? item.chipBackground : this.chipBackground)
                        .property("readOnly", this.readOnly)
                        .property("onDelete", () => {
                            this.items = this.items.filter(needle => needle != item);
                            this.changed = true; //Make sure changed flag was set. 
                        })
                        .child(item.content);
                }),
                new div()
                    .class("input-container")
                    .child(
                        new div()
                            .class("input")
                            .attribute("contenteditable", "true")
                            .hide(this.editing, value => { return !value; })
                            .ref(this.input)
                            .onEnter((e) => {
                                e.preventDefault();

                                //If we are read only don't do anything.
                                if (this.readOnly) {
                                    return;
                                }

                                //If this chip allows free form input then add a new item.
                                if (this.freeForm && this.input.textContent.trim().length >= 1) {
                                    if (this.items == null) {
                                        this.items = [];
                                    }

                                    //Add a new item to the chip list.
                                    this.items.push({ content: this.input.textContent.trim() });
                                    this.input.innerHTML = "";
                                    this.searching = false; //Reset searching since we just added a item.
                                    this.changed = true; //Make sure changed flag was set.
                                }
                            })
                            .onBackspace((e) => {
                                //If we are read only don't do anything.
                                if (this.readOnly) {
                                    return;
                                }

                                //If the backspace key is pressed and there is no content, try to remove the last item from the list.
                                if (this.input.textContent.length == 0 || (this.input.textContent.length == 1 && this.input.textContent[0] == " ")) {
                                    e.preventDefault();

                                    if (this.items) {
                                        this.items.pop();
                                        this.changed = true; //Make sure changed flag was set.
                                    }

                                    this.searching = false;
                                }
                            })
                            .on("keydown", (e) => {
                                //If we are read only don't do anything.
                                if (this.readOnly) {
                                    return;
                                }

                                //If the escape key is pressed stop searching until something else happens.
                                if (e.key == "Escape") {
                                    this.searching = false;
                                    e.preventDefault();
                                    e.stopPropagation();
                                    return;
                                }

                                //Reset the searching and input if we get a tab, since the browser
                                //will focus the next avaiable element.
                                if (e.key == "Tab") {
                                    this.searching = false;

                                    if (this.stopEditingOnBlur) {
                                        this.editing = false;

                                        //Fire a change event if there was a change.
                                        if (this.changed) {
                                            this.changed = false;
                                            this.dispatchEvent(new Event("change"));
                                        }
                                    }

                                    this.input.innerHTML = "";
                                    return;
                                }

                                //If we are not searching and a key was pressed, open the search box.
                                if (!this.searching && this.search && (e.key !== "Backspace" || (e.key == "Backspace" && e.target.textContent.length > 1)) && (this.items == null || this.items.length < this.itemsMax)) {
                                    this.searching = true;
                                    this.showSearchResults = true;
                                } else if (this.items != null && this.items.length >= this.itemsMax && e.key !== "Backspace") {
                                    //Don't allow input if we reached the max number of items.
                                    e.preventDefault();
                                }

                                //If we are searching and we have a on search function invoke it.
                                if (this.searching && this.onSearch) {
                                    if (this.onSearchCallback) {
                                        clearTimeout(this.onSearchCallback);
                                    }
                                    this.onSearchCallback = setTimeout(() => this.onSearch(this, this.input.textContent.trim()), this.onSearchDelay);
                                }
                            })
                    ),
                new Popper()
                    .property("show", this.searching)
                    .child(
                        new div()
                            .class("d-flex flex-column justify-content-center p-2 shadow bg-white")
                            .style("border-radius", "0.4em")
                            .child(
                                new LinearProgress().class("my-2").property("loading", this.searchLoading),
                                new span(this.searchPlaceholder).class("mt-2").hide([this.searchResults, this.showSearchResults, this.searchLoading], (searchResults, showSearchResults, searchLoading) => {
                                    //Dont show the placeholder if we have search results, or if we are not showing search results.
                                    return (searchResults != null && searchResults.length > 0 && !searchLoading) || (!showSearchResults || searchLoading);
                                }),
                                new list(this.searchResults, item => {
                                    return new div(item.content).class("search-result p-2").onClick(() => this.searchResultClick(item));
                                }).hide([this.showSearchResults, this.searchLoading], (showSearchResults, searchLoading) => !showSearchResults || searchLoading),
                                this.searchFooter
                            )
                    ),
            )

    }

    ready() {
        //Add a listener to the document click to blur our element.
        this.documentListener = (e) => this.onBlur(e);
        window.document.addEventListener("click", this.documentListener);
    }

    cleanup() {
        window.document.removeEventListener("click", this.documentListener);
    }

    onFocus() {
        if (!this.readOnly) {
            if (!this.editing) {
                this.editing = true;
                this.input.focus();
                this.input.textContent = " ";
            } else {
                this.input.focus();
            }
        }
    }

    onBlur(e) {
        //Only blur if we are editing and the target is not ourself or any of our children.
        if (this.editing) {
            if (e.target != this && !this.contains(e.target)) {
                if (this.stopEditingOnBlur) {
                    this.editing = false;

                    //Fire a change event if there was a change.
                    if (this.changed) {
                        this.changed = false;
                        this.dispatchEvent(new Event("change"));
                    }
                }
                this.searching = false;
                this.input.blur();
                this.input.innerHTML = "";
            }
        }
    }

    searchResultClick(item) {
        if (this.items == null) {
            this.items = [];
        }

        this.changed = true; //Make sure changed flag is set.
        this.items.push(item);
        this.searching = false;
        this.input.innerHTML = "";
        this.input.focus();
    }
}

class ChipListTemplate extends IgniteTemplate {
    constructor(...children) {
        super("mt-chip-list", children);
    }
}

IgniteHtml.register("mt-chip-list", ChipList);

export {
    ChipListTemplate as ChipList
}