import { quizApi } from "@api/quiz";
import { devlog, getMessageFromFetchError } from "@frontend/kitui";
import { quizToEditQuizRequest } from "@model/quiz/edit";
import { Quiz, RawQuiz, rawQuizToQuiz } from "@model/quiz/quiz";
import { QuizConfig, maxQuizSetupSteps } from "@model/quizSettings";
import { produce } from "immer";
import { enqueueSnackbar } from "notistack";
import { NavigateFunction } from "react-router-dom";
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
import { RequestQueue } from "../../utils/requestQueue";
import { QuizStore, useQuizStore } from "./store";
import { createUntypedQuestion } from "@root/questions/actions";


export const setEditQuizId = (quizId: number | null) => setProducedState(state => {
    state.editQuizId = quizId;
}, {
    type: "setEditQuizId",
    quizId,
});

export const resetEditConfig = () => setProducedState(state => {
    state.editQuizId = null;
    state.currentStep = 0;
}, "resetEditConfig");

export const setQuizes = (quizes: RawQuiz[] | null) => setProducedState(state => {
    state.quizes = quizes?.map(rawQuizToQuiz) ?? [];
}, {
    type: "setQuizes",
    quizes,
});

const addQuiz = (quiz: Quiz) => setProducedState(state => {
    state.quizes.push(quiz);
}, {
    type: "addQuiz",
    quiz,
});

const removeQuiz = (quizId: string) => setProducedState(state => {
    const index = state.quizes.findIndex(q => q.id === quizId);
    if (index === -1) return;

    state.quizes.splice(index, 1);
}, {
    type: "removeQuiz",
    quizId,
});

const setQuizBackendId = (quizId: string, backendId: number) => setProducedState(state => {
    const quiz = state.quizes.find(q => q.id === quizId);
    if (!quiz) return;

    quiz.backendId = backendId;
}, {
    type: "setQuizBackendId",
    quizId,
    backendId,
});

export const incrementCurrentStep = () => setProducedState(state => {
    state.currentStep = Math.min(maxQuizSetupSteps - 1, state.currentStep + 1);
}, {
    type: "incrementCurrentStep",
});

export const decrementCurrentStep = () => setProducedState(state => {
    state.currentStep = Math.max(0, state.currentStep - 1);
}, {
    type: "decrementCurrentStep",
});

export const setCurrentStep = (step: number) => setProducedState(state => {
    state.currentStep = Math.max(0, Math.min(maxQuizSetupSteps - 1, step));
}, {
    type: "setCurrentStep",
    step,
});

export const setQuizType = (
    quizId: string,
    quizType: QuizConfig["type"],
) => {
    updateQuiz(
        quizId,
        quiz => {
            quiz.config.type = quizType;
        },
    );
};

export const setQuizStartpageType = (
    quizId: string,
    startpageType: QuizConfig["startpageType"],
) => {
    updateQuiz(
        quizId,
        quiz => {
            quiz.config.startpageType = startpageType;
        },
    );
};

const REQUEST_DEBOUNCE = 200;
const requestQueue = new RequestQueue();
let requestTimeoutId: ReturnType<typeof setTimeout>;

export const updateQuiz = async (
    quizId: string | null | undefined,
    updateFn: (quiz: Quiz) => void,
) => {
    if (!quizId) return;

    setProducedState(state => {
        const quiz = state.quizes.find(q => q.id === quizId);
        if (!quiz) return;

        updateFn(quiz);
    }, {
        type: "updateQuiz",
        quizId,
        updateFn: updateFn.toString(),
    });

    clearTimeout(requestTimeoutId);
    requestTimeoutId = setTimeout(async () => {
        requestQueue.enqueue(async () => {
            const quiz = useQuizStore.getState().quizes.find(q => q.id === quizId);
            if (!quiz) return;

            const response = await quizApi.edit(quizToEditQuizRequest(quiz));

            setQuizBackendId(quizId, response.updated);
            setEditQuizId(response.updated);
        }).catch(error => {
            if (isAxiosCanceledError(error)) return;

            devlog("Error editing quiz", error, quizId);
            enqueueSnackbar("Не удалось сохранить настройки квиза");
        });
    }, REQUEST_DEBOUNCE);
};

export const createQuiz = async (navigate: NavigateFunction) => requestQueue.enqueue(async () => {
    try {
        const rawQuiz = await quizApi.create();
        const quiz = rawQuizToQuiz(rawQuiz);

        addQuiz(quiz);
        setEditQuizId(quiz.backendId);
        navigate("/edit");

        await createUntypedQuestion(rawQuiz.id);
    } catch (error) {
        devlog("Error creating quiz", error);

        const message = getMessageFromFetchError(error) ?? "";
        enqueueSnackbar(`Не удалось создать квиз. ${message}`);
    }
});

export const deleteQuiz = async (quizId: string) => requestQueue.enqueue(async () => {
    const quiz = useQuizStore.getState().quizes.find(q => q.id === quizId);
    if (!quiz) return;

    try {
        await quizApi.delete(quiz.backendId);

        removeQuiz(quizId);
    } catch (error) {
        devlog("Error deleting quiz", error);

        const message = getMessageFromFetchError(error) ?? "";
        enqueueSnackbar(`Не удалось удалить квиз. ${message}`);
    }
});
export const updateRootInfo = (quizId: string, have:boolean) => updateQuiz(
    quizId,
    quiz => {
        quiz.config.haveRoot = have;
    },
);

// TODO copy quiz

export const uploadQuizImage = async (
    quizId: string,
    blob: Blob,
    updateFn: (quiz: Quiz, imageId: string) => void,
) => {
    const quiz = useQuizStore.getState().quizes.find(q => q.id === quizId);
    if (!quiz) return;

    try {
        const response = await quizApi.addImages(quiz.backendId, blob);

        const values = Object.values(response);
        if (values.length !== 1) {
            console.warn("Error uploading image");
            return;
        }

        const imageId = values[0];

        updateQuiz(quizId, quiz => {
            updateFn(quiz, `https://squiz.pena.digital/squizimages/${quiz.qid}/${imageId}`);
        });
    } catch (error) {
        devlog("Error uploading quiz image", error);

        enqueueSnackbar("Не удалось загрузить изображение");
    }
};

function setProducedState<A extends string | { type: unknown; }>(
    recipe: (state: QuizStore) => void,
    action?: A,
) {
    useQuizStore.setState(state => produce(state, recipe), false, action);
}
