import { Controller } from "@hotwired/stimulus";
import Mousetrap from "mousetrap";
import "@tailwindplus/elements";

export default class extends Controller {
    static targets = ["dialog", "input", "list", "previewPane", "palette", "defaults", "empty", "sentinel", "emptyPreview"];
    static values = {
        searchUrl: String
    };

    connect() {
        this.lastQuery = this.inputTarget?.value || '';
        this.aborter = null;
        this.page = 1;
        this.loading = false;
        this.hasMore = true;

        Mousetrap.bind(['mod+k'], (e) => {
            e.preventDefault();
            this.open();
        });

        const setupLibrary = () => {
            this.paletteTarget.setFilterCallback(({ query, node, content }) => {
                // keep defaults visible only when empty, otherwise show fetched results
                // (this callback must return boolean per node; simplest is "always true")
                return true;
            });
        };

        if (customElements.get('el-command-palette')) {
            setupLibrary();
        } else {
            window.addEventListener('elements:ready', setupLibrary);
        }

        if (this.hasSentinelTarget) {
            this.observer = new IntersectionObserver(async (entries) => {
                const entry = entries[0];
                if (entry.isIntersecting && !this.loading && this.hasMore && this.lastQuery.length >= 2) {
                    await this.fetchNextPage();
                }
            }, {
                root: this.listTarget.parentElement,
                threshold: 0.1
            });
            this.observer.observe(this.sentinelTarget);
        }
    }

    disconnect() {
        if (this.observer) {
            this.observer.disconnect();
        }
        Mousetrap.unbind(['mod+k']);
    }

    open() {
        if (this.hasDialogTarget) {
            this.dialogTarget.showModal();
        }
    }

    async fetchNextPage() {
        if (this.loading || !this.hasMore) return;
        this.page++;
        await this.performSearch(true);
    }

    debounce(fn, ms = 200) {
        let t;
        return (...args) => {
            clearTimeout(t);
            t = setTimeout(() => fn.apply(this, args), ms);
        };
    }

    onSelectionChange() {
        if (this.hasEmptyPreviewTarget) {
            this.emptyPreviewTarget.classList.add('hidden');
            this.emptyPreviewTarget.classList.remove('sm:flex');
        }
    }

    onSelect(event) {
        const item = event.detail?.relatedTarget || event.target.closest('a');
        if (item && item.href && !item.href.endsWith('#')) {
            window.location.href = item.href;
        }
    }

    onInput = this.debounce(async () => {
        const query = this.inputTarget.value;

        if (this.lastQuery === query) return;

        if (query.startsWith('@')) {
            this.paletteTarget.dataset.mode = 'user';
        } else if (query.startsWith('#')) {
            this.paletteTarget.dataset.mode = 'participant';
        } else if (query.startsWith('>')) {
            this.paletteTarget.dataset.mode = 'offer';
        } else if (query.startsWith('€')) {
            this.paletteTarget.dataset.mode = 'payment';
        } else {
            delete this.paletteTarget.dataset.mode;
        }

        if (this.hasEmptyPreviewTarget) {
            this.emptyPreviewTarget.classList.remove('hidden');
            this.emptyPreviewTarget.classList.add('sm:flex');
        }

        this.lastQuery = query;
        this.page = 1;
        this.hasMore = true;

        if (query.length < 2) {
            this.listTarget.innerHTML = '';
            this.previewPaneTarget.querySelectorAll('el-command-preview[for]:not([for=""])').forEach(el => el.remove());
            if (this.hasDefaultsTarget) this.defaultsTarget.hidden = query !== '';
            if (this.hasEmptyTarget) this.emptyTarget.hidden = true;
            return;
        }

        if (this.hasDefaultsTarget) this.defaultsTarget.hidden = true;

        await this.performSearch(false);
    }, 200);

    async performSearch(append = false) {
        const query = this.lastQuery;
        if (append && this.loading) return;
        
        if (!append && this.aborter) {
            this.aborter.abort();
        }

        this.loading = true;

        if (!append) {
            this.aborter = new AbortController();
        }

        try {
            const url = new URL(this.searchUrlValue, window.location.origin);
            url.searchParams.set('q', query);
            url.searchParams.set('page', this.page);

            const response = await fetch(url, {
                signal: this.aborter ? this.aborter.signal : null
            });
            const html = await response.text();

            if (this.inputTarget.value !== query) return;

            const parser = new DOMParser();
            const doc = parser.parseFromString(html, 'text/html');
            const newResults = doc.querySelector('[data-results-list]')?.innerHTML || '';
            const newPreviews = doc.querySelector('[data-previews-list]')?.innerHTML || '';

            if (append) {
                this.listTarget.insertAdjacentHTML('beforeend', newResults);
                this.previewPaneTarget.insertAdjacentHTML('beforeend', newPreviews);
            } else {
                this.listTarget.innerHTML = newResults;
                this.previewPaneTarget.querySelectorAll('el-command-preview[for]:not([for=""])').forEach(el => el.remove());
                this.previewPaneTarget.insertAdjacentHTML('beforeend', newPreviews);
            }
            
            this.hasMore = newResults.trim() !== '';
            
            if (this.hasEmptyTarget && !append) {
                this.emptyTarget.hidden = newResults.trim() !== '';
            }
        } catch (e) {
            if (e.name !== 'AbortError') {
                console.error(e);
            }
        } finally {
            this.loading = false;
        }
    }
}
