import { questionApi } from "@api/question";
import { quizApi } from "@api/quiz";
import { devlog } from "@frontend/kitui";
import { questionToEditQuestionRequest } from "@model/question/edit";
import { QuestionType, RawQuestion, rawQuestionToQuestion } from "@model/question/question";
import { AnyTypedQuizQuestion, QuestionVariant, UntypedQuizQuestion, createQuestionVariant } from "@model/questionTypes/shared";
import { defaultQuestionByType } from "../../constants/default";
import { produce } from "immer";
import { nanoid } from "nanoid";
import { enqueueSnackbar } from "notistack";
import { isAxiosCanceledError } from "../../utils/isAxiosCanceledError";
import { RequestQueue } from "../../utils/requestQueue";
import { QuestionsStore, useQuestionsStore } from "./store";


export const setQuestions = (questions: RawQuestion[] | null) => setProducedState(state => {
    const untypedQuestions = state.questions.filter(q => q.type === null);

    state.questions = questions?.map(rawQuestionToQuestion) ?? [];
    state.questions.push(...untypedQuestions);
}, {
    type: "setQuestions",
    questions,
});

export const createUntypedQuestion = (quizId: number) => setProducedState(state => {
    state.questions.push({
        id: nanoid(),
        quizId,
        type: null,
        title: "",
        description: "",
        deleted: false,
        expanded: true,
    });
}, {
    type: "createUntypedQuestion",
    quizId,
});

const removeQuestion = (questionId: string) => setProducedState(state => {
    const index = state.questions.findIndex(q => q.id === questionId);
    if (index === -1) return;

    state.questions.splice(index, 1);
}, {
    type: "removeQuestion",
    questionId,
});

export const updateUntypedQuestion = (
    questionId: string,
    updateFn: (question: UntypedQuizQuestion) => void,
) => {
    setProducedState(state => {
        const question = state.questions.find(q => q.id === questionId);
        if (!question) return;
        if (question.type !== null) throw new Error("Cannot update typed question, use 'updateQuestion' instead");

        updateFn(question);
    }, {
        type: "updateUntypedQuestion",
        questionId,
        updateFn: updateFn.toString(),
    });
};

export const cleanQuestions = () => setProducedState(state => {
    state.questions = [];
}, {
    type: "cleanQuestions",
});

const setQuestionBackendId = (questionId: string, backendId: number) => setProducedState(state => {
    const question = state.questions.find(q => q.id === questionId);
    if (!question) return;
    if (question.type === null) throw new Error("Cannot set backend id for untyped question");

    question.backendId = backendId;
}, {
    type: "setQuestionBackendId",
    questionId: questionId,
    backendId,
});

export const reorderQuestions = (
    sourceIndex: number,
    destinationIndex: number,
) => {
    if (sourceIndex === destinationIndex) return;

    setProducedState(state => {
        const [removed] = state.questions.splice(sourceIndex, 1);
        state.questions.splice(destinationIndex, 0, removed);
    }, {
        type: "reorderQuestions",
        sourceIndex,
        destinationIndex,
    });
};

export const toggleExpandQuestion = (questionId: string) => setProducedState(state => {
    const question = state.questions.find(q => q.id === questionId);
    if (!question) return;

    question.expanded = !question.expanded;
}, {
    type: "toggleExpandQuestion",
    questionId,
});

export const collapseAllQuestions = () => setProducedState(state => {
    state.questions.forEach(question => question.expanded = false);
}, "collapseAllQuestions");


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

export const updateQuestion = (
    questionId: string,
    updateFn: (question: AnyTypedQuizQuestion) => void,
) => {
    setProducedState(state => {
        const question = state.questions.find(q => q.id === questionId) || state.questions.find(q => q.content.id === questionId);
        if (!question) return;
        if (question.type === null) throw new Error("Cannot update untyped question, use 'updateUntypedQuestion' instead");

        updateFn(question);
    }, {
        type: "updateQuestion",
        questionId,
        updateFn: updateFn.toString(),
    });

    // clearTimeout(requestTimeoutId);
    // requestTimeoutId = setTimeout(() => {
        requestQueue.enqueue(async () => {
            const q = useQuestionsStore.getState().questions.find(q => q.id === questionId)  || useQuestionsStore.getState().questions.find(q => q.content.id === questionId);
            console.log("мы пытаемся найти вопрос ")
            if (!q) return;
            if (q.type === null) throw new Error("Cannot send update request for untyped question");
            console.log(q.title)

            const response = await questionApi.edit(questionToEditQuestionRequest(q));

            setQuestionBackendId(questionId, response.updated);
        }).catch(error => {
            if (isAxiosCanceledError(error)) return;

            devlog("Error editing question", { error, questionId });
            enqueueSnackbar("Не удалось сохранить вопрос");
        });
    // }, REQUEST_DEBOUNCE);
};

export const addQuestionVariant = (questionId: string) => {
    updateQuestion(questionId, question => {
        switch (question.type) {
            case "variant":
            case "emoji":
            case "select":
            case "images":
            case "varimg":
                question.content.variants.push(createQuestionVariant());
                break;
            default: throw new Error(`Cannot add variant to question of type "${question.type}"`);
        }
    });
};

export const deleteQuestionVariant = (questionId: string, variantId: string) => {
    updateQuestion(questionId, question => {
        if (!("variants" in question.content)) return;

        const variantIndex = question.content.variants.findIndex(variant => variant.id === variantId);
        if (variantIndex === -1) return;

        question.content.variants.splice(variantIndex, 1);
    });
};

export const setQuestionVariantField = (
    questionId: string,
    variantId: string,
    field: keyof QuestionVariant,
    value: QuestionVariant[keyof QuestionVariant],
) => {
    updateQuestion(questionId, question => {
        if (!("variants" in question.content)) return;

        const variantIndex = question.content.variants.findIndex(variant => variant.id === variantId);
        if (variantIndex === -1) return;

        const variant = question.content.variants[variantIndex];
        variant[field] = value;
    });
};

export const reorderQuestionVariants = (
    questionId: string,
    sourceIndex: number,
    destinationIndex: number,
) => {
    if (sourceIndex === destinationIndex) return;

    updateQuestion(questionId, question => {
        if (!("variants" in question.content)) return;

        const [removed] = question.content.variants.splice(sourceIndex, 1);
        question.content.variants.splice(destinationIndex, 0, removed);

    });
};

export const uploadQuestionImage = async (
    questionId: string,
    quizQid: string | undefined,
    blob: Blob,
    updateFn: (question: AnyTypedQuizQuestion, imageId: string) => void,
) => {
    const question = useQuestionsStore.getState().questions.find(q => q.id === questionId);
    if (!question || !quizQid) return;

    try {
        const response = await quizApi.addImages(question.quizId, blob);

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

        const imageId = values[0];
        const imageUrl = `https://squiz.pena.digital/squizimages/${quizQid}/${imageId}`;

        updateQuestion(questionId, question => {
            updateFn(question, imageUrl);
        });

        return imageUrl;
    } catch (error) {
        devlog("Error uploading question image", error);

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

export const setQuestionInnerName = (
    questionId: string,
    name: string,
) => {
    updateQuestion(questionId, question => {
        question.content.innerName = name;
    });
};

export const changeQuestionType = (
    questionId: string,
    type: QuestionType,
) => {
    updateQuestion(questionId, question => {
        question.type = type;
        question.content = defaultQuestionByType[type].content;
    });
};

export const createTypedQuestion = async (
    questionId: string,
    type: QuestionType,
) => requestQueue.enqueue(async () => {
    const question = useQuestionsStore.getState().questions.find(q => q.id === questionId);
    if (!question) return;
    if (question.type !== null) throw new Error("Cannot upgrade already typed question");
    
    try {
        const createdQuestion = await questionApi.create({
            quiz_id: question.quizId,
            type,
            title: question.title,
            description: question.description,
            page: 0,
            required: true,
            content: JSON.stringify(defaultQuestionByType[type].content),
        });

        setProducedState(state => {
            const questionIndex = state.questions.findIndex(q => q.id === questionId);
            if (questionIndex !== -1) state.questions.splice(
                questionIndex,
                1,
                rawQuestionToQuestion(createdQuestion)
            );
        }, {
            type: "createTypedQuestion",
            question,
        });
    } catch (error) {
        devlog("Error creating question", error);
        enqueueSnackbar("Не удалось создать вопрос");
    }
});

export const deleteQuestion = async (questionId: string) => requestQueue.enqueue(async () => {
    const question = useQuestionsStore.getState().questions.find(q => q.id === questionId);
    if (!question) return;

    if (question.type === null) {
        removeQuestion(questionId);
        return;
    }

    try {
        await questionApi.delete(question.backendId);

        removeQuestion(questionId);
    } catch (error) {
        devlog("Error deleting question", error);
        enqueueSnackbar("Не удалось удалить вопрос");
    }
});

export const copyQuestion = async (questionId: string, quizId: number) => requestQueue.enqueue(async () => {
    const question = useQuestionsStore.getState().questions.find(q => q.id === questionId);
    if (!question) return;

    if (question.type === null) {
        const copiedQuestion = structuredClone(question);
        copiedQuestion.id = nanoid();

        setProducedState(state => {
            state.questions.push(copiedQuestion);
        }, {
            type: "copyQuestion",
            questionId,
            quizId,
        });

        return;
    }

    try {
        const { updated: newQuestionId } = await questionApi.copy(question.backendId, quizId);

        const copiedQuestion = structuredClone(question);
        copiedQuestion.backendId = newQuestionId;
        copiedQuestion.id = nanoid();

        setProducedState(state => {
            state.questions.push(copiedQuestion);
        }, {
            type: "copyQuestion",
            questionId,
            quizId,
        });
    } catch (error) {
        devlog("Error copying question", error);
        enqueueSnackbar("Не удалось скопировать вопрос");
    }
});

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


export const cleardragQuestionContentId = () => {
    useQuestionsStore.setState({dragQuestionContentId: null});
};

export const getQuestionById = (questionId: string | null) => {
    if (questionId === null) return null;
    return useQuestionsStore.getState().questions.find(q => q.id === questionId) || null;
};
export const getQuestionByContentId = (questionContentId: string | null) => {
    console.log("questionContentId " + questionContentId)
    if (questionContentId === null) return null;
    return useQuestionsStore.getState().questions.find(q => {
        console.log(q.content.id)
        console.log(q.content.id === questionContentId)
        return ( q.content.id === questionContentId)}) || null;
};

export const updateOpenedModalSettingsId = (id?: string) => useQuestionsStore.setState({openedModalSettingsId: id ? id : null});
export const updateDragQuestionContentId = (contentId?: string) => {
    console.log("contentId " + contentId)
    useQuestionsStore.setState({dragQuestionContentId: contentId ? contentId : null});
}
