import rxjs from "../../lib/rx.js";
import { qs } from "../../lib/dom.js";
import { ApplicationError } from "../../lib/error.js";
import { query as getConfig } from "../../model/config.js";
import { get as getAdminConfig } from "./model_config.js";
import { formObjToJSON$ } from "./helper_form.js";

const backendsEnabled$ = new rxjs.BehaviorSubject();

export async function initStorage() {
    return await getConfig().pipe(
        rxjs.map(({ connections }) => connections),
        rxjs.tap((connections) => {
            if (backendsEnabled$.value !== undefined) return;
            backendsEnabled$.next(Array.isArray(connections) ? connections : []);
        }),
    ).toPromise();
}

export { getBackends as getBackendAvailable } from "./model_backend.js";

export function getBackendEnabled() {
    return backendsEnabled$.asObservable();
}

export function addBackendEnabled(type) {
    return getState().pipe(rxjs.map(({ connections }) => {
        let label = type;
        let i = 0;
        while (true) {
            if (!connections.find((obj) => obj.label === label)) {
                break;
            }
            i += 1;
            label = `${type} ${i}`;
        }
        const newConnections = connections.concat({ type, label });
        backendsEnabled$.next(newConnections);
        return newConnections;
    }));
}

export function removeBackendEnabled(labelToRemove) {
    return getState().pipe(rxjs.map(({ connections }) => {
        const newConnections = connections.filter(({ label }) => label !== labelToRemove);
        backendsEnabled$.next(newConnections);
        return newConnections;
    }));
}

const middlewareEnabled$ = new rxjs.ReplaySubject(1);

export async function initMiddleware() {
    return await getAdminConfig().pipe(
        rxjs.map(({ middleware }) => middleware),
        formObjToJSON$(),
        rxjs.tap(({ identity_provider }) => middlewareEnabled$.next(identity_provider.type)),
        rxjs.first(),
    ).toPromise();
}

export { getAuthMiddleware as getMiddlewareAvailable } from "./model_auth_middleware.js";

export function getMiddlewareEnabled() {
    return middlewareEnabled$.asObservable();
}

export function toggleMiddleware(type) {
    return middlewareEnabled$.pipe(
        rxjs.first(),
        rxjs.map((oldValue) => {
            const newValue = oldValue === type ? null : type;
            middlewareEnabled$.next(newValue);
            return newValue;
        }),
    );
}

export function getState() {
    return getAdminConfig().pipe(
        rxjs.first(),
        formObjToJSON$(),
        rxjs.map((config) => { // connections
            const connections = [];
            const formData = new FormData(qs(document.body, `[data-bind="backend-enabled"]`));
            for (const [type, label] of formData.entries()) {
                connections.push({ type, label });
            }
            config.connections = connections;
            return config;
        }),
        rxjs.map((config) => { // middleware
            const authType = document
                .querySelector(`[data-bind="authentication_middleware"] box-item.active`)
                ?.getAttribute("data-label");

            config.middleware = {
                identity_provider: { type: null },
                attribute_mapping: null,
            };
            if (!authType) return config;

            const $formIDP = document.querySelector(`[data-bind="idp"]`);
            if (!($formIDP instanceof HTMLFormElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: idp isn't a form");
            let formValues = [...new FormData($formIDP)];
            config.middleware.identity_provider = {
                type: authType,
                params: JSON.stringify(
                    formValues
                        .filter(([key]) => key.startsWith(`${authType}.`)) // remove elements that aren't in scope
                        .map(([key, value]) => [key.replace(new RegExp(`^${authType}\.`), ""), value]) // format the relevant keys
                        .reduce((acc, [key, value]) => { // transform onto something ready to be saved
                            if (key === "type") return acc;
                            else if (key === "banner") return acc;
                            else if (typeof key !== "string") return acc;
                            return {
                                ...acc,
                                [key]: value,
                            };
                        }, {}),
                ),
            };

            const $formAM = document.querySelector(`[data-bind="attribute-mapping"]`);
            if (!($formAM instanceof HTMLFormElement)) throw new ApplicationError("INTERNAL_ERROR", "assumption failed: attribute mapping isn't a form");
            formValues = [...new FormData($formAM)];
            config.middleware.attribute_mapping = {
                related_backend: (formValues.shift() || [])[1],
                params: JSON.stringify(formValues.reduce((acc, [key, value]) => {
                    const k = key.split(".");
                    if (k.length !== 2) return acc;
                    if (!acc[`${k[0]}`]) acc[`${k[0]}`] = {};
                    if (value !== "") acc[`${k[0]}`][`${k[1]}`] = value;
                    return acc;
                }, {})),
            };

            return config;
        }),
    );
}
