<script src="https://cdn.jsdelivr.net/npm/flexsearch@0.7.21/dist/flexsearch.bundle.js"></script>
<script>
    document.addEventListener('DOMContentLoaded', init, false);
    document.addEventListener('DOMContentLoaded', setCorrectShortcut, false);

    window.toggleSearch = function () {
        if (document.getElementById('globalsearch').classList.contains('active')) {
            document
                .getElementById('globalsearch')
                .classList
                .remove('active');
        } else {
            document
                .getElementById('globalsearch')
                .classList
                .add('active');
            document
                .getElementById('term')
                .focus();
        }
    }

    window.toggleTagSearch = function (evt) {
        console.log(evt.textContent);
        const term = evt.textContent;
        if (term) {
            window
                .document
                .getElementById('term')
                .value = term.trim();
            window.toggleSearch();
            window.search();
        }
    }

    const loadingSvg = `
    <svg width="100" height="100" viewBox="0 0 45 45" xmlns="http://www.w3.org/2000/svg" stroke="#fff">
      <g fill="none" fill-rule="evenodd" transform="translate(1 1)" stroke-width="2">
          <circle cx="22" cy="22" r="6" stroke-opacity="0">
              <animate attributeName="r"
                   begin="1.5s" dur="3s"
                   values="6;22"
                   calcMode="linear"
                   repeatCount="indefinite" />
              <animate attributeName="stroke-opacity"
                   begin="1.5s" dur="3s"
                   values="1;0" calcMode="linear"
                   repeatCount="indefinite" />
              <animate attributeName="stroke-width"
                   begin="1.5s" dur="3s"
                   values="2;0" calcMode="linear"
                   repeatCount="indefinite" />
          </circle>
          <circle cx="22" cy="22" r="6" stroke-opacity="0">
              <animate attributeName="r"
                   begin="3s" dur="3s"
                   values="6;22"
                   calcMode="linear"
                   repeatCount="indefinite" />
              <animate attributeName="stroke-opacity"
                   begin="3s" dur="3s"
                   values="1;0" calcMode="linear"
                   repeatCount="indefinite" />
              <animate attributeName="stroke-width"
                   begin="3s" dur="3s"
                   values="2;0" calcMode="linear"
                   repeatCount="indefinite" />
          </circle>
          <circle cx="22" cy="22" r="8">
              <animate attributeName="r"
                   begin="0s" dur="1.5s"
                   values="6;1;2;3;4;5;6"
                   calcMode="linear"
                   repeatCount="indefinite" />
          </circle>
      </g>
  </svg>`;

    function debounce(func, wait, immediate) {
        var timeout;
        return function () {
            var context = this,
                args = arguments;
            var later = function () {
                timeout = null;
                if (!immediate) 
                    func.apply(context, args);
                };
            var callNow = immediate && !timeout;
            clearTimeout(timeout);
            timeout = setTimeout(later, wait);
            if (callNow) 
                func.apply(context, args);
            };
    };

    function setCorrectShortcut() {
        if (navigator.platform.toUpperCase().indexOf('MAC') >= 0) {
            document
                .querySelectorAll(".search-keys")
                .forEach(x => x.innerHTML = "⌘ + K");
        }
    }

    function createIndex(posts) {
        const encoder = (str) => str
            .toLowerCase()
            .split(/([^a-z]|[^\x00-\x7F])/)
        const contentIndex = new FlexSearch.Document({
            cache: true,
            charset: "latin:extra",
            optimize: true,
            index: [
                {
                    field: "content",
                    tokenize: "reverse",
                    encode: encoder
                }, {
                    field: "title",
                    tokenize: "forward",
                    encode: encoder
                }, {
                    field: "tags",
                    tokenize: "forward",
                    encode: encoder
                }
            ]
        })
        posts.forEach((p, idx) => {
            contentIndex.add({
                id: idx, title: p.title, content: p.content, tags: p.tags //Change to removeHTML
            })
        });
        return contentIndex;
    }

    async function init() {
        //init offline search index

        const searchIndexDate = '{{meta.buildDate|isoDate}}';
        let shouldFetch = true;
        if(localStorage.getItem("searchIndex")) {
            let {date, docs}= JSON.parse(localStorage.getItem('searchIndex'));
            if(date === searchIndexDate){
                shouldFetch = false;
                let index = createIndex(docs);
                window.docs = docs
                window.index = index;
            }        
        }
        if(shouldFetch){
            let docs = await(await fetch('/searchIndex.json?v={{meta.buildDate|isoDate}}')).json();
            let index = createIndex(docs);
            localStorage.setItem("searchIndex", JSON.stringify({date: '{{meta.buildDate|isoDate}}', docs}));
            window.docs = docs
            window.index = index;
        }

        //open searchmodal when ctrl + k is pressed, cmd + k on mac
        document.addEventListener('keydown', (e) => {
            if ((e.ctrlKey || e.metaKey) && e.key === 'k') {
                e.preventDefault();
                toggleSearch();
            }
            if (e.key === 'Escape') {
                document
                    .getElementById('globalsearch')
                    .classList
                    .remove('active');
            }

            //navigate search results with arrow keys
            if (document.getElementById('globalsearch').classList.contains('active')) {
                if (e.key === 'ArrowDown') {
                    e.preventDefault();
                    let active = document.querySelector('.searchresult.active');
                    if (active) {
                        active
                            .classList
                            .remove('active');
                        if (active.nextElementSibling) {
                            active
                                .nextElementSibling
                                .classList
                                .add('active');
                        } else {
                            document
                                .querySelector('.searchresult')
                                .classList
                                .add('active');
                        }
                    } else {
                        document
                            .querySelector('.searchresult')
                            .classList
                            .add('active');
                    }

                    let currentActive = document.querySelector('.searchresult.active');
                    if (currentActive) {
                        currentActive.scrollIntoView({behavior: 'smooth', block: 'nearest', inline: 'start'});
                    }
                }
                if (e.key === 'ArrowUp') {
                    e.preventDefault();
                    let active = document.querySelector('.searchresult.active');
                    if (active) {
                        active
                            .classList
                            .remove('active');
                        if (active.previousElementSibling) {
                            active
                                .previousElementSibling
                                .classList
                                .add('active');
                        } else {
                            document
                                .querySelectorAll('.searchresult')
                                .forEach((el) => {
                                    if (!el.nextElementSibling) {
                                        el
                                            .classList
                                            .add('active');
                                    }
                                });
                        }
                    } else {
                        document
                            .querySelectorAll('.searchresult')
                            .forEach((el) => {
                                if (el.nextElementSibling) {
                                    el
                                        .classList
                                        .add('active');
                                }
                            });
                    }

                    let currentActive = document.querySelector('.searchresult.active');
                    if (currentActive) {
                        currentActive.scrollIntoView({behavior: 'smooth', block: 'nearest', inline: 'start'});
                    }

                }
                if (e.key === 'Enter') {
                    e.preventDefault();
                    let active = document.querySelector('.searchresult.active');
                    if (active) {
                        window.location.href = active
                            .querySelector("a")
                            .href;
                    }
                }
            }
        });

        const debouncedSearch = debounce(search, 200, false);
        field = document.querySelector('#term');
        field.addEventListener('keydown', (e) => {
            if (e.key !== 'ArrowDown' && e.key !== 'ArrowUp') {
                debouncedSearch();
            }
        });
        resultsDiv = document.querySelector('#search-results');

        const params = new URL(location.href).searchParams;
        if (params.get('q')) {
            field.setAttribute('value', params.get('q'));
            toggleSearch();
            search();
        }
    }
    window.lastSearch = '';
    async function search() {
        let search = field
            .value
            .trim();
        if (!search) 
            return;
        if (search == lastSearch) 
            return;
        console.log(`search for ${search}`);
        window.lastSearch = search;

        resultsDiv.innerHTML = loadingSvg;
        //let searchRequest = await fetch(`/api/search?term=${encodeURIComponent(search)}`);
        //let results = await searchRequest.json();
        let results = offlineSearch(search);
        let resultsHTML = '';
        if (!results.length) {
            let resultParagraph = document.createElement("p");
            resultParagraph.innerText = `No results for "${search}"`;
            resultsDiv.innerHTML = '';
            resultsDiv.appendChild(resultParagraph);
            return;
        }
        resultsHTML += '<div style="max-width:100%;">';
        // we need to add title, url from ref
        results.forEach(r => {
            if(r.tags && r.tags.length > 0){
                resultsHTML += `<div class="searchresult">
                    <a class="search-link" href="${r.url}">${r.title}</a>
                    <div onclick="window.location='${r.url}'">
                        <div class="header-meta">
                            <div class="header-tags">
                                ${r.tags.map(tag=>'<a class="tag" href="JavaScript:Void(0);">#'+tag+'</a>').join("")}
                            </div>
                        </div>
                        ${r.content}
                    </div>
                </div>`;
            } else {
                resultsHTML += `<div class="searchresult">
                    <a class="search-link" href="${r.url}">${r.title}</a>
                    <div onclick="window.location='${r.url}'">
                        ${r.content}
                    </div>
                </div>`;
            }
        });
        resultsHTML += '</div>';
        resultsDiv.innerHTML = resultsHTML;
    }

    function truncate(str, size) {
        //first, remove HTML
        str = str.replaceAll(/<[^>]*>/g, '');
        if (str.length < size) 
            return str;
        return str.substring(0, size - 3) + '...';
    }

    function offlineSearch(searchQuery) {
        let data = window.docs;

        let isTagSearch = searchQuery[0] === "#" && searchQuery.length > 1;

        let searchResults = isTagSearch
            ? index.search(searchQuery.substring(1), [
                {
                    field: "tags"
                }
            ])
            : index.search(searchQuery, [
                {
                    field: "title",
                    limit: 5
                }, {
                    field: "content",
                    weight: 10
                }
            ]);

        const getByField = (field) => {
            const results = searchResults.filter((x) => x.field === field)
            if (results.length === 0) {
                return []
            } else {
                return [...results[0].result]
            }
        }
        const allIds = new Set([
            ...getByField("title"),
            ...getByField("content"),
            ...getByField("tags")
        ])
        const dataIds = [...allIds];
        const finalResults = dataIds.map((id) => {
            let result = data[id];
            result.content = truncate(result.content, 400);
            result.tags = result
                .tags
                .filter((x) => x != "gardenEntry" && x != "note"); //Note is automatically added by 11ty. GardenEntry is used internally to mark the home page

            return result;
        })
        return finalResults;

    }
</script>