// retoor <retoor@molodetz.nl>

export class Http {
    static toLogin() {
        const next = encodeURIComponent(location.pathname + location.search + location.hash);
        window.location.href = `/auth/login?next=${next}`;
    }

    static suspend() {
        return new Promise(() => {});
    }

    static _gate(data, options, retry) {
        if (options.termsRetry) return null;
        const gate = window.app && window.app.termsGate;
        if (!gate || !gate.matches(data)) return null;
        return gate.intercept(data.error, retry);
    }

    static notifyError(message) {
        const text = (message && String(message).trim()) || "Something went wrong. Please try again.";
        const app = window.app;
        if (app && app.dialog && typeof app.dialog.alert === "function") {
            app.dialog.alert({ title: "Error", message: text });
            return;
        }
        if (app && app.toast && typeof app.toast.show === "function") {
            app.toast.show(text, { type: "error", ms: 6000 });
            return;
        }
        console.error(text);
    }

    static async _messageFrom(response) {
        const data = await response.json().catch(() => ({}));
        const message = (data && data.error && data.error.message)
            || (data && Array.isArray(data.messages) && data.messages.length ? data.messages.join(" ") : "")
            || `request failed with status ${response.status}`;
        return { data, message };
    }

    static _error(message, status) {
        const error = new Error(message);
        error.status = status;
        return error;
    }

    static async getJson(url) {
        const response = await fetch(url, { headers: { "Accept": "application/json" } });
        if (!response.ok) {
            throw Http._error(`request failed with status ${response.status}`, response.status);
        }
        return response.json();
    }

    static async sendForm(url, params = {}, options = {}) {
        const response = await fetch(url, {
            method: "POST",
            headers: {
                "Accept": "application/json",
                "X-Requested-With": "fetch",
                "Content-Type": "application/x-www-form-urlencoded",
            },
            body: new URLSearchParams(params),
        });
        if (response.redirected && response.url.includes("/auth/login")) {
            Http.toLogin();
            return Http.suspend();
        }
        if (!response.ok) {
            const { data, message } = await Http._messageFrom(response);
            const gate = Http._gate(data, options, () =>
                Http.sendForm(url, params, { ...options, termsRetry: true })
            );
            if (gate) return gate;
            if (!options.silent) Http.notifyError(message);
            throw new Error(message);
        }
        return response.json().catch(() => ({}));
    }

    static async send(url, params = {}, options = {}) {
        const response = await fetch(url, {
            method: "POST",
            headers: {
                "Accept": "application/json",
                "Content-Type": "application/x-www-form-urlencoded",
            },
            body: new URLSearchParams(params),
        });
        if (response.redirected && response.url.includes("/auth/login")) {
            Http.toLogin();
            return Http.suspend();
        }
        const data = await response.json().catch(() => ({}));
        if (!response.ok || data.ok === false) {
            const message = (data.error && data.error.message) || `request failed: ${response.status}`;
            const gate = Http._gate(data, options, () =>
                Http.send(url, params, { ...options, termsRetry: true })
            );
            if (gate) return gate;
            if (!options.silent) Http.notifyError(message);
            throw new Error(message);
        }
        return data;
    }

    static async sendDelete(url, options = {}) {
        const response = await fetch(url, {
            method: "DELETE",
            headers: { "Accept": "application/json" },
        });
        if (response.redirected && response.url.includes("/auth/login")) {
            Http.toLogin();
            return Http.suspend();
        }
        const data = await response.json().catch(() => ({}));
        if (!response.ok || data.ok === false) {
            const message = (data.error && data.error.message) || `request failed: ${response.status}`;
            const gate = Http._gate(data, options, () =>
                Http.sendDelete(url, { ...options, termsRetry: true })
            );
            if (gate) return gate;
            if (!options.silent) Http.notifyError(message);
            throw new Error(message);
        }
        return data;
    }

    static async postJson(url, body, options = {}) {
        const response = await fetch(url, {
            method: "POST",
            headers: { "Accept": "application/json", "Content-Type": "application/json" },
            body: JSON.stringify(body),
        });
        if (response.redirected && response.url.includes("/auth/login")) {
            Http.toLogin();
            return Http.suspend();
        }
        if (!response.ok) {
            const { data, message } = await Http._messageFrom(response);
            const gate = Http._gate(data, options, () =>
                Http.postJson(url, body, { ...options, termsRetry: true })
            );
            if (gate) return gate;
            if (!options.silent) Http.notifyError(message);
            throw Http._error(message, response.status);
        }
        return response.json();
    }

    static postForm(action, data = {}) {
        const form = document.createElement("form");
        form.method = "POST";
        form.action = action;
        for (const [name, value] of Object.entries(data)) {
            const input = document.createElement("input");
            input.type = "hidden";
            input.name = name;
            input.value = value;
            form.appendChild(input);
        }
        document.body.appendChild(form);
        form.submit();
    }
}

window.Http = Http;
