// 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)
    },
    getCommentCookie = (el) => {
        let name = el.getAttribute('name'),
        v = C.get(`comment_${name}`)
        
        if (v && (name == 'email' || name == 'url')) {
            return v.replaceAll('"', '')
        }
        return v
    },
    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')
        }
    }
    window.addEventListener('hashchange', hashChangedHandler)
    hashChangedHandler()
    
    // Detail Paginator
    window.addEventListener('popstate', e => {
        if (e.state && e.state.commentContent) {
            let commentPageElem = Q('.comment-page');
            commentPageElem.innerHTML = e.state.commentContent
        }
    });
    
    
    function Comment(area) {
        // 一个 Comment 在对应的 .comment-area[post-id] 内运行
        let 
        postId = area.getAttribute('post-id'),
        aSel = `.comment-area[post-id="${postId}"]`,
        commentDiv = Q(`${aSel} #respond`)
        if (!commentDiv) return;
        let
        enabled = commentDiv.getAttribute('comment-enabled') == 'true',
        formSelector = `${aSel} .new-comment-form`,
        form = Q(formSelector),
        textarea = form.querySelector('textarea'),
        
        // common funcs
        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()
            
            commentDiv.querySelector('[name="replyTo"]').value = ''
            commentDiv.querySelector('[name="id"]').value = ''
            area.querySelector('comment').appendChild(commentDiv)
            
            A('.editing').forEach(editing => {
                editing.querySelectorAll('.edit').forEach(edit => edit.textContent = 'edit')
                editing.classList.remove('editing')
            })
            
            let previewCbx = form.querySelector('[name="preview"]')
            if (previewCbx) previewCbx.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);
        },
        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="markdown"]'),
            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.markdown
            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
        }
    
        document.body.on('click', [aSel, ' .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 || getCommentCookie(elem) || ''
            })
            form.querySelectorAll('input[type="checkbox"]:not(.no-cookie)').forEach(elem => {
                elem['checked'] = getCommentCookie(elem) !== 'false'
            })
            let prevHeight = textarea.getAttribute('prev-height')
            if (prevHeight) {
                textarea.style.height = prevHeight
                textarea.setAttribute('prev-height', '')
            }
            return 0
        })
        
        // Detail Paginator
        document.body.on('click', `${aSel} .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', [`${formSelector}`, ' .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', `${aSel} 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.tweet-make-comment[post-id="${postId}"]`, e => {
            e.preventDefault();
            A('.editing').forEach(editing => editing.classList.remove('editing'))
            A('.edit').forEach(edit => edit.textContent = 'edit')
            // Copy comment div to assigned div
            area.insertBefore(commentDiv, area.firstChild)
            //area.insertAdjacentElement('beforebegin', commentDiv)
            textarea.click()
            textarea.focus()
            commentDiv.style.display = 'block'
            return !1;
        })
    
        document.body.on('click', `${aSel} 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', [aSel, ' .comment-setting ', '[name="markdown"],[name="preview"]'], e => {
            form.disable()
            let preview = form.querySelector('.comment-preview'),
            textareaWrapper = textarea.parentElement,
            markdownCbx = form.querySelector('[name="markdown"]'),
            isMarkdownChecked = markdownCbx && markdownCbx.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 => {
                        let template = data =>
                        `
                        <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>
                            ${enabled ? `<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 = E('div')
                        div.innerHTML = template(data)
                        div.classList.add('author')
                        let b = div.querySelector('.author-name>b')
                        if (data.url) {
                            let a = E('a')
                            a.setAttribute('href', data.url)
                            a.setAttribute('rel', 'external nofollow')
                            a.textContent = data.author
                            b.innerHTML = a.outerHTML
                        }
                        else {
                            b.textContent = data.author
                        }
                        return div.outerHTML
                    }
                    
                    if (comment) {
                        // Edited comment
                        comment.querySelector('.author').outerHTML = authorTemplate(data)
                        let content = comment.querySelector('.comment-content')
                        content[data.markdown ? '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.markdown ? '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 = area.querySelector('ol.comment-list');
                            olFirst.appendChild(comment);
                            
                            if (area.querySelector('.tweet-list-comment') && !olFirst.classList.contains('has-comment')) {
                                olFirst.classList.add('has-comment');
                            }
                        } else {
                            // append to the end of the target sub-level comment list
                            let replyTarget = area.querySelector('#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.markdown) {
                        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.markdown && 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)
                console.error(e)
            })
            .finally(() => {
                textarea.blur();
                refreshSubmit(true);
                textarea = Q(`${formSelector} textarea`);
            })
            return 0
        })
    }
    
    Comment.getCommentObj = function (postId) {
        let 
        aSel = `.comment-area[post-id="${postId}"]`,
        area = Q(aSel)
        if (!area) {
            console.warn(`Cannot find ${aSel}`)
            return null
        }
        return area.commentObj
    }
    
    let allAreas = A('.comment-area[post-id]');
    allAreas.forEach(area => {
        area.commentObj = Comment(area)
    })
    
    window.Doghole.Comment = Comment;
}).call(this);
