<template>
    <Form @submit="onSubmit" @invalid-submit="onInvalidSubmit" ref="theForm" v-slot="{ meta }">
        <div class="page-container-grouped-styles capability-questions">
            <loadingModal ref="loadingModal" />
            <funnelHeader cmsWidgetName="FunnelHeaderWidget" />
            <vehicleBanner cmsWidgetName="VehicleBannerWidget" :displayGenericVehicleImage=false />
            <funnelSubHeader cmsWidgetName="FunnelSubHeaderWidget" />
            <div class="fade-on-route-transition sub-container make-tall">
                <alert ref="alertFewMoreQuestions" class="my-5" alertClass="alert-warning"
                    :manualHeadline="AlertFewMoreQuestionsHeader" :manualCopy="AlertFewMoreQuestionsCopy"
                    v-bind:isDismissible="false" />
                <div v-for="(glass, i) in capabilityQuestionsData" :key="glass.key">
                    <questionChain ref="questionChain"
                        v-model="selectedAnswers[glass.glassLocation + '-' + glass.glassName]"
                        :questionData="glass.capabilityQuestions" :glassIndex="i"
                        v-if="showThisQuestionChain(glass, i)"
                        validationRules="questions-required" />
                </div>
                <funnel-footer ref="funnelFooter" cmsWidgetName="FunnelFooterWidget"
                    :isForwardActionDisabled="!meta.valid" @back-clicked="backButtonAction"
                    @ForwardClicked="forwardButtonAction" />
            </div>
        </div>
    </Form>
</template>

<script>
// Components
import funnelHeader from "@/common-components/funnel-header/funnel-header";
import vehicleBanner from "@/common-components/vehicle-banner/vehicle-banner";
import alert from "@/ux-components/alert/alert";
import questionChain from "@/common-components/question-chain/question-chain";
import funnelSubHeader from "@/common-components/funnel-sub-header/funnel-sub-header";
import funnelFooter from "@/common-components/funnel-footer/funnel-footer";
import loadingModal from '@/common-components/loading-modal/loading-modal.vue';

// Supporting Files
import { fetchCmsContentForPage } from "@/helpers/cms-content-helper";
import { settleAllPromises } from "@/helpers/layout-helper";
import store from "@/store";
import { storeActions } from "@/constants/store-actions";
import { fmgPageValues } from "@/router/router-constants/fmgPage-values";
import { Form, defineRule } from "vee-validate";
import { required } from "@/helpers/validation-rules";
import { errorMessages } from "@/constants/error-messages";
import vehicleQuestionsMixin from "@/mixins/vehicle-questions-mixin";
import { damageLocationsSelected } from "@/constants/damage-locations-selected";

// DEFINE VALIDATION RULES
defineRule("questions-required", required(errorMessages.OPTION_REQUIRED));

export default {
    name: "capability-questions",
    mixins: [vehicleQuestionsMixin],
    async beforeRouteEnter(to, from, next) {
        // Call APIs
        const cmsContentPromise = fetchCmsContentForPage(to.query.fmgPage);

        // Settle promises and get results
        const promiseResultMap = [
            {
                resultKey: "cmsContent",
                promise: cmsContentPromise,
            },
        ];

        const resultMap = await settleAllPromises(promiseResultMap);

        // Call the "next" function to complete the transition to this page.
        next((vm) => {
            vm.setCmsContent(resultMap.cmsContent);
        });
    },
    data() {
        return {
            capabilityQuestionsData: store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS).partsOrQuestions,
            selectedAnswers: {},
            currentQuestionChainIndex: 0,
            foundDuplicateQuestions: [],
        };
    },
    computed: {
        AlertFewMoreQuestionsHeader() {
            return this.getCmsContent("AdditionalPartsQuestionsAlert", "HeadlineText");
        },
        AlertFewMoreQuestionsCopy() {
            return this.getCmsContent("AdditionalPartsQuestionsAlert", "BodyText");
        },
        windshieldPart() {
            return this.pageData.partsOrQuestions.find(x => x.glassLocation === damageLocationsSelected.WINDSHIELD);
        },
        windshieldPartInfo() {
            return this.windshieldPart.parts[0];
        },
        pageData() {
            return this.$store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS);
        }
    },
    mounted() {
        this.loadInitialCapabilityQuestionsData();
    },
    methods: {
        showThisQuestionChain(glass, i) {
            if (!glass.capabilityQuestions || glass.capabilityQuestions?.length < 1 || glass.isSuppressedPart) { return false; } // return false if no capabilityQuestions or if suppressed
            return this.currentQuestionChainIndex === i || glass.answerData?.answerResult?.length > 0;
        },
        arePagePrerequisitesValid() {
            const capabilityQuestionsPageData = store.getters.pageData(fmgPageValues.CAPABILITY_QUESTIONS);
            return capabilityQuestionsPageData && Object.keys(capabilityQuestionsPageData).length > 0;
        },
        async forwardButtonAction() {
            const capabilityQuestionsAnswersArray = this.capabilityQuestionsData.map((glass) => {
                const selectedAnswerResult1 = glass.answerData.answerResult;
                const selectedAnswerResult2 = glass.answerData.answerResult2;

                return {
                    glassLocation: glass.glassLocation,
                    glassName: glass.glassName,
                    result: glass.answerData.answerResult,
                    result1: selectedAnswerResult1,
                    result2: selectedAnswerResult2,
                    answeredQuestions: glass.answerData.answeredQuestions,
                    isSuppressedPart: glass.isSuppressedPart,
                };
            });

            // clear out answerData for future page loads; must occur prior to store save
            this.capabilityQuestionsData.forEach((glass) => {
                glass.answerData = {};
            });

            // save to vuex store as order.damage.capabilityQuestionAnswers (array)
            await this.dispatchStoreAction(this.storeActions.SAVE_CAPABILITY_QUESTION_ANSWERS, capabilityQuestionsAnswersArray, false);

            // get parts from the capabilityQuestionAnswers
            let partsOrQuestions = this.pageData.partsOrQuestions;
            for (let answer of capabilityQuestionsAnswersArray) {
                const correspondingPart = partsOrQuestions.find(partOrQuestion => partOrQuestion.glassLocation === answer.glassLocation);
                const partFromCapabilityQuestionAnswer = (await this.dispatchStoreAction(storeActions.GET_PART_FROM_CAPABILITY_QUESTION_ANSWER, answer.glassLocation, false)).data;

                partsOrQuestions.find(partOrQuestion => partOrQuestion.glassLocation === answer.glassLocation).parts = partFromCapabilityQuestionAnswer;
            }

            this.navigateForward(partsOrQuestions);
        },
        handleAnswerUpdates(answer) {
            // only runs when all questions in a question-chain have been answered
            //      when selectedAnswers updates, user has completed this part's question chain and has a final answer
            //      (does not get run for each invididual question's answer, only when 
            //      all relevent questions for the current part have been answered)

            /*
            answer example format:
            {
                "answerResult": "DD11132",
                "answeredQuestions": [
                    {
                        "questionText": "Is your Grand Cherokee the Laredo model?",
                        "selectedAnswerText": "Yes",
                        "questionNum": 1,
                    }
                ],
                "glassIndex": 0
            }
            */

            // collect a list of the answered questions' numbers, needed later below
            const answeredQuestionIndexes = [];

            // if user has answered a question differently than anything that was preloaded,
            //     we need to clear out any preloaded answers
            this.selectedAnswers = {};

            // loop through every answered question on the currently answered glass part
            answer.answeredQuestions?.forEach((aq) => {

                // keep track of this question number
                answeredQuestionIndexes.push(aq.questionNum);

                const answeredQuestionText = aq.questionText.toUpperCase();
                const answeredQuestionAnswer = aq.selectedAnswerText.toUpperCase();

                // HANDLE DUPLICATE QUESTIONS

                // loop through all glass parts data
                this.capabilityQuestionsData.forEach((glass, gpIndex) => {

                    // only look for duplicates forward... to parts that follow after the currently being answered part
                    if (gpIndex > answer.glass) {

                        let suppressUntil;
                        // reset this glass part, in case user is changing their previous answers
                        glass.answerData = null;
                        glass.isSuppressedPart = null;

                        // loop through this glass part's part questions, looking for a questionText match
                        glass.capabilityQuestions.forEach((pq, pqIndex) => {

                            // clear out any previously set answers
                            pq.answerSelected = null;

                            // clear or set suppressQuestion property for each question
                            if (suppressUntil) {
                                // if suppressUntil has been set, then suppress this question if before it
                                if (pqIndex + 1 < suppressUntil) {
                                    pq.suppressQuestion = true;
                                } else {
                                    pq.suppressQuestion = null;
                                }
                            } else {
                                pq.suppressQuestion = null;
                            }

                            // if these match then we have a duplicate question
                            if (pq.questionText.toUpperCase() === answeredQuestionText) {

                                const thisAnsweredCapabilityQuestion = glass.capabilityQuestions[pqIndex];
                                let matchedAnswer;
                                let rejectedAnswers = []; // set as an array, in case we ever have questions with more than 2 answers...

                                // which one of this capabilityQuestions' answers matches our answer?
                                pq.answers.forEach((ans) => {
                                    if (ans.answerText.toUpperCase() === answeredQuestionAnswer) {
                                        matchedAnswer = ans;
                                        ans.selected = true;
                                    } else {
                                        rejectedAnswers.push(ans);
                                        ans.selected = null;
                                    }
                                });

                                // Update the key to re-render this part's question-chain component
                                this.capabilityQuestionsData[gpIndex].key = this.capabilityQuestionsData[gpIndex].glassLocation + this.capabilityQuestionsData[gpIndex].glassName + Date.now().toString();

                                // handle suppressing downstream in this question chain

                                if (matchedAnswer.nextQuestionSequence) {
                                    // ensure that the question that the accepted answer has set to be next is NOT suppressed
                                    glass.capabilityQuestions[matchedAnswer.nextQuestionSequence - 1].suppressQuestion = null;
                                    // if the duplicate is the 1ST question, then set suppressUntil var to lowest nextQuestion number
                                    if (pqIndex === 0) {
                                        if (!suppressUntil) { suppressUntil = matchedAnswer.nextQuestionSequence }
                                        if (matchedAnswer.nextQuestionSequence < suppressUntil) { suppressUntil = matchedAnswer.nextQuestionSequence }
                                    }
                                }

                                // handle suppressing upstream in this question chain
                                glass.capabilityQuestions.forEach((q) => {
                                    q.answers.forEach((thisAns) => {
                                        // restore any of the answers that formerly led to the duplicated question
                                        if (thisAns.originalNextQuestionSequence === pq.questionSequence) {
                                            // restore original nextQuestionSequence
                                            thisAns.nextQuestionSequence = thisAns.originalNextQuestionSequence;
                                            this.originalNextQuestionSequence = null;
                                            // restore original answerResult
                                            if (thisAns.originalAnswerResult) {
                                                thisAns.answerResult = thisAns.originalAnswerResult;
                                                thisAns.answerResult1 = thisAns.originalAnswerResult;
                                                thisAns.originalAnswerResult = null;
                                            }
                                        }
                                        // search for any answers that lead to the duplicated question
                                        if (thisAns.nextQuestionSequence === pq.questionSequence) {
                                            // update either the nextQuestionSequence or the answerResult
                                            if (matchedAnswer.nextQuestionSequence) {
                                                thisAns.originalNextQuestionSequence = thisAns.nextQuestionSequence;
                                                thisAns.nextQuestionSequence = matchedAnswer.nextQuestionSequence;
                                            } else {
                                                thisAns.originalNextQuestionSequence = thisAns.nextQuestionSequence;
                                                thisAns.nextQuestionSequence = null;
                                                thisAns.originalAnswerResult = thisAns.originalAnswerResult || thisAns.answerResult;
                                                thisAns.answerResult = matchedAnswer.answerResult;
                                                thisAns.answerResult1 = matchedAnswer.answerResult;
                                            }
                                        }
                                    });
                                });

                                // suppress current question
                                thisAnsweredCapabilityQuestion.suppressQuestion = true;

                                const thisGlassPart = "glass" + gpIndex;
                                if (this.foundDuplicateQuestions[thisGlassPart]) {
                                    if (!this.foundDuplicateQuestions[thisGlassPart].includes(thisAnsweredCapabilityQuestion.questionSequence)) {
                                        this.foundDuplicateQuestions[thisGlassPart].push(thisAnsweredCapabilityQuestion.questionSequence);
                                    }
                                } else {
                                    this.foundDuplicateQuestions[thisGlassPart] = [thisAnsweredCapabilityQuestion.questionSequence];
                                }

                                // are there any questions left that are not suppressed?
                                const remainingQuestions = glass.capabilityQuestions.filter((q) => {
                                    return !q.suppressQuestion;
                                });

                                if (remainingQuestions.length < 1) {
                                    // this is the final answer for this glass part

                                    // mark this part as completely answered by adding answerData
                                    const answeredQuestionObj = {
                                        questionText: pq.questionText,
                                        selectedAnswerText: matchedAnswer.answerText,
                                        questionNum: pq.questionSequence,
                                        suppressQuestion: pq.suppressQuestion,
                                    };
                                    // set the answerData as 'already answered'
                                    glass.answerData = {
                                        answerResult: matchedAnswer.nextQuestionSequence ? matchedAnswer.nextQuestionSequence : matchedAnswer.answerResult,
                                        answerResult1: matchedAnswer.nextQuestionSequence ? matchedAnswer.nextQuestionSequence : matchedAnswer.answerResult,
                                        answeredQuestions: [answeredQuestionObj],
                                    };

                                    // suppress this glass because it has an answer
                                    glass.isSuppressedPart = true;
                                }

                            }

                        });

                        // Update the key to re-render this part's question-chain component
                        this.capabilityQuestionsData[gpIndex].key = this.capabilityQuestionsData[gpIndex].glassLocation + this.capabilityQuestionsData[gpIndex].glassName + Date.now().toString();
                        
                    }
                });
            });

            // DETERMINE ANSWERED QUESTIONS LIST FOR THIS GLASS PART

            // look through all (this part's) part questions for any duplicates that were suppressed; 
            //      add them to the list of answered questions if found

            // EX answeredQuestionIndexes: [1,5,11,13]

            // EX this.foundDuplicateQuestions = {
            //     "glassPart1": [1],
            //     "glassPart2": [7, 10]
            // };

            const thisPartsDupes = this.foundDuplicateQuestions["glass" + answer.glassIndex];
            const completeAnsweredQuestions = answer.answeredQuestions ? [...answer.answeredQuestions] : [];
            const glassPartAnswered = this.capabilityQuestionsData[answer.glassIndex];

            thisPartsDupes?.forEach((dupe) => {
                // dupe is a single integer
                const dupeQuestion = glassPartAnswered.capabilityQuestions[dupe - 1];
                const dupeQuestionAnswer = dupeQuestion.answers.find((q) => q.selected === true);

                glassPartAnswered.capabilityQuestions.forEach((q) => {
                    let includeThisDupeInAnsweredQuestions = false;

                    // did one of the answers of this question point to the duplicated question?
                    q.answers.forEach((a) => {
                        if ((dupe === a.originalNextQuestionSequence) &&
                            (answeredQuestionIndexes.includes(q.questionSequence)) &&
                            (a.answerText.toUpperCase() === dupeQuestionAnswer.answerText.toUpperCase())) {
                            includeThisDupeInAnsweredQuestions = true;
                        }
                    });

                    // is this q.questionSequence listed as the duplicated question's nextQuestionSequence?
                    if ((q.questionSequence === dupeQuestionAnswer.nextQuestionSequence) && (answeredQuestionIndexes.includes(q.questionSequence))) {
                        includeThisDupeInAnsweredQuestions = true;
                    }

                    if (includeThisDupeInAnsweredQuestions) {
                        completeAnsweredQuestions.push({
                            questionNum: dupeQuestion.questionSequence,
                            questionText: dupeQuestion.questionText,
                            selectedAnswerText: dupeQuestionAnswer.answerText,
                            suppressQuestion: dupeQuestion.suppressQuestion,
                        });
                    }
                });
            });

            // make sure there are no duplicated dupes in the list...
            const foundInCompleteAnsweredQuestions = new Set();
            let filteredCompleteAnsweredQuestions = completeAnsweredQuestions.filter(el => {
                const duplicate = foundInCompleteAnsweredQuestions.has(el.questionText);
                foundInCompleteAnsweredQuestions.add(el.questionText);
                return !duplicate;
            });
            filteredCompleteAnsweredQuestions = filteredCompleteAnsweredQuestions.sort((a, b) => a.questionNum - b.questionNum);

            const selectedAnswerResult1 = answer.answerResult;
            const selectedAnswerResult2 = this.getCorrespondingAnswerResult2(selectedAnswerResult1);

            // set final answer data for the current answered glass part
            glassPartAnswered.answerData = {
                answerResult: selectedAnswerResult1,
                answerResult1: selectedAnswerResult1,
                answerResult2: selectedAnswerResult2,
                answeredQuestions: filteredCompleteAnsweredQuestions,
            }

            // this part has been fully answered, so advance to next part's question chain
            for (let i = answer.glassIndex + 1; i < this.capabilityQuestionsData.length; i++) {
                // if this part has not yet been fully answered, then make it the current part
                if (!this.capabilityQuestionsData[i].answerData?.answerResult) {
                    this.currentQuestionChainIndex = i;
                    break;
                }
            }
        },
        // TODO I'm sure there's a better/simplier way to do this that I'm missing but my brain's fried
        // Find the part that has some answer that has an answerResult matching the selected answerResult, then get the corresponding answerResult2
        getCorrespondingAnswerResult2(answerResult1) {
            return this.capabilityQuestionsData
                .find(part => part.capabilityQuestions.some(question => question.answers.some(answer => answer.answerResult == answerResult1)))     // found part
                .capabilityQuestions.find(question => question.answers.some(answer => answer.answerResult == answerResult1))    // found question
                .answers.find(answer => answer.answerResult == answerResult1)
                .answerResult2;
        },
        loadInitialCapabilityQuestionsData() {
            // are there alreadyAnsweredQuestions?
            const alreadyAnsweredQuestions = store.getters.damage.capabilityQuestionAnswers;

            this.capabilityQuestionsData = this.pageData.partsOrQuestions.filter(x => x.capabilityQuestions).map((glass, i) => {
                glass.key = glass.glassLocation + "-" + glass.glassName;
                this.selectedAnswers[glass.key] = [];
                if (!alreadyAnsweredQuestions) {
                    glass.answerData = null;
                }

                alreadyAnsweredQuestions?.forEach((savedPart) => {

                    if (!savedPart.glassLocation || !savedPart.glassName || !savedPart.answeredQuestions || !savedPart.result) {
                        return;
                    }
                    if (glass.glassLocation === savedPart.glassLocation && glass.glassName === savedPart.glassName) {
                        let answerString = "";

                        // matches; loop through answeredQuestions for matches
                        savedPart.answeredQuestions.forEach((aq) => {
                            if (!aq.questionNum || !aq.selectedAnswerText) {
                                return;
                            }
                            // determine which answer was previously chosen
                            const theAns = glass.capabilityQuestions[aq.questionNum - 1].answers.find((a) => {
                                return a.answerText.toUpperCase() === aq.selectedAnswerText.toUpperCase();
                            });
                            if (theAns.nextQuestionSequence) {
                                answerString = `${aq.questionNum}|nextQuestion|${theAns.nextQuestionSequence}|${theAns.answerText}`
                            } else {
                                answerString = `${aq.questionNum}|answer|${theAns.answerResult}|${theAns.answerText}`
                            }

                            // mark this partQuestion as answered (question-chain will read this)
                            glass.capabilityQuestions[aq.questionNum - 1].answerSelected = answerString;
                            // mark this partQuestion as duplicate if it is (question-chain will read this)
                            if (aq.suppressQuestion) {
                                glass.capabilityQuestions[aq.questionNum - 1].suppressQuestion = true;
                            }
                        });

                        // advance the currentQuestionChainIndex
                        this.currentQuestionChainIndex = i;

                        // add answerData to current glass
                        glass.answerData = {
                            answerResult: savedPart.result,
                            answerResult1: savedPart.result,
                            answerResult2: this.getCorrespondingAnswerResult2(savedPart.result),
                            answeredQuestions: savedPart.answeredQuestions
                        }
                    }

                });

                // Set up watch for each set of glass questions, which gets updated when all questions for a glass have been answered
                this.$watch("selectedAnswers." + glass.key, (newValue) => {
                    if (newValue) {
                        this.handleAnswerUpdates(newValue);
                    }
                }, { deep: true })

                return glass;
            });

        },
    },
    components: {
        funnelHeader,
        vehicleBanner,
        alert,
        questionChain,
        funnelSubHeader,
        funnelFooter,
        loadingModal,
        Form,
    },
};
</script>

<style lang="scss">
.capability-questions {
    .question-text {
        margin-bottom: .5rem;

        span {
            text-align: left;
        }
    }
}
</style>
