import { useCallback, useState } from 'react';

import {
  FormSkipTypes,
  InputType,
  type Question,
  type Skip,
  type Step,
} from 'src/types/forms';

type UseSurveyBranchingReturn = {
  currentStep: number;
  currentSection: Step | undefined;
  isFirstStep: boolean;
  checkIsLastStep: (formValues: Record<string, unknown>) => boolean;
  goToNextStep: (formValues: Record<string, unknown>) => void;
  goToPreviousStep: () => void;
  resetBranchFields: (
    formValues: Record<string, unknown>,
    resetField: (name: string) => void,
  ) => void;
};

const SKIPPABLE_INPUT_TYPES = new Set<string>([
  InputType.CHECKBOX,
  InputType.DROPDOWN,
]);

const findStepIndexById = (steps: Step[], stepId: number): number =>
  steps.findIndex(s => s.id === stepId);

/**
 * Given the current section's questions and the form values, resolve which
 * option-level skip should apply. Returns the skip from the last question
 * (by position) that has a selected option with a skip configured — later
 * questions take priority over earlier ones.
 */
const resolveOptionSkip = (
  questions: Question[],
  formValues: Record<string, unknown>,
  stepIndex: number,
  sectionId: number,
): Skip | null => {
  let result: Skip | null = null;

  for (let qIdx = 0; qIdx < questions.length; qIdx++) {
    const question = questions[qIdx];
    if (!SKIPPABLE_INPUT_TYPES.has(question.inputType)) continue;
    if (!question.options?.some(o => o.skip)) continue;

    const fieldName = `${stepIndex}-${qIdx}-${question.inputType}-${question.multipleAnswer}-${question.id}-${sectionId}`;
    const value = formValues[fieldName];
    if (value == null) continue;

    if (question.inputType === InputType.CHECKBOX) {
      const selectedIndex = typeof value === 'number' ? value : Number(value);
      const selectedOption = question.options?.[selectedIndex];
      if (selectedOption?.skip) result = selectedOption.skip;
    } else if (question.inputType === InputType.DROPDOWN) {
      const label =
        typeof value === 'object' && value !== null && 'label' in value
          ? (value as { label: string }).label
          : String(value);
      const selectedOption = question.options?.find(
        o => o.description === label,
      );
      if (selectedOption?.skip) result = selectedOption.skip;
    }
  }

  return result;
};

/**
 * Determine the effective skip for a step: option-level skip takes priority,
 * then step-level skip, then null (go to next sequential step).
 */
const resolveEffectiveSkip = (
  step: Step,
  formValues: Record<string, unknown>,
  stepIndex: number,
): Skip | null => {
  const optionSkip = resolveOptionSkip(
    step.questions ?? [],
    formValues,
    stepIndex,
    step.id,
  );
  if (optionSkip) return optionSkip;
  return step.skip ?? null;
};

const computeIsLastStep = (
  steps: Step[],
  stepIndex: number,
  hasBranching: boolean,
  formValues: Record<string, unknown>,
): boolean => {
  if (!steps?.length) return true;
  if (!hasBranching) return stepIndex === steps.length - 1;
  if (stepIndex >= steps.length - 1) return true;

  const step = steps[stepIndex];
  if (!step) return true;

  const effectiveSkip = resolveEffectiveSkip(step, formValues, stepIndex);
  if (!effectiveSkip) return stepIndex === steps.length - 1;
  return effectiveSkip.skipType === FormSkipTypes.END;
};

const useSurveyBranching = (
  steps: Step[] | undefined,
  hasBranching: boolean,
): UseSurveyBranchingReturn => {
  const safeSteps = steps ?? [];
  const [navigationStack, setNavigationStack] = useState<number[]>([0]);

  const currentStep = navigationStack[navigationStack.length - 1] ?? 0;
  const currentSection = safeSteps[currentStep];

  const isFirstStep = navigationStack.length <= 1;

  const checkIsLastStep = useCallback(
    (formValues: Record<string, unknown>) =>
      computeIsLastStep(safeSteps, currentStep, hasBranching, formValues),
    [safeSteps, currentStep, hasBranching],
  );

  const goToNextStep = useCallback(
    (formValues: Record<string, unknown>) => {
      if (!hasBranching) {
        setNavigationStack(prev => [...prev, currentStep + 1]);
        return;
      }

      const step = safeSteps[currentStep];
      if (!step) return;

      const effectiveSkip = resolveEffectiveSkip(step, formValues, currentStep);

      if (!effectiveSkip) {
        setNavigationStack(prev => [...prev, currentStep + 1]);
        return;
      }

      if (effectiveSkip.skipType === FormSkipTypes.STEP) {
        const destinationIndex = findStepIndexById(
          safeSteps,
          effectiveSkip.destinationStepId,
        );
        if (destinationIndex !== -1) {
          setNavigationStack(prev => [...prev, destinationIndex]);
        }
        return;
      }

      if (effectiveSkip.skipType === FormSkipTypes.END) {
        return;
      }
    },
    [currentStep, hasBranching, safeSteps],
  );

  const goToPreviousStep = useCallback(() => {
    setNavigationStack(prev => {
      if (prev.length <= 1) return prev;
      return prev.slice(0, -1);
    });
  }, []);

  const resetBranchFields = useCallback(
    (
      formValues: Record<string, unknown>,
      resetField: (name: string) => void,
    ) => {
      if (!hasBranching) return;

      const step = safeSteps[currentStep];
      if (!step?.questions) return;

      const destinationStepIds = new Set<number>();
      for (const question of step.questions) {
        if (!question.options) continue;
        for (const option of question.options) {
          if (option.skip?.destinationStepId != null) {
            destinationStepIds.add(option.skip.destinationStepId);
          }
        }
      }

      if (destinationStepIds.size === 0) return;

      for (const fieldKey of Object.keys(formValues)) {
        const parts = fieldKey.split('-');
        const sectionId = Number(parts[parts.length - 1]);
        if (destinationStepIds.has(sectionId)) {
          resetField(fieldKey);
        }
      }
    },
    [currentStep, hasBranching, safeSteps],
  );

  return {
    currentStep,
    currentSection,
    isFirstStep,
    checkIsLastStep,
    goToNextStep,
    goToPreviousStep,
    resetBranchFields,
  };
};

export default useSurveyBranching;
