import {
  ReturnProcessAnswers,
  ReturnProcessQuestion,
  ReturnProcessQuestionType,
} from '../../models';
import { ReturnProcessQuestionSchema } from '../../questions';
import { ReturnProcessChecklistAnswerSchema } from '../../schemas';

/**
 * Determines which questions in a return process flow are currently active based on previous answers.
 * The function handles different question types and their dependencies, preventing infinite loops
 * from cyclic dependencies.
 *
 * @param questions - Array of possible questions in the return process
 * @param answers - Current answers provided for the questions
 * @returns Array of currently active questions based on the provided answers
 * @throws {Error} If a cyclic dependency is detected in the question flow
 * @throws {PropertyNullOrUndefinedError} If questions or answers parameters are null/undefined
 *
 * @example
 * ```typescript
 * const questions = [
 *   { key: 'condition', type: ReturnProcessQuestionType.Select, options: [...] },
 *   { key: 'reason', type: ReturnProcessQuestionType.Select, options: [...] }
 * ];
 * const answers = { condition: 'damaged' };
 *
 * const activeQuestions = activeReturnProcessQuestions(questions, answers);
 * ```
 */
export function activeReturnProcessQuestions(
  questions: ReturnProcessQuestion[],
  answers: ReturnProcessAnswers,
): ReturnProcessQuestion[] {
  return internalActiveReturnProcessQuestions(questions, answers).questions;
}

export function internalActiveReturnProcessQuestions(
  questions: ReturnProcessQuestion[],
  answers: ReturnProcessAnswers,
): { questions: ReturnProcessQuestion[]; nextQuestion?: string } {
  let questionKey: string | undefined = questions[0].key;
  const result: ReturnProcessQuestion[] = [];
  const visited = new Set<string>();

  while (questionKey) {
    if (visited.has(questionKey)) {
      throw new Error('Cyclic question dependency detected');
    }
    visited.add(questionKey);

    const question = questions.find((q) => q.key === questionKey);
    if (question) {
      result.push(question);
      questionKey = undefined;

      const schema = ReturnProcessQuestionSchema[question.key];

      const parseResult = schema.safeParse(answers[question.key]);

      if (!parseResult.success) {
        break;
      }

      if (question.type === ReturnProcessQuestionType.Select) {
        const option = question.options.find(
          (o) => o.value === answers[question.key],
        );
        questionKey = option?.nextQuestion;
      } else if (question.type === ReturnProcessQuestionType.Product) {
        questionKey = answers[question.key] ? question.nextQuestion : undefined;
      } else if (question.type === ReturnProcessQuestionType.Checklist) {
        const answer = ReturnProcessChecklistAnswerSchema.optional().parse(
          answers[question.key],
        );

        if ((answer && answer.options?.length) || (answer && answer.other)) {
          questionKey = question.nextQuestion;
        } else {
          questionKey = undefined;
        }
      } else if (question.type === ReturnProcessQuestionType.Info) {
        questionKey = question.nextQuestion;
      } else if (question.type === ReturnProcessQuestionType.Group) {
        const groupQuestions = internalActiveReturnProcessQuestions(
          question.questions,
          answers,
        );

        const questionGroup = {
          ...question,
          questions: groupQuestions.questions,
        };

        result[result.length - 1] = questionGroup; // Replace the group question with the updated one

        questionKey = groupQuestions.nextQuestion; // No next question for group type
      } else {
        break;
      }
    } else {
      break;
    }
  }

  return { questions: result, nextQuestion: questionKey };
}
