const STATE_LOADING = 0,
      STATE_READY = 1,
      STATE_VERIFYING = 2,
      STATE_SUCCESS = 3,
      STATE_FAIL = 4;

export default (root, onSuccess) => {
    const find = (s) => root.querySelector(s),
          findAll = (s) => root.querySelectorAll(s),
          icon = (i) => '<b class="icon-item"><b class="badge">' + i + '</b><b class="icon-box"><b class="icon-shape"></b></b></b>';
    
    let captcha_id = null,
        xy_li = [],
        id_counter = 0,
        tip_state = STATE_LOADING,
        current_tips = [],
        img_url = '',
        icons_html = '',
        fail_timer = null;

    for (let i = 1; i <= 3; ++i) icons_html += icon(i);

    root.innerHTML = '<main class="captcha ing">' +
        '<header>' +
            '<b class="header-top">' +
                '<b class="title"></b>' +
                '<button class="refresh" title="刷新"></button>' +
            '</b>' +
            '<b class="icons-row">' + icons_html + '</b>' +
        '</header>' +
        '<b class="click-box"></b>' +
        '</main>';

    const update = () => {
        const main = find('main.captcha'),
              title = find('.title'),
              box = find('.click-box'),
              shapes = findAll('.icon-shape'),
              overlay = (cls) => {
                  box.style.display = 'block';
                  box.innerHTML = '<b class="state-overlay" style="background:white;backdrop-filter:none"><b class="' + cls + '"></b></b>';
              },
              tip = (text, cls) => {
                  title.innerText = text;
                  title.className = 'title' + (cls ? ' ' + cls : '');
              };

        main.classList.toggle('ing', tip_state === STATE_LOADING || tip_state === STATE_VERIFYING);

        if (tip_state === STATE_LOADING) {
            tip('加载中...');
            box.style.display = 'block';
            box.innerHTML = '<b class="state-overlay"><b class="spinner"></b></b>';
            for (let i = 0; i < shapes.length; ++i) {
                const s = shapes[i].style;
                s.webkitMaskImage = '';
                s.maskImage = '';
            }
        } else if (tip_state === STATE_VERIFYING) {
            box.style.display = 'block';
            if (!find('.state-overlay')) {
                box.innerHTML += '<b class="state-overlay"><b class="spinner"></b></b>';
            }
        } else if (tip_state === STATE_SUCCESS) {
            tip('验证成功', 'success-text');
            overlay('success-mark');
        } else if (tip_state === STATE_FAIL) {
            tip('验证失败，1秒后刷新', 'error-text');
            overlay('fail-mark');
            fail_timer = setTimeout(() => {
                load();
            }, 1000);
        } else if (tip_state === STATE_READY) {
            tip('请按序点击下图中的图标');
            box.style.display = 'block';
            box.style.backgroundImage = 'url("' + img_url + '")';
            box.innerHTML = '';
            
            for (let i = 0; i < 3; ++i) {
                if (current_tips[i]) {
                    let svg_str = current_tips[i];
                    if (!svg_str.includes('xmlns=')) {
                        svg_str = svg_str.replace('<svg ', '<svg xmlns="http://www.w3.org/2000/svg" ');
                    }
                    const svg_uri = 'data:image/svg+xml;charset=utf-8,' + encodeURIComponent(svg_str),
                          s = shapes[i].style,
                          url = 'url(\'' + svg_uri + '\')';
                    s.webkitMaskImage = url;
                    s.maskImage = url;
                }
            }
        }
    };

    const reset = () => {
        id_counter = 0;
        xy_li = [];
        if (fail_timer) {
            clearTimeout(fail_timer);
            fail_timer = null;
        }
        const box = find('.click-box');
        if (box) box.innerHTML = '';
    };

    const load = async () => {
        tip_state = STATE_LOADING;
        reset();
        update();

        try {
            const res = await fetch('/api/captcha'),
                  data = await res.json(),
                  bytes = new Uint8Array(data.img),
                  blob = new Blob([bytes], { type: 'image/webp' });
                  
            captcha_id = data.id;
            current_tips = data.tip || [];
            
            if (img_url) URL.revokeObjectURL(img_url);
            img_url = URL.createObjectURL(blob);
            
            tip_state = STATE_READY;
        } catch (e) {
            console.error(e);
        }
        update();
    };

    const verify = async () => {
        tip_state = STATE_VERIFYING;
        update();
        
        try {
            const res = await fetch('/api/verify', {
                method: 'POST',
                headers: { 'Content-Type': 'application/json' },
                body: JSON.stringify({
                    id: captcha_id,
                    clicks: xy_li
                })
            }),
                  valid = await res.json();
            
            if (valid) {
                tip_state = STATE_SUCCESS;
                update();
                if (onSuccess) setTimeout(onSuccess, 1000);
            } else {
                tip_state = STATE_FAIL;
                update();
            }
        } catch (e) {
            console.error(e);
            tip_state = STATE_FAIL;
            update();
        }
    };

    const click = (e) => {
        if (tip_state !== STATE_READY) return;
        
        const { offsetX: x, offsetY: y } = e;
        
        xy_li.push(x, y);
        ++id_counter;

        const box = find('.click-box'),
              badge = document.createElement('b');
              
        badge.className = 'badge click-badge';
        badge.innerText = id_counter;
        badge.style.left = (x - 12) + 'px';
        badge.style.top = (y - 12) + 'px';
        badge.dataset.id = id_counter;

        badge.onclick = (ev) => {
            ev.stopPropagation();
            const clicked_id = parseInt(badge.dataset.id),
                  badges = box.querySelectorAll('.click-badge');
            id_counter = clicked_id - 1;
            xy_li.splice(id_counter * 2);
            for (let i = 0; i < badges.length; ++i) {
                const b = badges[i];
                if (parseInt(b.dataset.id) >= clicked_id) b.remove();
            }
        };

        box.appendChild(badge);

        if (id_counter === 3) {
            verify();
        }
    };

    find('.refresh').addEventListener('click', load);
    find('.click-box').addEventListener('click', click);

    load();
};
