import axios from "axios";
import { playSound } from "@/utils/sound";
import { toast } from "react-toastify";
import { Generating } from "@/stores/examEditor/types";
import useExamEditorStore from "@/stores/examEditor";
import { Module } from "@/interfaces";

interface GeneratorConfig {
    method: 'GET' | 'POST';
    queryParams?: Record<string, string | string[]>;
    files?: Record<string, string>;
    body?: Record<string, any>;
}

export function generate(
    sectionId: number,
    module: Module,
    type: Generating,
    config: GeneratorConfig,
    mapData: (data: any) => Record<string, any>[],
    levelSectionId?: number,
    level: boolean = false
) {
    const setGenerating = (sectionId: number, generating: Generating, level: boolean, remove?: boolean) => {
        const state = useExamEditorStore.getState();
        const dispatch = state.dispatch;
        let generatingUpdate;
        if (level) {
            if (remove) {
                generatingUpdate = state.modules["level"].sections.find((s) => s.sectionId === levelSectionId)!.levelGenerating.filter(g => g === generating)
            }
            else {
                generatingUpdate = [...state.modules["level"].sections.find((s) => s.sectionId === levelSectionId)!.levelGenerating, generating];
            }
        } else {
            generatingUpdate = generating;
        }

        dispatch({
            type: "UPDATE_SECTION_SINGLE_FIELD",
            payload: { sectionId, module: level ? "level" : module, field: level ? "levelGenerating" : "generating", value: generatingUpdate }
        });
    };

    const setGeneratedResult = (sectionId: number, generating: Generating, result: Record<string, any>[] | undefined, level: boolean) => {
        const state = useExamEditorStore.getState();
        const dispatch = state.dispatch;

        let genResults;
        if (level) {
            genResults = [...state.modules["level"].sections.find((s) => s.sectionId === levelSectionId)!.levelGenResults, { generating, result, module }];
        } else {
            genResults = { generating, result, module };
        }

        dispatch({
            type: "UPDATE_SECTION_SINGLE_FIELD",
            payload: { sectionId: level ? levelSectionId! : sectionId, module: level ? "level" : module, field: level ? "levelGenResults" : "genResult", value: genResults }
        });
    };

    setGenerating(level ? levelSectionId! : sectionId, type, level);

    function buildQueryString(params: Record<string, string | string[]>): string {
        const searchParams = new URLSearchParams();
        
        Object.entries(params).forEach(([key, value]) => {
          if (Array.isArray(value)) {
            value.forEach(v => searchParams.append(key, v));
          } else {
            searchParams.append(key, value);
          }
        });
        
        return searchParams.toString();
    }
    
    const queryString = config.queryParams ? buildQueryString(config.queryParams) : '';

    const url = `/api/exam/generate/${module}/${sectionId}${queryString ? `?${queryString}` : ''}`;

    let body = null;
    if (config.files && Object.keys(config.files).length > 0 && config.method === 'POST') {
        const formData = new FormData();

        const buildForm = async () => {
            await Promise.all(
                Object.entries(config.files ?? {}).map(async ([key, blobUrl]) => {
                    const response = await fetch(blobUrl);
                    const blob = await response.blob();
                    const file = new File([blob], key, { type: blob.type });
                    formData.append(key, file);
                })
            );

            if (config.body) {
                Object.entries(config.body).forEach(([key, value]) => {
                    formData.append(key, value as string);
                });
            }
            return formData;
        };

        buildForm().then(form => {
            body = form;

            const request = axios.post(url, body, { headers: { 'Content-Type': 'multipart/form-data' } });
            request
                .then((result) => {
                    playSound("check");
                    setGeneratedResult(level ? levelSectionId! : sectionId, type, mapData(result.data), level);
                })
                .catch((error) => {
                    setGenerating(sectionId, undefined, level, true);
                    playSound("error");
                    toast.error("Something went wrong! Try to generate again.");
                });
        });
    } else {
        body = config.body;

        const request = config.method === 'POST'
            ? axios.post(url, body, { headers: { 'Content-Type': 'application/json' } })
            : axios.get(url);

        request
            .then((result) => {
                playSound("check");
                setGeneratedResult(level ? levelSectionId! : sectionId, type, mapData(result.data), level);
            })
            .catch((error) => {
                setGenerating(sectionId, undefined, level, true);
                playSound("error");
                toast.error("Something went wrong! Try to generate again.");
            });
    }
}