
import { createApp } from './vue.esm-browser.js';

createApp({
    components: { Cluster, Host },
    data() {
        return {
            forms: {
                store: {},
                storeUpload: {},
                delKey: {},
                hostFromTemplate: {},
                hostFromTemplateDel: "",
            },
            view: "",
            viewFilter: "",
            session: {},
            error: null,
            publicState: null,
            serverVersion: null,
            uiHash: null,
            watchingState: false,
            state: null,
            toasts: [],
            toastId: 0,
        }
    },
    mounted() {
        this.session = JSON.parse(sessionStorage.state || "{}")
        this.watchPublicState()
        document.addEventListener('keydown', (e) => this.onKeydown(e))
    },
    unmounted() {
        document.removeEventListener('keydown', (e) => this.onKeydown(e))
    },
    watch: {
        session: {
            deep: true,
            handler(v) {
                sessionStorage.state = JSON.stringify(v)

                if (v.token && !this.watchingState) {
                    this.watchState()
                    this.watchingState = true
                }
            }
        },
        publicState: {
            deep: true,
            handler(v) {
                if (v) {
                    this.serverVersion = v.ServerVersion
                    if (this.uiHash && v.UIHash != this.uiHash) {
                        console.log("reloading")
                        location.reload()
                    } else {
                        this.uiHash = v.UIHash
                    }
                }
            },
        }
    },

    computed: {
        adminViews() {
            return [{type: "actions", name: "admin", title: "Admin actions"}];
        },
        clusterViews() {
            return (this.state.Clusters||[]).map((c) => ({type: "cluster", name: c.Name, title: c.Name}));
        },
        hostViews() {
            return (this.state.Hosts||[])
              .filter((h) => h.Name.includes(this.viewFilter))
              .map((h) => ({type: "host", name: h.Name, title: h.Name}));
        },
        viewObj() {
            if (this.view) {
                if (this.view.type == "cluster") {
                    return this.state.Clusters.find((c) => c.Name == this.view.name);
                }
                if (this.view.type == "host") {
                    return this.state.Hosts.find((h) => h.Name == this.view.name);
                }
            }
            return undefined;
        },
        hostsFromTemplate() {
            return (this.state.Hosts||[]).filter((h) => h.Template);
        },
    },

    methods: {
        any(array) {
            return array && array.length != 0;
        },
        isActive(v) {
            return this.view && this.view.type == v.type && this.view.name == v.name
        },
        onKeydown(e) {
            if ((e.ctrlKey || e.metaKey) && e.key == 'k') {
                e.preventDefault()
                this.$nextTick(() => {
                    const el = document.querySelector('.sidebar input[type="search"]')
                    if (el) el.focus()
                })
                return
            }

            if (e.key == 'Escape') {
                this.viewFilter = ''
                return
            }
        },
        copyText(text) {
            event.preventDefault()
            try {
                window.navigator.clipboard.writeText(text)
                this.toast('copied!', 'info')
            } catch (e) {
                this.toast('copy failed: ' + e, 'error')
            }
        },
        setToken() {
            event.preventDefault()
            this.session.token = this.forms.setToken
            this.forms.setToken = null
        },
        uploadStore() {
            event.preventDefault()
            this.apiPost('/public/store.tar', this.$refs.storeUpload.files[0], (v) => {
                this.forms.store = {}
            }, "application/tar")
        },
        namedPassphrase(name, passphrase) {
            return {Name: this.forms.store.name, Passphrase: btoa(this.forms.store.pass1)}
        },
        storeAddKey() {
            const params = this.namedPassphrase();
            if (this.forms.store.byHash) {
                params.Hash = this.forms.store.hash;
            }

            this.apiPost('/store/add-key', params, (v) => {
                this.forms.store = {}
            })
        },
        storeDelKey() {
            event.preventDefault()

            let name = this.forms.delKey.name

            if (!confirm("Remove key named "+JSON.stringify(name)+"?")) {
                return
            }
            this.apiPost('/store/delete-key', name , (v) => {
                this.forms.delKey = {}
            })
        },
        unlockStore() {
            this.apiPost('/public/unlock-store', this.namedPassphrase(), (v) => {
                this.forms.store = {}

                if (v) {
                    this.session.token = v
                    if (!this.watchingState) {
                        this.watchState()
                        this.watchingState = true
                    }
                }
            })
        },
        uploadConfig() {
            const file = this.$refs.configUpload.files[0];
            let contentType = "text/vnd.yaml";
            if (file.name.endsWith(".json")) {
                contentType = "application/json";
            }
            this.apiPost('/configs', file, (v) => {}, contentType, true)
        },
        hostFromTemplateAdd() {
            let v = this.forms.hostFromTemplate;
            this.apiPost('/hosts-from-template/'+v.name, v, (v) => { this.forms.hostFromTemplate = {} });
        },
        hostFromTemplateDel() {
            event.preventDefault()

            let v = this.forms.hostFromTemplateDel;
            if (!confirm("delete host template instance "+v+"?")) {
                return
            }
            this.apiDelete('/hosts-from-template/'+v, (v) => { this.forms.hostFromTemplateDel = "" });
        },
        apiPost(action, data, onload, contentType = 'application/json', raw = false) {
            event.preventDefault()

            if (data === undefined) {
                throw("action " + action + ": no data")
            }

            /* TODO
            fetch(action, {
                method: 'POST',
                body: JSON.stringify(data),
            })
              .then((response) => response.json())
              .then((result) => onload)
            // */

            var xhr = new XMLHttpRequest()

            xhr.responseType = 'json'
            xhr.onerror = () => {}
            xhr.onload = (r) => {
                if (xhr.status != 200) {
                    this.toast((xhr.response && xhr.response.message) || 'request failed', 'error')
                    return
                }
                this.error = null
                if (onload) {
                    onload(xhr.response)
                }
            }

            xhr.open("POST", action)
            xhr.setRequestHeader('Accept', 'application/json')
            xhr.setRequestHeader('Content-Type', contentType)
            if (this.session.token) {
                xhr.setRequestHeader('Authorization', 'Bearer '+this.session.token)
            }

            if (!raw && contentType == "application/json") {
                xhr.send(JSON.stringify(data))
            } else {
                xhr.send(data)
            }
        },
        apiDelete(action, data, onload) {
            event.preventDefault()

            var xhr = new XMLHttpRequest()
            xhr.onload = (r) => {
                if (xhr.status != 200) {
                    this.error = xhr.response
                    return
                }
                this.error = null
                if (onload) {
                    onload(xhr.response)
                }
            }
            xhr.open("DELETE", action)
            xhr.setRequestHeader('Accept', 'application/json')
            if (this.session.token) {
                xhr.setRequestHeader('Authorization', 'Bearer '+this.session.token)
            }
            xhr.send()
        },
        download(url) {
            event.target.target = '_blank'
            event.target.href = this.downloadLink(url)
        },
        downloadLink(url) {
            // TODO once-shot download link
            return url + '?token=' + this.session.token
        },
        toast(message, kind) {
            const id = ++this.toastId
            this.toasts.push({id, message, kind: kind || 'info'})
            setTimeout(() => this.dismissToast(id), 4000)
        },
        dismissToast(id) {
            this.toasts = this.toasts.filter(t => t.id != id)
        },
        watchPublicState() {
            this.watchStream('publicState', '/public-state')
        },
        watchState() {
            this.watchStream('state', '/state', true)
        },
        watchStream(field, path, withToken) {
            let evtSrc = new EventSource(path + (withToken ? '?token='+this.session.token : ''));
            evtSrc.onmessage = (e) => {
                let update = JSON.parse(e.data)

                console.log("watch "+path+":", update)

                if (update.err) {
                    console.log("watch error from server:", err)
                }
                if (update.set) {
                    this[field] = update.set
                }
                if (update.p) { // patch
                    new jsonpatch.JSONPatch(update.p, true).apply(this[field])
                }
            }
            evtSrc.onerror = (e) => {
                // console.log("event source " + path + " error:", e)
                if (evtSrc) evtSrc.close()

                this[field] = null

                window.setTimeout(() => { this.watchStream(field, path, withToken) }, 1000)
            }
        },
    }

}).mount('#app');
