"use client";

import {
  createContext,
  type ReactNode,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";

type QuestionProgressContextValue = {
  answered: number;
  total: number;
  isCompleted: boolean;
  markQuestionPassed: (questionIndex: number) => void;
};

const QuestionProgressContext = createContext<QuestionProgressContextValue>({
  answered: 0,
  total: 0,
  isCompleted: false,
  markQuestionPassed: () => {},
});

export function useQuestionProgress() {
  return useContext(QuestionProgressContext);
}

type QuestionProgressTrackerProps = {
  children: ReactNode;
  total: number;
};

function isQuestionAnswered(question: Element) {
  const explicitAnsweredState =
    question.getAttribute("data-question-answered") ??
    question
      .querySelector("[data-question-answered]")
      ?.getAttribute("data-question-answered");

  if (explicitAnsweredState === "true") {
    return true;
  }

  if (explicitAnsweredState === "false") {
    return false;
  }

  const inputs = Array.from(
    question.querySelectorAll<
      HTMLInputElement | HTMLSelectElement | HTMLTextAreaElement
    >("input, select, textarea"),
  );

  return inputs.some((input) => {
    if (input instanceof HTMLInputElement) {
      if (input.type === "checkbox" || input.type === "radio") {
        return input.checked;
      }

      if (input.type === "file") {
        return input.files !== null && input.files.length > 0;
      }
    }

    return input.value.trim().length > 0;
  });
}

function isQuestionOptional(question: Element) {
  return (
    question.getAttribute("data-question-optional") === "true" ||
    question.getAttribute("data-question-required") === "false"
  );
}

function isCurrentQuestion(question: Element) {
  return question.closest('[aria-current="step"]') !== null;
}

export function QuestionProgressTracker({
  children,
  total: initialTotal,
}: QuestionProgressTrackerProps) {
  const containerRef = useRef<HTMLDivElement>(null);
  const [answered, setAnswered] = useState(0);
  const [total, setTotal] = useState(initialTotal);
  const [passedQuestionIndexes, setPassedQuestionIndexes] = useState<
    Set<number>
  >(() => new Set());
  const safeTotal = Math.max(total, 0);
  const progress = safeTotal > 0 ? (answered / safeTotal) * 100 : 0;
  const isCompleted = safeTotal === 0 || answered >= safeTotal;

  const markQuestionPassed = useCallback((questionIndex: number) => {
    setPassedQuestionIndexes((currentIndexes) => {
      if (currentIndexes.has(questionIndex)) {
        return currentIndexes;
      }

      return new Set(currentIndexes).add(questionIndex);
    });
  }, []);

  const updateProgress = useCallback(() => {
    const container = containerRef.current;

    if (!container) {
      return;
    }

    const activeQuestions = Array.from(
      container.querySelectorAll("[data-question-disabled]"),
    ).filter((el) => el.getAttribute("data-question-disabled") !== "true");

    const nextTotal = activeQuestions.length;
    const nextAnswered = activeQuestions.filter((question) => {
      const questionIndex = Number(
        question.getAttribute("data-question-index"),
      );
      return (
        isQuestionAnswered(question) ||
        passedQuestionIndexes.has(questionIndex) ||
        (isQuestionOptional(question) && isCurrentQuestion(question))
      );
    }).length;

    setTotal(nextTotal);
    setAnswered(nextAnswered);
  }, [passedQuestionIndexes]);

  useEffect(() => {
    const container = containerRef.current;

    if (!container) {
      return;
    }

    const observer = new MutationObserver(updateProgress);

    observer.observe(container, {
      attributeFilter: [
        "aria-current",
        "data-question-answered",
        "data-question-disabled",
        "data-question-optional",
        "data-question-required",
      ],
      attributes: true,
      subtree: true,
    });

    updateProgress();

    return () => observer.disconnect();
  }, [updateProgress]);

  const contextValue = useMemo(
    () => ({ answered, total: safeTotal, isCompleted, markQuestionPassed }),
    [answered, safeTotal, isCompleted, markQuestionPassed],
  );

  return (
    <QuestionProgressContext.Provider value={contextValue}>
      <div
        ref={containerRef}
        className="flex flex-col flex-1 min-h-0"
        onChange={updateProgress}
        onInput={updateProgress}
      >
        <div className="w-full shrink-0 px-[17px] pt-2 pb-[24px]">
          <div
            aria-label={`Answered questions: ${answered} of ${safeTotal}`}
            aria-valuemax={safeTotal}
            aria-valuemin={0}
            aria-valuenow={answered}
            role="progressbar"
            className="w-full bg-[#F7F1F0]"
          >
            <div className="mb-[7px] flex items-center justify-between group-10 leading-none font-normal text-[#747474]">
              <span>fields to complete</span>
              <span>
                {answered} /{safeTotal}
              </span>
            </div>
            <div className="relative h-[6px] rounded-full bg-[#D8D8D8]">
              <div
                className={[
                  "absolute top-1/2 h-[10px] -translate-y-1/2 rounded-full bg-[#F2465F] transition-[width] duration-200",
                  answered > 0 ? "min-w-[37px]" : "",
                ]
                  .filter(Boolean)
                  .join(" ")}
                style={{ width: `${progress}%` }}
              />
            </div>
          </div>
        </div>

        {children}
      </div>
    </QuestionProgressContext.Provider>
  );
}

export default QuestionProgressTracker;
