import '../../../css/config.css';
import {rendy} from 'rendy';
import currify from 'currify';
import wraptile from 'wraptile';
import squad from 'squad';
import {promisify} from 'es6-promisify';
import {tryToCatch} from 'try-to-catch';
import load from 'load.js';
import createElement from '@cloudcmd/create-element';
import * as Events from '#dom/events';
import * as Files from '#dom/files';
import {getTitle} from '#common/cloudfunc';
import * as Images from '#dom/images';
import * as input from './input.js';

const {CloudCmd, DOM} = globalThis;

const {Dialog, setTitle} = DOM;

const Name = 'Config';

const loadSocket = promisify(DOM.loadSocket);

const showLoad = () => {
    Images.show.load('top');
};

const addKey = currify((fn, input) => {
    Events.addKey(input, fn);
    return input;
});

const addChange = currify((fn, input) => {
    Events.add('change', input, fn);
    return input;
});

const Config = {};

let Template;

const loadCSS = load.css;

export async function init() {
    if (!CloudCmd.config('configDialog'))
        return;
    
    showLoad();
    
    const {DIR_DIST} = CloudCmd;
    
    [Template] = await Promise.all([
        Files.get('config-tmpl'),
        loadSocket(),
        loadCSS(`${DIR_DIST}/config.css`),
        CloudCmd.View(),
    ]);
    
    initSocket();
}

const {config, Key} = CloudCmd;

let Element;

function getHost() {
    const {
        host,
        origin,
        protocol,
    } = location;
    
    return origin || `${protocol}//${host}`;
}

function initSocket() {
    const href = getHost();
    const {prefixSocket, prefix} = CloudCmd;
    
    const ONE_MINUTE = 60 * 1000;
    
    const socket = globalThis.io.connect(href + prefixSocket + '/config', {
        reconnectionAttempts: Infinity,
        reconnectionDelay: ONE_MINUTE,
        path: `${prefix}/socket.io`,
    });
    
    const save = (data) => {
        onSave(data);
        socket.send(data);
    };
    
    authCheck(socket);
    
    socket.on('connect', () => {
        Config.save = save;
    });
    
    socket.on('message', onSave);
    socket.on('log', CloudCmd.log);
    
    socket.on('disconnect', () => {
        Config.save = saveHttp;
    });
    
    socket.on('err', Dialog.alert);
}

function authCheck(socket) {
    socket.emit('auth', config('username'), config('password'));
    socket.on('reject', wraptile(Dialog.alert, 'Wrong credentials!'));
}

Config.save = saveHttp;

export async function show() {
    if (!CloudCmd.config('configDialog'))
        return;
    
    await fillTemplate();
}

async function fillTemplate() {
    const [error, config] = await tryToCatch(Files.get, 'config');
    
    if (error)
        return Dialog.alert('Could not load config!');
    
    const {
        editor,
        menu,
        packer,
        columns,
        theme,
        configAuth,
        configPort,
        ...obj
    } = input.convert(config);
    
    obj[`${menu}-selected`] = 'selected';
    obj[`${editor}-selected`] = 'selected';
    obj[`${packer}-selected`] = 'selected';
    obj[`${columns}-selected`] = 'selected';
    obj[`${theme}-selected`] = 'selected';
    obj.configAuth = configAuth ? '' : 'hidden';
    obj.configPort = configPort ? '' : 'hidden';
    
    const innerHTML = rendy(Template, obj);
    
    Element = createElement('form', {
        className: 'config',
        innerHTML,
    });
    
    const inputs = document.querySelectorAll('input, select', Element);
    const [inputFirst] = inputs;
    
    let afterShow;
    
    if (inputFirst) {
        onAuthChange(inputFirst.checked);
        afterShow = inputFirst.focus.bind(inputFirst);
    }
    
    const getTarget = ({target}) => target;
    const handleChange = squad(onChange, getTarget);
    
    Array
        .from(inputs)
        .map(addKey(onKey))
        .map(addChange(handleChange));
    
    const autoSize = true;
    
    CloudCmd.View.show(Element, {
        autoSize,
        afterShow,
    });
}

export function hide() {
    CloudCmd.View.hide();
}

async function onChange(el) {
    const obj = {};
    const name = input.getName(el);
    const data = input.getValue(name, Element);
    
    if (name === 'name')
        onNameChange(data);
    else if (name === 'auth')
        onAuthChange(data);
    
    obj[name] = data;
    
    await Config.save(obj);
}

function onSave(obj) {
    for (const name of Object.keys(obj)) {
        const data = obj[name];
        
        CloudCmd._config(name, data);
        input.setValue(name, data, Element);
    }
}

async function saveHttp(obj) {
    const {RESTful} = DOM;
    const [e] = await RESTful.Config.write(obj);
    
    if (e)
        return;
    
    onSave(obj);
}

function onAuthChange(checked) {
    const elUsername = input.getElementByName('username', Element);
    const elPassword = input.getElementByName('password', Element);
    
    elUsername.disabled = !checked;
    elPassword.disabled = !checked;
}

function onNameChange(name) {
    setTitle(getTitle({
        name,
    }));
}

async function onKey({keyCode, target}) {
    switch(keyCode) {
    case Key.ESC:
        return hide();
    
    case Key.ENTER:
        return await onChange(target);
    }
}

CloudCmd[Name] = {
    init,
    show,
    hide,
};
