import React, { useState, useMemo, useContext } from 'react';
import { Modal, Upload, notification, Button } from 'antd';
import csv from 'csvtojson';

import { RepeatedData } from '../../RepeatedDataProvider';
import { InboxOutlined } from '@ant-design/icons';
import QuestionListTable from './QuestionListTable';
import './uploadQuestion.css';
import _, { lowerCase } from 'lodash';
import { BulkQuestionObj } from './questionUploadSchema';
import { selectorService } from '../../../services/selectorService';

const { Dragger } = Upload;


const UploadQuestionModal = ({ uploadMode, setUploadMode }) => {

    //context
    const { data: { subjects, questionTypes, queue } } = useContext(RepeatedData);

    const [fileList, setFileList] = useState([]);
    const [uploading, setUploading] = useState(false);
    const [jsonObj, setJsonObj] = useState([]);

    // making all the individual keys lowercase
    function renameKeys(obj, newKeys) {
        const keyValues = Object.keys(obj).map((key, i) => {
            let newLowercaseKey = newKeys[i] || key;
            if (newLowercaseKey === "topics") newLowercaseKey = "category";
            return { [newLowercaseKey]: obj[key] };
        });
        return Object.assign({}, ...keyValues);
    }

    // function to determine question type from the 'answer' property (also return answer array optionally)
    function determineQuestionType(answer, returnAnsArr = false) {
        // first check if the question has the answer field available otherwise return undefined
        if (!answer) {
            return undefined;
        }
        let ans = answer.replace(/\s/g, '');  // remove all the spaces
        let ar = ans.split(","); // determine the number of answer
        if (returnAnsArr) return ar;
        if (lowerCase(ar[0]).replace(/\s/g, '') === "true" || lowerCase(ar[0]).replace(/\s/g, '') === "false") return "TNF"; //check for true and false question -> TNF
        if (ar.length === 1) return "MCQ"; // check for answer having only one option -> MCQ
        if (ar.length > 1) return "MRQ"; // check for answer having more than one option -> MRQ
    }

    // conversion of property option1, option2 and so on to option array of object with
    // two property 'text' and 'isCorrect'
    function formNewOptionProperty(indexedObj, calledFrom = "csvImport") {
        let options = [];
        let answerArr = typeof indexedObj.answer === "string" ? determineQuestionType(indexedObj.answer, true) : indexedObj.answer;
        if (indexedObj.type === "MCQ" || indexedObj.type === "MRQ") {
            for (let i = 1; i < 7; i++) {
                if (indexedObj['option' + i] && indexedObj['option' + i]?.replace(/\s/g, '') && lowerCase(indexedObj['option' + i]) !== "nil") {
                    if (answerArr && answerArr.some(el => (`${i}` === el))) {
                        options.push({ text: indexedObj['option' + i], isCorrect: true });
                    } else {
                        options.push({ text: indexedObj['option' + i], isCorrect: false });
                    }
                }
            }
            // this part is the weeb out any potential answer for an option that doesn't exist 
            // Example - answer = ["8"] while we have only 4 options is wrong.
            if (options?.length > 0 && typeof answerArr === "object") {
                const c = options.length; // length of option -> means the max value of answer can be upto (c - 1)
                answerArr = answerArr.filter(ans => ans <= c);
            }
        }
        if (indexedObj.type === "TNF") {
            // to lowercase the TRUE and FALSE option
            if (answerArr[0]) {
                answerArr[0] = lowerCase(answerArr[0]).replace(/\s/g, "");
            }
            options.push({ text: "True", isCorrect: answerArr[0] !== "true" ? false : true });
            options.push({ text: "False", isCorrect: answerArr[0] === "false" ? true : false });

            // this part is the weeb out any potential answer for an option that doesn't exist 
            // Example - answer = ["retcon"] while we have only "true" and "false" options is wrong.
            if (answerArr[0] && !(answerArr[0] === "true" || answerArr[0] === "false")) {
                console.log("popping")
                answerArr.pop();
            }
        }

        // turning answer into an array
        indexedObj.answer = answerArr ? answerArr : [];
        // adding options as a property
        indexedObj.options = options;
        return indexedObj;
    }

    // function to add (hopefully) a subject and topic to each question before forwarding it to the table
    function determineSubjectAndTopic(questionObj) {
        let subjectId; let topicId;
        const subjectOfQuestion = lowerCase(questionObj.subject).replace(/\s/g, '');
        const topicOfQuestion = lowerCase(questionObj.category).replace(/\s/g, '');

        subjectId = subjects.find(subjectItem => {
            const check1 = lowerCase(subjectItem.name).replace(/\s/g, '');
            if (check1 === subjectOfQuestion) {
                return true;
            } else return false;
        });
        // when subjectId has no match, it just sends undefined to subject and topic
        if (!subjectId) {
            return { subject: undefined, topic_id: undefined }
        }
        topicId = subjectId.topics.find(topicItem => {
            const check2 = lowerCase(topicItem.name).replace(/\s/g, '');
            if (check2 === topicOfQuestion) {
                return true;
            } else return false;
        });
        // when topicId is not found, it sends undefined on topic property
        if (!topicId) {
            return { subject: subjectId.id, topic_id: undefined }
        }
        return { subject: subjectId.id, topic_id: topicId.id };
    }

    const props = useMemo(() => ({
        onRemove: (file) => {
            const index = fileList.indexOf(file);
            const newFileList = fileList.slice();
            newFileList.splice(index, 1);
            setFileList(newFileList);
            setJsonObj([]);
        },
        beforeUpload: (file) => {
            setFileList([...fileList, file]);

            const reader = new FileReader();

            reader.onload = async (e) => {
                try {
                    // checking file type
                    if (!(file.type === "application/vnd.ms-excel" || lowerCase(file.name.split(".").pop()) === "csv")) {
                        setFileList([]);
                        throw Error("Please select a valid CSV file");
                    }

                    const json = await csv({ noheader: false }).fromString(e.target.result);
                    let i;
                    let newJson = []; // controlled array

                    // this loop is going through each question picked up from csv file
                    for (i of json) {
                        let keysArr = Object.keys(i);
                        let lowerKeysArr = [];
                        //removing spaces from individual keys in the objects
                        for (let item of keysArr) {
                            lowerKeysArr.push(_.lowerCase(item).replace(/\s/g, ''));
                        }

                        // new converted object
                        const newIndexObj = renameKeys(i, lowerKeysArr);

                        // adding question type proprty as queType
                        newIndexObj.type = determineQuestionType(newIndexObj.answer);

                        // adding an option propert to newIndexObj
                        const controlledObj = formNewOptionProperty(newIndexObj);

                        // determining subject and topics of each question
                        let newProperty = determineSubjectAndTopic(controlledObj);
                        controlledObj.subject = newProperty.subject;
                        controlledObj.category = newProperty.topic_id;

                        // pushing the converted object into the controlled array
                        // AIM - to add topic(id) and subject(id) for dropdown to identify their entry.
                        newJson.push(controlledObj);
                    }
                    setJsonObj([...newJson]);
                } catch (error) {
                    console.error(error);
                }
            };

            reader.readAsText(file);
            return false;
        },
        //onchange: (err, event) => console.log('event'),
        fileList,
        maxCount: 1
    }), [subjects, questionTypes]);

    React.useEffect(() => {
        if (jsonObj.length === 0) {
            setFileList((__) => []);
        }
    }, [jsonObj.length]);

    async function handleSubmit() {

        // set uploading to true to provide feback to user about a process going on
        setUploading(true);

        // /v{version}/{language}/AddBulkQuestions

        // setUploadMode({ isOpen: false }); // disabled in development
        let controlledValue = [];

        for (let item of jsonObj) {
            let controlledObj = {
                topic_id: item.category,
                answer: item.answer,
                complexity_code: parseInt(item.complexity, 10),
                title: item.question,
                answer_explanation: item.explanation,
                options: item.options,
                type: item.type,
                source: "BULK UPLOAD",
                status: "DRAFT"
            };

            controlledValue.push(controlledObj);
        }

        try {
            const validatedBulkQuestion = BulkQuestionObj.safeParse(controlledValue);
            if (!validatedBulkQuestion.success) {
                throw validatedBulkQuestion.error;
            }
            if (validatedBulkQuestion.success) {
                console.log(validatedBulkQuestion.data);
                if (validatedBulkQuestion.data.length <= 0) {
                    throw "No data found, please upload proper CSV file before pressing submit"
                }
                const questionObj = {
                    questions: [...validatedBulkQuestion.data]
                }
                const submit = await selectorService.createBulkQuestion(questionObj);
                if (submit.status.code < 0) {
                    throw "Failed validation. Please check your questions thoroughly before uploading."
                }
                notification.success({
                    message: "Successfully uploaded to the questions! Please check the Draft question to view them."
                });

                //reset after success
                setFileList([]);
                setJsonObj([]);
                setUploading(false);
                setUploadMode({ isOpen: false });
            }
        } catch (error) {
            if (error === "No data found, please upload proper CSV file before pressing submit") {
                notification.error({
                    message: error
                });
                setUploading(false);
                return;
            }
            if (error === "Failed validation. Please check your questions thoroughly before uploading") {
                notification.error({
                    message: error
                });
                setUploading(false);
                return;
            }
            process.env.NODE_ENV === "development" && console.log(error.issues || error);
            notification.error({
                message: "Please go through all the pages and make sure all the questions are validated correctly before submitting"
            });
            setUploading(false);
        }
    }

    return (
        <Modal
            title="Bulk upload questions"
            className='bulk-upload-module'
            width={"auto"}
            style={{
                minWidth: "360px",
                maxWidth: "calc(100vw - 40px)",
                maxHeight: "calc(100vh - 100px)"
            }}
            centered
            visible={uploadMode.isOpen}
            onOk={() => handleSubmit()}
            onCancel={() => {
                setFileList([]);
                setJsonObj([]);
                setUploadMode({ isOpen: false });
            }}
            footer={[
                <Button key="back" loading={uploading} onClick={() => {
                    setFileList([]);
                    setJsonObj([]);
                    setUploadMode({ isOpen: false });
                }}>
                    Cancel
                </Button>,
                <Button key="submit" type="primary" loading={uploading} onClick={() => handleSubmit()}>
                    Submit
                </Button>
            ]}
        >
            {/* condition is written to stop the application from loading sections, where resources might still be getting fetched from server */}
            {!queue.subjectsAreSet || !queue.questionTypesAreSet || !queue.topicsAreSet ? <div className='center-flex m-1-0'>
                Gathering all your books...please wait
            </div>
                : <>
                    <Dragger {...props} style={{ display: fileList.length < 1 ? 'block' : 'none' }}>
                        <p className="ant-upload-drag-icon">
                            {jsonObj.length < 1 && <InboxOutlined />}
                        </p>
                        <p className="ant-upload-text">
                            Click or drag file to this area to upload
                        </p>
                        <p className="ant-upload-hint">Works with purely text based questions</p>
                    </Dragger>
                    {fileList.length > 0 && <QuestionListTable data={jsonObj} setData={setJsonObj} subjects={subjects}
                        questionTypes={questionTypes} formNewOptionProperty={formNewOptionProperty} />}
                </>
            }
        </Modal>
    );
}

export default UploadQuestionModal;
