import SubmitStrategy from "../SubmitStrategy";
import Form from "../../Form";
import SubmitStrategyFactory, {SubmitStrategyConfig} from "../SubmitStrategyFactory";
import InnerNode from "../../../../../Data/InnerNode";

export default class RestSubmit implements SubmitStrategy{
    public url: string = "";
    public headers: object = {};
    protected parentData: InnerNode|null = null;
    protected childData: InnerNode|null = null;

    constructor(protected form: Form, protected saveDataOnSuccess: boolean = false) {
        form.onFormChange.add(() => {
            window.addEventListener('beforeunload', this.leaveCheck);
        });

        if (this.saveDataOnSuccess)
            this.form.getFormBuilder().onComponentsCreated.add(() => {
                let parentData = this.form.parent?.getDataNode();
                if (!parentData) return;
                const childData = this.form.getDataNode();
                this.childData = childData;
                parentData.removeChild(childData);

                while(parentData.index == null && parentData.parent != null){
                    parentData = parentData.parent;
                }

                this.parentData = parentData;
            });
    }

    public async submit(data: any) {
        this.form.message = null;
        this.form.submitting = true;
        await fetch(this.url, {
            method: 'POST',
            headers: {
                ...this.headers,
                'Content-Type': 'application/json',
            },
            body: JSON.stringify(data)
        }).then(
            response => response.json()
        ).then(
            data => this.handleResponse(data)
        ).catch(
            error => {
                console.error(error);
                this.form.message = "Er is iets misgegaan, probeer het later opnieuw.";
                this.form.submitting = false;

                this.form.getPlacedHtml().every(element => element.classList.add("pad-error"));
            }
        );
    }

    public leaveCheck(event: any): void{
        event.preventDefault();
        event.returnValue = true;
    }

    protected handleRedirect(data: RedirectRestResponse){
        window.removeEventListener('beforeunload', this.leaveCheck);
        window.location.href = data.url;
    }

    protected handleMessage(data: MessageRestResponse){
        if ( data.message == null )
            data.message = (() => {switch (data.status) {
                case "success": return "Succesvol verzonden.";
                case "failed": return "Iets is mis gegaan, probeer het later opnieuw.";
                case "server-error": return "Er is iets misgegaan op de server. Probeer het later opnieuw.";
                case "config-error": return "Er is iets misgegaan met de configuratie. Laat de ons dit graag weten.";
                default: return "Er is iets misgegaan. Probeer het later opnieuw.";
            }})();

        this.form.message = data.message;
        this.form.submitting = false;

        if (data.status !== "success"){
            console.warn("Error while submitting form, status: " + data.status);
            console.warn(data);
            this.form.getPlacedHtml().every(element => element.classList.add("pad-error"));
            if (data.errors != null){
                this.form.serverSideErrors = data.errors;
            }
            return;
        }
        this.form.serverSideErrors = {};

        if (this.saveDataOnSuccess && this.parentData && this.childData) {
            let dataInParent = this.parentData.value ?? {};
            let dataInChild = this.childData.value ?? {};
            this.parentData.value = {...dataInParent, ...dataInChild};
        }

        this.form.getPlacedHtml().every(element => element.classList.remove("pad-error"));
        window.removeEventListener('beforeunload', this.leaveCheck);
    }

    protected handleResponse(data: any){
        if (this.responseIsRedirect(data)) {
            this.handleRedirect(data);
            return;
        }
        if (this.responseIsMessage(data)) {
            this.handleMessage(data);
            return;
        }
        console.error("Unknown response from rest submit");
        console.error(data);
        this.form.submitting = false;
    }

    protected responseIsRedirect(data: any): data is RedirectRestResponse{
        return data.status === "redirect";
    }

    protected responseIsMessage(data: any): data is MessageRestResponse{
        return data.status === "success" || data.status === "server-error" || data.status === "config-error" || data.status === "failed";
    }
}

export function addRestSubmitToFactory(factory: SubmitStrategyFactory): void {
    factory.registerConstructor("rest", (config, form) => {
        if (!isRestConfig(config)) return null;
        const restSubmit = new RestSubmit(form, config.saveDataOnSuccess);
        restSubmit.url = config.url;
        restSubmit.headers = config.headers;
        return restSubmit;
    });
}

function isRestConfig(config: SubmitStrategyConfig): config is RestConfig{
    return config.type === "rest";
}

interface RestConfig extends SubmitStrategyConfig{
    type: "rest";
    url: string;
    headers: object;
    saveDataOnSuccess: boolean;
}

interface MessageRestResponse{
    status: "success"|"server-error"|"config-error"|"failed";
    message?: string;
    errors?: object;
}

interface RedirectRestResponse{
    status: "redirect";
    url: string;
}