export abstract class TesoftComponent extends HTMLElement {
    private ready: boolean;

    protected constructor() {
        super();

        this.ready = false;
    }

    // noinspection JSUnusedGlobalSymbols
    async connectedCallback() {
        const promises = [];

        for (const child of findComponents(this.shadowRoot)) {
            promises.push(new Promise(resolve =>
                window.customElements.whenDefined(child.tagName.toLowerCase()).then(() => {
                    if (child.isConnected) {
                        resolve(void {});
                    } else {
                        child.addEventListener("ready", (e: any) => {
                            e.preventDefault();
                            resolve(void {});
                        }, {once: true});
                    }
                })
            ));
        }

        await Promise.all(promises);
        await this.populate();

        this.ready = true;
        this.dispatchEvent(new CustomEvent("ready", {
            bubbles: true,
            cancelable: true,
        }));
    }

    async populate() {
        // Overwrite if necessary in components
    }

    get isReady(): boolean {
        return this.ready;
    }
}

function findComponents(root: any | null): TesoftComponent[] {
    const components = [];

    for (const child of root?.querySelectorAll("*") ?? []) {
        if (child.tagName.toLowerCase().startsWith("snz-")) {
            components.push(child);
        }
    }

    return components;
}

export function onReady(callback: () => void, ...components: TesoftComponent[]) {
    async function domLoaded() {
        if (components.length === 0) {
            components = findComponents(document);
        }

        const promises = [];
        for (const component of components) {
            if (!component.isReady) {
                promises.push(new Promise(resolve => {
                    component.addEventListener("ready", resolve, {once: true});
                }));
            }
        }

        await Promise.all(promises);
        document.querySelector("body > div")!.classList.remove("hidden");
        callback();
    }

    if (document.readyState === "loading") {
        document.addEventListener("DOMContentLoaded", domLoaded, {once: true});
    } else {
        // noinspection JSIgnoredPromiseFromCall
        domLoaded();
    }
}

export function sendApiPost(path: string, data: Record<string, any>): Promise<Response> {
    const url = new URL(`/api/${path}`, location.origin);

    return fetch(url, {
        method: "POST",
        headers: {
            "Content-Type": "application/json",
        },
        body: JSON.stringify(data),
    });
}

export function capitalize(str: string): string {
    if (str) {
        return str.charAt(0).toUpperCase() + str.slice(1);
    }

    return str;
}

export function sendApiGet(path: string, query: Record<string, string | number>): Promise<Response> {
    const url = new URL(`/api/${path}`, location.origin);
    for (const [key, value] of Object.entries(query)) {
        url.searchParams.append(key, value.toString());
    }

    return fetch(url, {
        method: "GET",
    });
}

export function formatDate(date: Date): string {
    return `${date.getFullYear().toString().padStart(4, "0")}-${(date.getMonth() + 1).toString().padStart(2, "0")}-${date.getDate().toString().padStart(2, "0")}`;
}
