// Doghole Comment
(function() {
    let 
    Q = x => document.querySelector(x),
    A = x => document.querySelectorAll(x),
    E = x => document.createElement(x),
    C = {
        get: (x) => Doghole.Cookies.get(x),
        set: (x, y) => Doghole.Cookies.set(x, y),
        del: (x) => Doghole.Cookies.del(x)
    },
    commentDiv = Q('#respond');
    if (!commentDiv) return;
    let
    enabled = commentDiv.getAttribute('comment-enabled') == 'true',
    formSelector = '.new-comment-form',
    form = commentDiv.querySelector(formSelector),
    textarea = form.querySelector('textarea'),
    setCommentError = error => {
        let elem = Q('.comment-error')
        elem.textContent = error
        Doghole.Animate.slideDown(elem, 200, () => {
            setTimeout(() => {
                Doghole.Animate.slideUp(elem, 200)
            }, 2600)
        })
    },
    // As its name, will be called after press ESC while editing or before comment page changed.
    foldCommentTextarea = () => {
        form.classList.remove('active')
        C.set('comment_content', textarea.value, 30)
        textarea.value = ''
        textarea.setAttribute('placeholder', '')
        textarea.blur()
        let subCommentBox = Q('.comment-list .respond')
        if (subCommentBox) {
            subCommentBox.querySelector('[name="replyTo"]').value = ''
            subCommentBox.querySelector('[name="id"]').value = ''
            Q('comment').appendChild(subCommentBox)
        }
        A('.editing').forEach(editing => {
            editing.querySelectorAll('.edit').forEach(edit => edit.textContent = 'edit')
            editing.classList.remove('editing')
        })
        form.querySelector('[name="preview"]').checked = !1
        let preview = form.querySelector('.comment-preview')
        preview.textContent = ''
        preview.style.display = 'none'
        textarea.parentElement.style.display = 'unset'
        if (!enabled) commentDiv.style.display = 'none'
    },
    saveCommentHistoryState = () => {
        let commentPageElem = Q('.comment-page');

        window.history.replaceState({
            status: 0,
            commentContent: commentPageElem.innerHTML
        }, '', location.href);
    },
    hashChangedHandler = () => {
        let hash = location.hash.slice(1);
        if (hash.indexOf('comment-') == 0) {
            A(`[id^=li-].active`).forEach(li => li.classList.remove('active'));
            let activeComment = Q(`#li-${hash}`);
            if (activeComment)
                activeComment.classList.add('active')
        }
    },
    verifyFields = {
        author: {
            name: 'Author',
            required: 1
        },
        url: {
            name: 'Url',
            reg: /^(https?:\/\/)?([a-zA-Z0-9]([a-zA-Z0-9\-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,6}(\/.*?)?$/
        },
        email: {
            name: 'Email',
            required: 1,
            reg: /^\w+([-+.]\w+)*@\w+([-.]\w+)*\.\w+([-.]\w+)*$/
        },
        content: {
            name: 'Comment content',
            required: 1
        }
    }
    
    form.querySelector('button').on('mouseenter', (e) => {
        form.setAttribute('action', form.getAttribute('u'))
        form.removeAttribute('u')
        e.target.setAttribute('type', 'submit')
        e.target.off('mouseenter')
    })
    
    form.disable = () => {
        form.querySelectorAll('textarea,input,buttom').forEach(elem => {
            elem.disabled = !0
        })
    }
    form.enable = () => {
        form.querySelectorAll('textarea,input,buttom').forEach(elem => {
            elem.disabled = !1
        })
    }
    form.set = comment => {
        let
        cbIsMarkdown = form.querySelector('[name="isMarkdown"]'),
        cbSubscribeReply = form.querySelector('[name="subscribeReply"]'),
        txtAuthor = form.querySelector('[name="author"]'),
        txtEmail = form.querySelector('[name="email"]'),
        txtUrl = form.querySelector('[name="url"]'),
        txtId = form.querySelector('[name="id"]')
        
        if (cbIsMarkdown) cbIsMarkdown.checked = cbIsMarkdown.value = comment.isMarkdown
        if (cbSubscribeReply) cbSubscribeReply.checked = cbSubscribeReply.value = comment.subscribeReply
        txtAuthor.value = comment.author
        txtEmail.value = comment.email
        txtUrl.value = comment.url
        textarea.value = comment.content
        txtId.value = comment.id
    }

    window.addEventListener('popstate', e => {
        if (e.state && e.state.commentContent) {
            let commentPageElem = Q('.comment-page');
            commentPageElem.innerHTML = e.state.commentContent
        }
    });

    document.body.on('click', ['comment,.comment-list', ' .new-comment'], e => {

        let
        placeholder = 'Press Esc to fold comment input box.'
        form.classList.add('active')
        
        if (textarea.getAttribute('support-markdown') == 'true') {
            placeholder += '\r\nComment now supports Markdown.'
        }
        textarea.setAttribute('placeholder', placeholder);

        form.querySelectorAll('textarea, input[type="text"]').forEach(elem => {
            elem.value = elem.value || C.get(`comment_${elem.getAttribute('name')}`) || ''
        })
        form.querySelectorAll('input[type="checkbox"]:not(.no-cookie)').forEach(elem => {
            let cv = C.get(`comment_${elem.getAttribute('name')}`)
            elem['checked'] = cv !== 'false'
        })
        let prevHeight = textarea.getAttribute('prev-height')
        if (prevHeight) {
            textarea.style.height = prevHeight
            textarea.setAttribute('prev-height', '')
        }
        return 0
    })

    document.body.on('click', '.comment-page-num', e => {
        e.preventDefault()

        let
        commentPageElem = Q('.comment-page'),
        page = e.target,
        changedUrl = page.getAttribute('href'),
        url = changedUrl.replace(/(\/[^\/]*?)$/, '/comments$1')

        saveCommentHistoryState()
        foldCommentTextarea()

        fetch(url).then(resp => {
            if (!resp.ok)
                throw new Error('Load comment page error')
            return [resp.text(), resp.headers]
        }).then(async data => {
            commentPageElem.innerHTML = await data[0];
            let
            postId = data[1].get('Post-Id'),
            scrollTo = Q(`[post-id='${postId}'] [id^=li-comment-]`).offsetTop - 80;
            if (scrollTo < 0) {
                scrollTo = 0
            }
            Doghole.LazyJS.mermaid()
            if (window.Prism)
                Prism.highlightAll()
            else
                Doghole.LazyJS.prism();

            saveCommentHistoryState();

            Doghole.Animate.scrollTo(scrollTo, 800)
        }).catch(error => console.error(error.message));
        window.history.pushState({
            status: 0
        }, '', changedUrl);
        return 0
    });

    document.body.on('keyup', ['comment, .comment-list', ' .new-comment textarea'], e => {
        let currHeight = textarea.offsetHeight;
        if (e.which !== 27 && currHeight < textarea.scrollHeight && currHeight < 350) {
            textarea.style.height = textarea.scrollHeight + 'px'
        }
        if (e.which === 27) {
            textarea.setAttribute('prev-height', textarea.style.height)
            textarea.style.height = '55px'
            foldCommentTextarea();
        }
    });

    document.body.on('input propertychange', [`${formSelector}`, ' textarea, input[type="text"], input[type="checkbox"]:not(.no-cookie)'], e => {
        // save comment params to cookie
        let t = e.target;
        C.set('comment_' + t.getAttribute('name'), t.getAttribute('type') === 'checkbox' ? t.checked : t.value, 30);
    })

    document.body.on('click', 'a.reply', e => {
        e.preventDefault();
        A('.editing').forEach(editing => editing.classList.remove('editing'))
        A('.edit').forEach(edit => edit.textContent = 'edit')
        // Copy comment div to assigned div
        Doghole.Elem.parents(e.target, '.author').forEach(author => {
            let next = Doghole.Elem.next(author)
            if (next) next.after(commentDiv)
        })
        commentDiv.querySelector('[name=replyTo]').value = e.target.getAttribute('to')
        textarea.click()
        textarea.focus()
        commentDiv.style.display = 'block'
        return !1;
    })

    document.body.on('click', 'a.edit', e => {
        e.preventDefault();
        let comment = Doghole.Elem.closest(e.target, '.comment')
        A('.edit').forEach(edit => edit.textContent = 'edit')
        if (comment) {
            if (comment.classList.contains('editing')) {
                foldCommentTextarea()
                return
            }
            comment.classList.add('editing')
        }

        e.target.textContent = 'editing...'
        // Copy comment div to assigned div
        form.disable()
        
        let Elem = Doghole.Elem;
        Elem.parents(e.target, '.author').forEach(author => {
            let next = Elem.next(author);
            if (next)
                next.after(commentDiv);
        })
        commentDiv.querySelector('[name=replyTo]').value = e.target.getAttribute('for');
        textarea.click();
        textarea.focus();
        textarea.disabled = !0
        textarea.value = '载入中...'
        commentDiv.style.display = 'block'

        fetch(`/api/v2/comment/getContentSource/${e.target.getAttribute('of')}`, {
            method: 'GET'
        }).then(async resp => {
            if (!resp.ok) {
                let json = await resp.json();
                throw new Error(typeof json.data === 'string' && json.data || '获取评论原文失败');
            }
            return resp.json()
        }).then(json => {
            form.set(json.data)
            if (textarea.offsetHeight < textarea.scrollHeight && textarea.offsetHeight < 350) {
                textarea.style.height = textarea.scrollHeight + 'px'
            }
            form.enable()
            textarea.click()
            textarea.focus()
        }).catch(error => {
            setCommentError(e.message)
        })
        return !1;
    })

    document.body.on('input propertychange', ['.comment-setting ', '[name="isMarkdown"],[name="preview"]'], e => {
        form.disable()
        let preview = form.querySelector('.comment-preview'),
        textareaWrapper = textarea.parentElement,
        isMarkdownChecked = form.querySelector('[name="isMarkdown"]').checked
        isPreviewChecked = form.querySelector('[name="preview"]').checked
        if (isPreviewChecked) {
            
            if (!textarea.value) {
                setCommentError('评论为空时不允许预览')
                form.querySelector('[name="preview"]').checked = !1
                form.enable()
                textarea.click()
                textarea.focus()
                return
            }
            
            if (!isMarkdownChecked) {
                preview.textContent = textarea.value
                preview.style.display = 'block'
                preview.style.height = textarea.clientHeight + 'px'
                textareaWrapper.style.display = 'none'
                form.enable()
            } else {
                fetch('/api/v2/comment/preview', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json'
                    },
                    body: JSON.stringify({
                        content: textarea.value
                    })
                }).then(async resp => {
                    if (!resp.ok) {
                        let json = await resp.json();
                        throw new Error(typeof json.data === 'string' && json.data || '预览失败');
                    }
                    return resp.json()
                }).then(json => {
                    preview.innerHTML = json.data
                    preview.style.height = textarea.clientHeight + 'px'
                    textareaWrapper.style.display = 'none'
                    preview.style.display = 'block'
                }).catch(error => {
                    setCommentError(error)
                }).finally(() => {
                    form.enable()
                })
            }
        } else {
            textareaWrapper.style.display = 'unset'
            preview.style.display = 'none'
            form.enable()
            textarea.click()
            textarea.focus()
        }

    })

    document.body.on('submit', formSelector, e => {
        e.preventDefault()
        let
        refreshSubmit = e => {
            form.querySelector('button.comment-submit-button').classList[e ? 'remove' : 'add']('btn-loading')
            form[e?'enable':'disable']()
        },
        fetchUrl = form.getAttribute('action'),
        formData = new FormData()

        refreshSubmit(false)

        for (let i = 0; i < form.elements.length; i++) {
            let
            elem = form.elements[i],
            verify = verifyFields[elem.name]

            if (verify) {
                let err = 0;
                if (verify.required && !elem.value) {
                    err = verify.name + ' cannot be empty'
                } else if (elem.value && verify.reg && !verify.reg.test(elem.value)) {
                    err = verify.name + ' is invalid'
                }
                if (err) {
                    setCommentError(err);
                    elem.focus();
                    refreshSubmit(true);
                    return false;
                }
            }

            if (elem.type != 'checkbox' && !elem.value)
                continue;
            if (elem.type == 'checkbox')
                formData.append(elem.name, elem.checked);
            else
                formData.append(elem.name, elem.value);

        }

        fetch(fetchUrl, {
            method: 'POST',
            body: formData
        }).then(async resp => {
            if (!resp.ok) {
                let json = await resp.json();
                throw new Error(typeof json.data === 'string' && json.data || '评论失败');
            }
            return resp.json()
        }).then(json => {
            data = json.data;
            foldCommentTextarea()
            if (data) {
                
                let
                comment = Q('#li-comment-' + data.id),
                authorTemplate = data => 
                `<div class="author">
                    <img referrerpolicy="same-origin" class="avatar" alt="${data.author}" src="${data.avatarUrl}">
                    <div class="author-name">
                        <b>
                        ${data.url ? `<a href="${data.url}" rel="external nofollow">${data.author}</a>` : `${data.author}`}
                        </b>
                        <a rel="nofollow" class="reply" to="${data.id}">reply</a>
                        <a rel="nofollow" class="edit" of="${data.id}">edit</a>
                    </div>
                    <div class="author-date"><small>${data.createTime}</small></div>
                </div>`
                
                if (comment) {
                    // Edited comment
                    comment.querySelector('.author').outerHTML = authorTemplate(data)
                    let content = comment.querySelector('.comment-content')
                    content[data.isMarkdown ? 'innerHTML' : 'textContent'] = data.content
                    
                    if (data.commentStatus != 'RELEASED') {
                        if (!content.querySelector('.comment-error')) {
                            let span = E('span');
                            span.classList.add('comment-error')
                            span.textContent = '您的评论待审核可见'
                            span.append(E('br'))
                            content.prepend(span)
                        }
                    }
                    else {
                        let span = content.querySelector('.comment-error')
                        if (span) span.remove()
                    }
                }
                else {
                    // A new comment
                    let createComment = (data) => {
                        let
                        template = (data)=>`
                            ${authorTemplate(data)}
                            <div class="comment-content">
                            </div>`,
                        li = E('li');
                        li.setAttribute('id', `li-comment-${data.id}`);
                        li.classList.add('comment');
                        li.innerHTML = template(data);
                        let content = li.querySelector('.comment-content');
                        content[data.isMarkdown ? 'innerHTML' : 'textContent'] = data.content
                        if (data.commentStatus !== 'RELEASED') {
                            let span = E('span');
                            span.classList.add('comment-error')
                            span.textContent = '您的评论待审核可见'
                            span.append(E('br'))
                            content.prepend(span)
                        }
                        return li;
                    }
                    comment = createComment(data);
                    
                    
                    if (data.replyTo == -1) {
                        // append to the top-level comment list
                        let olFirst = Q('ol.comment-list');
                        olFirst.appendChild(comment);
                    } else {
                        // append to the end of the target sub-level comment list
                        let replyTarget = Q('#li-comment-' + data.replyTo);
                        if (!replyTarget)
                            location.reaload();
    
                        let wrapper = E('ol');
                        wrapper.classList.add('comment-list');
                        wrapper.appendChild(comment);
                        comment = wrapper;
    
                        let appendTo = replyTarget.querySelector('.comment-children');
                        if (!appendTo) {
                            appendTo = replyTarget;
                            wrapper = E('div');
                            wrapper.classList.add('comment-children');
                            wrapper.appendChild(comment);
                            comment = wrapper;
                        }
                        appendTo.appendChild(comment);
                    }
                }
                

                let scrollTo = Q('#li-comment-' + data.id).offsetTop - 80;

                if (scrollTo < 0) {
                    scrollTo = 0
                }

                if (data.isMarkdown) {
                    Doghole.LazyJS.mermaid();

                    // Render code highlight
                    if (comment.querySelector('code[class*="language-"],pre[class*="language-"]')) {
                        if (window.Prism)
                            window.Prism.highlightAll();
                        else {
                            Doghole.LazyJS.prism();
                        }
                    }

                    // Render katex
                    let katexes = comment.querySelectorAll('.katex');
                    if (data.isMarkdown && katexes.length) {
                        let renderKatex = () => {
                            katexes.forEach(v => katex.render(v.textContent, v))
                        }
                        if (window.katex) {
                            renderKatex();
                        } else {
                            Doghole.LazyJS.katex(renderKatex);
                        }
                    }

                }
                Doghole.Animate.scrollTo(scrollTo, 800)
            } else {
                location.reload();
            }
            C.del('comment_content');
            A('li[id^="li-comment-"].comment.active').forEach(c => {
                c.classList.remove('active')
            })
        })
        .catch(e => {
            setCommentError(e.message)
        })
        .finally(() => {
            textarea.blur();
            refreshSubmit(true);
            textarea = Q(`${formSelector} textarea`);
        })
        return 0

    })
    window.addEventListener('hashchange', hashChangedHandler)
    hashChangedHandler()
}).call(this);
