"use strict";

import * as USIMP from "usimp";

export class App {
    account: USIMP.Account | null;
    defaultLocation: string;
    main: HTMLElement;
    windows: HTMLElement;
    session: USIMP.Session | null;

    constructor() {
        this.defaultLocation = '/welcome';
        this.account = null;
        this.session = null;

        const main = document.getElementsByTagName("main")[0];
        if (!main) throw Error("Element <main> not found");
        this.main = main;

        const windows = document.getElementById("windows");
        if (!windows) throw Error("Element #windows not found");
        this.windows = windows;

        window.addEventListener("hashchange", event => {
            this.handleUrl(event.newURL);
        });

        this.handleUrl(document.URL);
    }

    setHash(hash: string) {
        const url = new URL(document.URL);
        url.hash = hash;
        location.href = url.toString();
    }

    handleUrl(url: string) {
        this.handleHash(new URL(url).hash);
    }

    handleHash(hash: string) {
        if (hash[0] === '#') hash = hash.substr(1);

        const defaultCase = () => {
            history.replaceState(null, document.title, `#${this.defaultLocation}`);
            this.handleHash(this.defaultLocation);
        }

        switch (hash) {
            case "/":
                if (!this.session) {
                    return defaultCase();
                }
                this.hideWindows();
                this.showMain();
                this.removeAllWindows();
                this.setupMain();
                break;
            case "/welcome":
                this.hideMain();
                this.showWindows();
                this.removeAllWindows();
                this.addWelcomeWindow();
                break;
            case "/login":
                if (this.session) {
                    return defaultCase();
                }
                this.hideMain();
                this.showWindows();
                this.removeAllWindows();
                this.addLoginWindow();
                break;
            default:
                console.warn(`Invalid url hash #${hash}`);
                return defaultCase();
        }
    }

    async login(accountName: string, domainName: string, password: string) {
        let domain;
        try {
            domain = await USIMP.Domain.fromName(domainName);
        } catch (error) {
            if (error instanceof DOMException) {
                throw Error("Connection timed out");
            } else {
                throw Error("Invalid USIMP domain");
            }
        }

        const session = new USIMP.Session(domain);
        const rtt = await session.chooseDomainServer();
        console.log(rtt);

        const response = await session.authenticate(accountName, password);
        if (response.status === "success") {
            this.session = session;
            this.defaultLocation = "/";
            this.setHash("/");
        } else {
            throw Error(response.message);
        }
    }

    hideMain() {
        this.main.style.visibility = "hidden";
    }

    showMain() {
        this.main.style.visibility = "visible";
    }

    hideWindows() {
        this.windows.style.visibility = "hidden";
    }

    showWindows() {
        this.windows.style.visibility = "visible";
    }

    removeAllWindows() {
        while (this.windows.lastChild) this.windows.removeChild(this.windows.lastChild);
    }

    addWelcomeWindow() {
        const win = document.createElement("div");
        win.classList.add("window-welcome");

        win.innerHTML = `
            <h1>Welcome to Locutus!</h1>
            <a href="#/login" class="button">Login</a>`;

        this.windows.appendChild(win);
    }

    addMessage(message: string) {
        const msg = document.createElement("div");
        msg.classList.add("message");

        msg.innerText = message;

        const chat = this.main.getElementsByClassName("chat-history")[0];
        if (!chat) throw Error("Element .chat-history not found");

        chat.appendChild(msg);
        chat.scrollTop = chat.scrollHeight;
    }

    addLoginWindow() {
        const win = document.createElement("div");
        win.classList.add("window-login");

        win.innerHTML = `
            <h1>Login to USIMP Account</h1>
            <form>
                <input name="account" placeholder="Account name" type="text" required/>
                <input name="domain" placeholder="Domain" type="text" pattern="([a-zA-Z0-9_-]+\\.)+[a-zA-Z]{2,}" required/>
                <input name="password" placeholder="Password" type="password" required/>
                <button type="submit">Login</button>
            </form>`;

        const form = win.getElementsByTagName("form")[0];
        if (!form) throw Error("Element <form> not found");

        form.addEventListener("submit", async event => {
            event.preventDefault();

            for (const e of form.getElementsByTagName("button")) e.disabled = false;
            for (const e of form.getElementsByTagName("input")) {
                e.disabled = true;
                e.removeAttribute("invalid");
            }

            try {
                try {
                    await this.login(form['account'].value, form['domain'].value, form['password'].value);
                } finally {
                    for (const d of form.getElementsByTagName("div")) form.removeChild(d);
                    for (const e of form.getElementsByTagName("input")) e.disabled = false;
                    for (const e of form.getElementsByTagName("button")) e.disabled = false;
                }
            } catch (error) {
                if (error.toString() === "Error: unable to authenticate") {
                    const account = document.getElementsByName("account")[0];
                    if (account) account.setAttribute("invalid", "invalid");
                } else {
                    const domain = document.getElementsByName("domain")[0];
                    if (domain) domain.setAttribute("invalid", "invalid");
                }

                const div = document.createElement("div");
                div.classList.add("error");
                div.innerText = error.toString();
                form.appendChild(div);
            }
        });

        this.windows.appendChild(win);

        const domain = document.getElementsByName("domain")[0];
        if (!domain) throw Error("Element name=domain not found");

        domain.addEventListener("input", (event) => {
            domain.removeAttribute("invalid");
        });

        const account = document.getElementsByName("account")[0];
        if (!account) throw Error("Element name=account not found");

        account.addEventListener("input", (event) => {
            account.removeAttribute("invalid");
        });

        account.focus();
    }

    setupMain() {
        if (!this.session) throw Error("Invalid state");

        this.main.innerHTML = `
            <div class="chat">
                <div class="chat-history"></div>
                <div class="chat-input-wrapper">
                    <input name="message" type="text" autocomplete="off"/>
                </div>
            </div>`;

        const input = document.getElementsByTagName("input")[0];
        if (!input) throw Error("Element <input> not found");

        input.addEventListener("keyup", async event => {
            if (event.key === "Enter" && input.value.length > 0) {
                if (!this.session) throw Error("No session found");

                this.addMessage(input.value);
                const val = input.value;
                input.value = "";
                await this.session.sendEvent("60nc0XXDIYUh6QzX4p0rMpCdzDmxghZLZk8dLuQh628", {
                    message: val,
                });
            }
        });

        this.session.subscribe(response => {
            this.addMessage(response.data.event.data.message);
        });
    }
}
