// Silence may be golden, but sometimes words paint the canvas of understanding.
const LinksyDropdownSearch = {
    name: "LinksyDropdownSearch",
    props: {
        value: String,
        noIcon: Boolean,
		placeholder: 'Search',
    },
    data() {
        return {
            q: "",
        };
    },
    watch: {
        q: debounce(function (val) {
            this.$emit("onChange", val);
        }, 900),
    },
    methods: {
        onCancel() {
            this.q = "";
            this.$emit("onChange", "");
            this.$emit("onSubmit", "");
        },

        onSubmit() {
            this.showDropdown = false;
            this.$emit("onSubmit", this.q);
        },
    },
    template: `
        <div class="searchbar d-flex align-items-center px-2 fs-6 border border-1 position-sticky top-0 rounded-3 mt-1 bg-white z-3 justify-content-between">
        <div class="d-flex flex-row align-items-center">
            <div :class="{ 'd-none': noIcon }">
              <i class="fa fa-magnifying-glass me-2"></i>
            </div>
            <div>
            <input
                class="p-0 border-0 shadow-none h-50 w-100"
                type="text"
                v-model="q"
                :placeholder="placeholder + '...'"
            >
            </div>
            </div>
            <i class="fa fa-times cursor-pointer text-danger"
              @click="onCancel"
              :style="{opacity: q === '' ? 0 : 1}"
            ></i>
        </div>
    `,
};

const LinksyDropdown = {
  name: "LinksyDropdown",
  directives: {
    "click-outside": LinksyClickOutside,
  },
  components: {
    LinksyDropdownSearch,
  },
  props: {
    options: {
      type: Array,
      default: [],
    },
    loadOptions: {
      type: Function,
      default: async () => {
        throw Error("load function not set");
      },
    },
    searchOptions: {
      type: Function,
      default: async (q, items) => {
        if (q.length > 0) {
          items = items.filter((item) => {
            return LinksyDropdown.methods
              .getItemLabel(item)
              .toLowerCase()
              .includes(q.toLowerCase());
          });
        }
        return items;
      },
    },
    hasPreview: Boolean,
    isAsync: Boolean,
    isMulti: Boolean,
    isManual: Boolean,
    isCreatable: Boolean,
    isResetable: Boolean,
    isSearchable: Boolean,
    placeholder: {
      type: String,
      default: "Select",
    },
    initialValue: {
      type: [String, Array, Object],
      default: null,
    },
    disabled: Boolean,
    position: {
      type: String,
      default: "bottom-left",
      validator(value) {
        return this.positions.includes(value);
      },
    },
  },
  data() {
    return {
        q: "",
        items: [],
        isOpen: false,
        isLoading: false,
        cachedItems: [],
        pendingItems: [],
        selectedItems: [],
        showCreateButton: false,
        positions: ["bottom-left", "bottom-right", "top-left", "top-right"],
        positionRect: {
            left: 0,
            right: 0,
            top: 0,
            bottom: 0
        },
    }
    },
    watch: {
        isOpen: {
            handler(val) {
                if (!val) {
                    this.positionRect.left = 0;
                    this.positionRect.top = 0;
                    return;
                }

                setTimeout(() => {
                    const {
                        left: dropdownLeft,
                        top: dropdownTop,
                        height: dropdownHeight,
                        width: dropdownWidth,
                    } = this.$refs.linksyDropdown?.getBoundingClientRect();
                    const {
                        left: bodyLeft,
                        top: bodyTop,
                        width: bodyWidth,
                        height: bodyHeight
                    } = this.$refs.linksyDropdown
                        .querySelector(".linksy-dropdown-body")
                        ?.getBoundingClientRect();

                    if (["bottom-right", "bottom-left"].includes(this.position)) {
                        this.positionRect.top = dropdownTop - bodyTop + dropdownHeight;
                    } else {
                        this.positionRect.top = dropdownTop - bodyTop - bodyHeight;
                    }

                    switch (this.position) {
                        case "bottom-right":
                            this.positionRect.left = dropdownLeft - bodyLeft + (dropdownWidth - bodyWidth);
                            break;
                        case "top-left":
                            this.positionRect.left = dropdownLeft - bodyLeft;
                            break;
                        case "top-right":
                            this.positionRect.left = dropdownLeft - bodyLeft + (dropdownWidth - bodyWidth);
                            break;
                        default:
                            this.positionRect.left = dropdownLeft - bodyLeft;
                            break;
                    }
                }, 10);

                this.pendingItems = [];
                this.initializeOptions();
            },
        },
        options: {
            immediate: true,
            handler: function () {
                this.cachedItems = [];
                setTimeout(() => this.initializeOptions(), 5);
            },
        },
        initialValue: {
            handler: function () {
                setTimeout(() => this.initialize(), 5);
            },
        },
    },
    computed: {
        title: {
            get() {
                if (this.isMulti) {
                    if (this.selectedItems.length > 0) {
                        return `${this.selectedItems.length} Added`;
                    }
                }

                if (this.selectedItems.length > 0) {
                    return this.getItemLabel(
                        this.items.find((e) => {
                            return (
                                this.getItemValue(e) ==
                                this.getItemValue(this.selectedItems[0])
                            );
                        })
                    );
                }

                return this.placeholder;
            },
        },
        uniquePendingItems: {
            get() {
                return this.pendingItems.filter((item) => {
                    return (
                        this.items.findIndex(
                            (e) =>
                                this.getItemValue(e) === this.getItemValue(item)
                        ) < 0
                    );
                });
            },
        },
        hasSelectionChanged: {
            get() {
                if (!this.initialValue) {
                    if (this.selectedItems.length > 0) {
                        return true;
                    }
                }

                if (this.isMulti) {
                    if (this.selectedItems.length != this.initialValue.length) {
                        return true;
                    }

                    if (
                        this.selectedItems.some(
                            (e) => !this.initialValue.includes(e)
                        )
                    ) {
                        return true;
                    }

                    if (
                        this.initialValue.some(
                            (e) => !this.selectedItems.includes(e)
                        )
                    ) {
                        return true;
                    }
                } else {
                    if (
                        this.selectedItems.length > 0 &&
                        this.selectedItems[0] != this.initialValue
                    ) {
                        return true;
                    }
                }

                return false;
            },
        },
    },
    methods: {
        initialize() {
            this.selectedItems = [];
            if (this.initialValue) {
                let initialValue = this.initialValue;

                if (!Array.isArray(initialValue)) {
                    initialValue = [initialValue];
                }

                // todo: validate
                this.selectedItems = [...initialValue];
            }
        },

        initializeOptions() {
            this.items = [];
            this.isLoading = true;

            const setItems = (items) => {
                this.items = items || [];
                this.isLoading = false;
                this.cachedItems = this.items;
            };

            if (this.isAsync) {
                this.loadOptions()
                    .then((res) => {
                        setItems(res);
                    })
                    .catch(() => {
                        setItems(this.options);
                    });

                return;
            }

            const items = [];
            [...this.cachedItems, ...this.options].forEach((e) => {
                if (
                    !items.find(
                        (item) =>
                            this.getItemValue(item) == this.getItemValue(e)
                    )
                ) {
                    items.push(e);
                }
            });

            setItems(items);
        },

        getItemValue(item) {
            return typeof item == "object" ? item.value : item;
        },
        getItemLabel(item) {
            return typeof item == "object" ? item.label : item;
        },

        isItemSelected(item) {
            return this.selectedItems.includes(this.getItemValue(item));
        },

        handleItemClick(item) {
            if (!item) {
                return;
            }

            this.q = "";
            this.pendingItems = []; // reset pending items

            const itemValue = this.getItemValue(item);
            const itemIndex = this.selectedItems.findIndex(
                (e) => e === itemValue
            );

            if (itemIndex !== -1) {
                if (this.isMulti) {
                    this.selectedItems.splice(itemIndex, 1);
                }
            } else {
                if (this.isMulti) {
                    this.selectedItems.push(itemValue);
                } else {
                    this.selectedItems = [itemValue];
                }
            }

            if (!this.isManual) {
                this.onSubmit();
            }
        },

        handleItemAdd(item, reset) {
            if (!item) {
                return;
            }

            const itemValue = this.getItemValue(item)?.trim();

            // todo: handle types
            if (this.items.some((e) => itemValue == this.getItemValue(e))) {
                return;
            }

            setTimeout(() => {
                this.items = [...this.items, item];
                this.cachedItems = this.items;

                if (reset) {
                    this.q = "";
                }

                if (this.isMulti) {
                    this.selectedItems.push(itemValue);
                } else {
                    this.selectedItems = [itemValue];
                }

                this.$emit("onItemAdded", itemValue, this.items);

                if (!this.isManual) {
                    this.onSubmit();
                }
            }, 10);
        },

        handleSearch(q) {
            this.searchOptions(q, this.cachedItems).then((res) => {
                this.q = q;
                const items = Array.from(new Set([...(res || [])]));

                if (this.hasPreview) {
                    this.pendingItems = items;
                } else {
                    if (!this.isCreatable) this.items = items;
                }
            });
        },

        onSubmit() {
            const selectedItems = Object.values(this.selectedItems);
            setTimeout(() => {
                if (!this.isMulti) {
                    this.isOpen = false;
                }

                this.$emit(
                    "selected",
                    this.isMulti
                        ? selectedItems
                        : selectedItems.length > 0
                        ? selectedItems[0]
                        : null,
                    this.items
                );
            }, 5);
        },

        onReset() {
            this.selectedItems = [];

            if (!this.isManual) {
                this.onSubmit();

                if (this.isMulti) {
                    this.isOpen = false;
                }
            }
        },
    },
    template: `
        <div
            ref="linksyDropdown"
            class="linksy-dropdown position-relative"
            :class="{ disabled }"
            v-click-outside="() => (isOpen = false)"
        >
            <div
                class="linksy-dropdown-input rounded cursor-pointer px-2"
                @click="() => (isOpen = disabled? false : !isOpen)"
            >
                <slot :opened="isOpen">
                    <div class="default-placeholder d-flex gap-2 justify-content-between align-items-center text-center py-2">
                        <slot name="title" :title="title">
                            <span class="fs-14px text-nowrap m-0 p-0">{{title}}</span>
                        </slot>
                        <i :class="['fas', 'fa-chevron-' + (isOpen ? 'up' : 'down')]"></i>
                    </div>
                </slot>
            </div>

            <div
                v-if="isOpen"
                class="linksy-dropdown-body p-2 z-3 bg-white d-flex flex-column position-fixed overflow-y-auto overflow-x-hidden shadow rounded gap-2"
                :class="{'opened': isOpen}"
                :style="{'left': positionRect.left + 'px', 'top': positionRect.top + 'px'}"
            >
                <!-- search bar -->
                <LinksyDropdownSearch
                    :no-icon="isCreatable"
                    :placeholder="isCreatable ? 'Type here' : 'Search'"
                    v-if="isSearchable || isCreatable"
                    @onChange="handleSearch"
                ></LinksyDropdownSearch>

                <div>
                    <!-- create button -->
                    <div v-if="isCreatable" class="text-end mb-2">
                      <button :disabled="q.length < 1" @click="() =>{ handleItemAdd(q, true); }">
                        <slot name="creatable-text">Create</slot>
                      </button>
                    </div>

                    <div class="dropdown-items-container">
                        <div v-if="isResetable" @click="onReset" class="mt-1 bg-light-subtle p-1 rounded clickable">
                            <span class="fs-14px">{{placeholder || '--'}}</span>
                        </div>

                        <ul class="bg-white bg-normal my-0 p-1 pb-0 mb-1" v-if="uniquePendingItems.length">
                            <li v-for="item in uniquePendingItems" @click="() => handleItemAdd(item)" class="cursor-pointer d-flex align-items-center justify-content-between gap-2 p-2">
                              <span>{{getItemLabel(item)}}</span>
                              <i class="fa-solid fa-plus-circle text-primary"></i>
                            </li>
                            <hr />
                        </ul>
                        <ul class="d-flex flex-column justify-content-start my-0">
                          <li v-for="(item, index) in items" @click="() => handleItemClick(item)" class="cursor-pointer d-flex align-items-center gap-2 p-1 rounded">
                            <slot name="item" :item="item" :index="index" :selected="isItemSelected(item)">
                              <span v-if="isMulti">
                                <span class="linksy-checkbox" :class="{'checked': isItemSelected(item)}" />
                              </span>
                              <span>{{getItemLabel(item)}}</span>
                            </slot>
                          </li>
                        </ul>
                    </div>

                    <p class="text-muted fs-14px text-center py-2" v-if="items.length < 1">
                        <slot name="empty-text">Nothing Here.</slot>
                    </p>
                </div>

                <!-- loader -->
                <div v-if="isLoading">loading...</div>

                <div v-if="isManual" class="footer">
                    <slot name="footer" :disabled="!hasSelectionChanged" :onSubmit="onSubmit" :onReset="onReset">
                        <div>
                          <div v-if="hasSelectionChanged">
                            <button @click="onSubmit">Submit</button>
                            <button v-if="isResetable" @click="onReset">Reset</button>
                          </div>
                        </div>
                    </slot>
                </div>
            </div>
        </div>
    `,
    beforeMount() {
        this.initialize();
    },
};
