"use client";

import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { Castle, Check } from "lucide-react";
import { Button } from "@/components/ui/Button";
import { Input } from "@/components/ui/Input";
import { Select } from "@/components/ui/Select";
import { StarRatingScale } from "@/components/features/surveys/StarRatingScale";
import { MultiSelectDropdown } from "@/components/features/surveys/MultiSelectDropdown";
import { isMultiSelectDropdown } from "@/lib/utils/dropdown-select";
import { submitSurveyResponse } from "@/lib/actions/filler";
import {
  prepareQuestionsForFiller,
  resolveNextQuestionId,
  calcFillerProgress,
  getVisibleQuestions,
  pruneHiddenAnswers,
} from "@/lib/utils/survey-logic";
import { safeSurveyRedirectUrl } from "@/lib/utils/safe-redirect";
import { validateFillerAnswer } from "@/lib/utils/filler-validation";
import {
  normalizeDisplayConfig,
  splitQuestionsByPageBreaks,
} from "@/lib/utils/survey-display";
import {
  resolveThankYouScreen,
  type ResolvedThankYou,
} from "@/lib/utils/thank-you-screen";
import type {
  FillerSurveyData,
  FillerQuestion,
  FillerAnswers,
  FillerAnswerValue,
} from "@/types/filler";
import { cn } from "@/lib/utils/cn";

type Phase = "welcome" | "questions" | "thanks";

interface SurveyFillerProps {
  data: FillerSurveyData;
  embed?: boolean;
  isPreview?: boolean;
}

function isFilledAnswer(value: FillerAnswerValue): boolean {
  if (value === null || value === undefined || value === "") return false;
  if (Array.isArray(value) && value.length === 0) return false;
  if (
    typeof value === "object" &&
    !Array.isArray(value) &&
    Object.keys(value).length === 0
  ) {
    return false;
  }
  return true;
}

export function SurveyFiller({ data, embed = false, isPreview = false }: SurveyFillerProps) {
  const questions = useMemo(
    () => prepareQuestionsForFiller(data.questions),
    [data.questions]
  );
  const display = useMemo(
    () => normalizeDisplayConfig(data.displayConfig),
    [data.displayConfig]
  );
  const layout = display.layout;

  const [phase, setPhase] = useState<Phase>(
    data.welcomeScreenConfig ? "welcome" : "questions"
  );
  const [history, setHistory] = useState<string[]>([]);
  const [currentId, setCurrentId] = useState<string | null>(
    questions[0]?.id ?? null
  );
  const [pageIndex, setPageIndex] = useState(0);
  const [answers, setAnswers] = useState<FillerAnswers>({});
  const answersRef = useRef(answers);
  answersRef.current = answers;
  const [error, setError] = useState<string | null>(null);
  const [fieldErrors, setFieldErrors] = useState<Record<string, string>>({});
  const [submitting, setSubmitting] = useState(false);
  const [thankYouView, setThankYouView] = useState<ResolvedThankYou | null>(null);
  const startedAtRef = useRef(new Date().toISOString());

  const visibleQuestions = useMemo(
    () => getVisibleQuestions(questions, answers),
    [questions, answers]
  );
  const visibleIds = useMemo(
    () => new Set(visibleQuestions.map((q) => q.id)),
    [visibleQuestions]
  );

  const allPages = useMemo(
    () => splitQuestionsByPageBreaks(questions, display.pageBreakAfterIds),
    [questions, display.pageBreakAfterIds]
  );
  const pagesWithContent = useMemo(
    () =>
      allPages
        .map((qs, i) => ({
          i,
          questions: qs.filter((q) => visibleIds.has(q.id)),
        }))
        .filter((p) => p.questions.length > 0),
    [allPages, visibleIds]
  );

  const currentPage =
    pagesWithContent.find((p) => p.i === pageIndex) ??
    pagesWithContent.find((p) => p.i > pageIndex) ??
    [...pagesWithContent].reverse().find((p) => p.i < pageIndex) ??
    null;
  const currentPageQuestions = currentPage?.questions ?? [];
  const activePageIndex = currentPage?.i ?? pageIndex;

  const currentQuestion = questions.find((q) => q.id === currentId) ?? null;
  const currentIndex = currentQuestion
    ? questions.findIndex((q) => q.id === currentQuestion.id)
    : 0;

  const answeredVisible = visibleQuestions.filter((q) =>
    isFilledAnswer(answers[q.id] ?? null)
  ).length;

  const progress = useMemo(() => {
    if (phase === "thanks") return 100;
    if (layout === "single_page") {
      return calcFillerProgress(
        Math.max(0, answeredVisible - 1),
        Math.max(1, visibleQuestions.length)
      );
    }
    if (layout === "grouped") {
      const idx = Math.max(
        0,
        pagesWithContent.findIndex((p) => p.i === activePageIndex)
      );
      return calcFillerProgress(idx, Math.max(1, pagesWithContent.length));
    }
    return calcFillerProgress(currentIndex, questions.length);
  }, [
    phase,
    layout,
    answeredVisible,
    visibleQuestions.length,
    pagesWithContent,
    activePageIndex,
    currentIndex,
    questions.length,
  ]);

  const safeRedirectUrl = useMemo(
    () => safeSurveyRedirectUrl(data.redirectUrl),
    [data.redirectUrl]
  );

  const setAnswer = useCallback(
    (questionId: string, value: FillerAnswerValue) => {
      setAnswers((prev) => {
        const merged = { ...prev, [questionId]: value };
        const vis = getVisibleQuestions(questions, merged);
        return pruneHiddenAnswers(merged, new Set(vis.map((q) => q.id)));
      });
      setError(null);
      setFieldErrors((prev) => {
        if (!prev[questionId]) return prev;
        const next = { ...prev };
        delete next[questionId];
        return next;
      });
    },
    [questions]
  );

  const finishSurvey = useCallback(
    async (finalAnswers: FillerAnswers) => {
      setSubmitting(true);
      try {
        const payload = getVisibleQuestions(questions, finalAnswers)
          .map((q) => ({ questionId: q.id, value: finalAnswers[q.id] ?? null }))
          .filter(({ value }) => isFilledAnswer(value as FillerAnswerValue));

        if (!isPreview) {
          const result = await submitSurveyResponse({
            slug: data.slug,
            collectorId: data.collectorId,
            surveyId: data.surveyId,
            startedAt: startedAtRef.current,
            answers: payload,
          });

          if (!result.success) {
            setError(result.error ?? "Submission failed");
            return;
          }
        } else {
          await new Promise((r) => setTimeout(r, 600));
        }

        const resolved = resolveThankYouScreen(
          data.thankYouScreenConfig,
          finalAnswers
        );
        setThankYouView(
          safeRedirectUrl && !isPreview
            ? {
                ...resolved,
                description: `${resolved.description} Redirecting you shortly…`,
              }
            : resolved
        );
        setPhase("thanks");
        if (safeRedirectUrl && !isPreview) {
          setTimeout(() => {
            window.location.href = safeRedirectUrl;
          }, 3000);
        }
      } catch {
        setError("Submission failed");
      } finally {
        setSubmitting(false);
      }
    },
    [questions, isPreview, data, safeRedirectUrl]
  );

  const validateQuestions = useCallback((qs: FillerQuestion[]) => {
    const nextErrors: Record<string, string> = {};
    for (const q of qs) {
      const validation = validateFillerAnswer(
        q,
        answersRef.current[q.id] ?? null
      );
      if (!validation.valid) {
        nextErrors[q.id] = validation.message ?? "Please answer this question.";
      }
    }
    setFieldErrors(nextErrors);
    if (Object.keys(nextErrors).length > 0) {
      setError("Please complete the required questions on this page.");
      return false;
    }
    setError(null);
    return true;
  }, []);

  const goNext = useCallback(async () => {
    if (layout === "single_page") {
      if (!validateQuestions(visibleQuestions)) return;
      await finishSurvey(answersRef.current);
      return;
    }

    if (layout === "grouped") {
      if (!validateQuestions(currentPageQuestions)) return;
      const currentPos = pagesWithContent.findIndex(
        (p) => p.i === activePageIndex
      );
      const nextPage = pagesWithContent[currentPos + 1];
      if (!nextPage) {
        await finishSurvey(answersRef.current);
        return;
      }
      setHistory((h) => [...h, String(activePageIndex)]);
      setPageIndex(nextPage.i);
      setError(null);
      setFieldErrors({});
      return;
    }

    if (!currentQuestion) return;

    const answer = answersRef.current[currentQuestion.id] ?? null;
    const validation = validateFillerAnswer(currentQuestion, answer);
    if (!validation.valid) {
      setError(validation.message ?? "Please answer this question.");
      return;
    }

    const next = resolveNextQuestionId(currentQuestion, answer, questions);

    if (next === "end" || next === null) {
      await finishSurvey({
        ...answersRef.current,
        [currentQuestion.id]: answer,
      });
      return;
    }

    setHistory((h) => [...h, currentQuestion.id]);
    setCurrentId(next);
    setError(null);
  }, [
    layout,
    validateQuestions,
    visibleQuestions,
    finishSurvey,
    currentPageQuestions,
    pagesWithContent,
    activePageIndex,
    currentQuestion,
    questions,
  ]);

  const goBack = useCallback(() => {
    if (history.length === 0) return;
    const prev = history[history.length - 1];
    setHistory((h) => h.slice(0, -1));
    if (layout === "grouped") {
      setPageIndex(Number(prev));
    } else {
      setCurrentId(prev);
    }
    setError(null);
    setFieldErrors({});
  }, [history, layout]);

  useEffect(() => {
    if (layout !== "grouped" || phase !== "questions") return;
    const hasCurrent = pagesWithContent.some((p) => p.i === pageIndex);
    if (hasCurrent) return;
    const next = pagesWithContent.find((p) => p.i > pageIndex);
    const prev = [...pagesWithContent].reverse().find((p) => p.i < pageIndex);
    const fallback = next ?? prev ?? pagesWithContent[0];
    if (fallback) setPageIndex(fallback.i);
  }, [layout, phase, pagesWithContent, pageIndex]);

  useEffect(() => {
    function onKeyDown(e: KeyboardEvent) {
      if (phase !== "questions" || layout !== "one_per_page" || !currentQuestion) {
        return;
      }

      if (document.activeElement?.tagName === "TEXTAREA") {
        if (e.key === "Enter" && e.ctrlKey) {
          e.preventDefault();
          void goNext();
        }
        return;
      }

      if (e.key === "Enter") {
        e.preventDefault();
        void goNext();
      }

      const n = parseInt(e.key, 10);
      if (Number.isNaN(n)) return;

      if (
        (currentQuestion.type === "multiple_choice" ||
          (currentQuestion.type === "dropdown" &&
            !isMultiSelectDropdown(
              currentQuestion.type,
              currentQuestion.optionsConfig
            ))) &&
        currentQuestion.optionsConfig?.choices
      ) {
        const choices = currentQuestion.optionsConfig.choices;
        if (n >= 1 && n <= choices.length) {
          setAnswer(currentQuestion.id, choices[n - 1]);
        }
      }

      if (
        (currentQuestion.type === "checkbox" ||
          isMultiSelectDropdown(
            currentQuestion.type,
            currentQuestion.optionsConfig
          )) &&
        currentQuestion.optionsConfig?.choices
      ) {
        const choices = currentQuestion.optionsConfig.choices;
        if (n >= 1 && n <= choices.length) {
          const opt = choices[n - 1];
          const current = Array.isArray(answers[currentQuestion.id])
            ? (answers[currentQuestion.id] as string[])
            : [];
          const next = current.includes(opt)
            ? current.filter((c) => c !== opt)
            : [...current, opt];
          setAnswer(currentQuestion.id, next);
        }
      }

      if (currentQuestion.type === "nps" && n >= 0 && n <= 10) {
        setAnswer(currentQuestion.id, n);
      }
    }

    window.addEventListener("keydown", onKeyDown);
    return () => window.removeEventListener("keydown", onKeyDown);
  }, [phase, currentQuestion, goNext, setAnswer, answers, layout]);

  const isLastStep =
    layout === "single_page" ||
    (layout === "grouped" &&
      pagesWithContent.findIndex((p) => p.i === activePageIndex) >=
        pagesWithContent.length - 1) ||
    (layout === "one_per_page" &&
      !!currentQuestion &&
      (resolveNextQuestionId(
        currentQuestion,
        answers[currentQuestion.id] ?? null,
        questions
      ) === "end" ||
        resolveNextQuestionId(
          currentQuestion,
          answers[currentQuestion.id] ?? null,
          questions
        ) === null));

  const remainingEstimate =
    layout === "single_page"
      ? Math.max(1, visibleQuestions.length - answeredVisible) * 30
      : layout === "grouped"
        ? Math.max(
            1,
            pagesWithContent.length -
              Math.max(
                0,
                pagesWithContent.findIndex((p) => p.i === activePageIndex)
              )
          ) * 30
        : Math.max(1, questions.length - currentIndex) * 30;

  const statusLabel =
    layout === "single_page"
      ? `${answeredVisible} of ${visibleQuestions.length} answered`
      : layout === "grouped"
        ? `Page ${
            Math.max(1, pagesWithContent.findIndex((p) => p.i === activePageIndex) + 1)
          } of ${Math.max(1, pagesWithContent.length)}`
        : `Question ${currentIndex + 1} of ${questions.length}`;

  const showingQuestions =
    layout === "single_page"
      ? visibleQuestions
      : layout === "grouped"
        ? currentPageQuestions
        : currentQuestion
          ? [currentQuestion]
          : [];

  return (
    <div
      className={cn(
        "flex min-h-screen items-center justify-center bg-gradient-to-br from-[#EFF6FF] to-bg p-4 sm:p-10",
        embed && "min-h-0 bg-white p-0"
      )}
    >
      <div
        className={cn(
          "w-full overflow-hidden rounded-[18px] bg-white shadow-lg",
          layout === "single_page" ||
            (layout === "grouped" && showingQuestions.length > 1)
            ? "max-w-[680px]"
            : "max-w-[600px]",
          embed && "max-w-none rounded-none shadow-none"
        )}
      >
        <div className="border-b border-border px-5 py-4 sm:px-[30px] sm:py-[22px]">
          <div className="mb-3.5 flex items-center gap-2 text-sm font-extrabold text-primary">
            <Castle className="h-4 w-4 text-accent" />
            {data.title}
          </div>
          <div className="h-[7px] overflow-hidden rounded-[5px] bg-[#F1F5F9]">
            <div
              className="h-full rounded-[5px] bg-gradient-to-r from-primary to-accent transition-all duration-300"
              style={{ width: `${phase === "thanks" ? 100 : progress}%` }}
            />
          </div>
          {phase === "questions" && (
            <div className="mt-2 flex justify-between font-mono text-[11.5px] text-muted">
              <span>{statusLabel}</span>
              <span>~{remainingEstimate}s left</span>
            </div>
          )}
        </div>

        <div className="px-5 py-8 sm:px-[34px] sm:py-[38px]">
          {phase === "welcome" && (
            <WelcomeStep
              title={data.welcomeScreenConfig?.title ?? data.title}
              description={
                data.welcomeScreenConfig?.description ??
                "Help us improve. This survey takes about 2 minutes."
              }
              buttonLabel={
                data.welcomeScreenConfig?.buttonLabel ?? "Start Survey"
              }
              onStart={() => {
                startedAtRef.current = new Date().toISOString();
                setPhase("questions");
              }}
            />
          )}

          {phase === "questions" && showingQuestions.length > 0 && (
            <div className="space-y-8">
              {showingQuestions.map((q) => {
                const index = questions.findIndex((item) => item.id === q.id);
                return (
                  <QuestionStep
                    key={q.id}
                    question={q}
                    index={index}
                    value={answers[q.id] ?? null}
                    onChange={(v) => setAnswer(q.id, v)}
                    error={
                      fieldErrors[q.id] ??
                      (layout === "one_per_page" ? error : null)
                    }
                    compact={showingQuestions.length > 1}
                  />
                );
              })}
              {layout !== "one_per_page" && error && (
                <p className="text-sm text-danger">{error}</p>
              )}
            </div>
          )}

          {phase === "thanks" && (
            <ThankYouStep
              emoji={thankYouView?.emoji ?? "✅"}
              title={thankYouView?.title ?? "Thank you!"}
              description={
                thankYouView?.description ??
                "Your response has been recorded."
              }
            />
          )}
        </div>

        {phase === "questions" && (
          <div className="flex flex-col gap-3 border-t border-border px-5 py-4 sm:flex-row sm:items-center sm:justify-between sm:px-[34px] sm:py-[18px]">
            <span className="hidden items-center gap-1.5 text-[11.5px] text-muted sm:flex">
              {layout === "one_per_page" ? (
                <>
                  <kbd className="rounded border border-border px-1.5 py-0.5 font-mono text-[11px]">
                    Enter ↵
                  </kbd>
                  for next
                </>
              ) : (
                "Skip logic hides questions you don’t need to answer"
              )}
            </span>
            <div className="flex w-full gap-2 sm:w-auto">
              <Button
                variant="outline"
                size="sm"
                type="button"
                className={cn("flex-1 sm:flex-none", history.length === 0 && "invisible")}
                onClick={goBack}
                disabled={history.length === 0 || submitting}
              >
                Back
              </Button>
              <Button
                size="sm"
                type="button"
                className="flex-1 sm:flex-none"
                onClick={() => void goNext()}
                loading={submitting}
              >
                {submitting ? "Submitting…" : isLastStep ? "Submit →" : "Next →"}
              </Button>
            </div>
          </div>
        )}
      </div>
    </div>
  );
}

function WelcomeStep({
  title,
  description,
  buttonLabel,
  onStart,
}: {
  title: string;
  description: string;
  buttonLabel: string;
  onStart: () => void;
}) {
  return (
    <div className="text-center">
      <h2 className="mb-3 text-xl font-extrabold sm:text-2xl">{title}</h2>
      <p className="mb-8 text-sm text-muted sm:text-base">{description}</p>
      <Button onClick={onStart} className="w-full sm:w-auto">
        {buttonLabel}
      </Button>
    </div>
  );
}

function ThankYouStep({
  emoji,
  title,
  description,
}: {
  emoji: string;
  title: string;
  description: string;
}) {
  return (
    <div className="py-8 text-center sm:py-[60px]">
      <div
        className="mx-auto mb-5 flex h-[72px] w-[72px] items-center justify-center rounded-full bg-[#F0FDF4] text-[40px] leading-none"
        aria-hidden
      >
        {emoji}
      </div>
      <h2 className="mb-2 text-xl font-extrabold sm:text-[22px]">{title}</h2>
      <p className="text-sm text-muted">{description}</p>
    </div>
  );
}

function QuestionStep({
  question,
  index,
  value,
  onChange,
  error,
  compact = false,
}: {
  question: FillerQuestion;
  index: number;
  value: FillerAnswerValue;
  onChange: (v: FillerAnswerValue) => void;
  error: string | null;
  compact?: boolean;
}) {
  return (
    <div className={cn(compact && "border-b border-border pb-8 last:border-b-0 last:pb-0")}>
      <div className="mb-2.5 font-mono text-[13px] font-extrabold text-accent">
        Q{index + 1}.
      </div>
      <h2
        className={cn(
          "mb-6 font-extrabold leading-snug",
          compact ? "text-base sm:text-lg" : "text-lg sm:text-[21px]"
        )}
      >
        {question.title}
        {question.isRequired && (
          <span className="ml-1 text-danger">*</span>
        )}
      </h2>

      <QuestionInput question={question} value={value} onChange={onChange} />

      {error && <p className="mt-4 text-sm text-danger">{error}</p>}
    </div>
  );
}

function QuestionInput({
  question,
  value,
  onChange,
}: {
  question: FillerQuestion;
  value: FillerAnswerValue;
  onChange: (v: FillerAnswerValue) => void;
}) {
  const choices = question.optionsConfig?.choices ?? [];

  if (
    question.type === "multiple_choice" ||
    question.type === "checkbox" ||
    question.type === "dropdown"
  ) {
    // Safety check: if no choices defined, show fallback message
    if (choices.length === 0) {
      return (
        <div className="rounded-lg border-2 border-dashed border-border bg-[#F8FAFC] p-4 text-center">
          <p className="text-sm text-muted">
            No options available for this question.
          </p>
        </div>
      );
    }

    if (question.type === "dropdown") {
      if (isMultiSelectDropdown(question.type, question.optionsConfig)) {
        const selected = Array.isArray(value)
          ? value
          : typeof value === "string" && value
            ? [value]
            : [];
        return (
          <MultiSelectDropdown
            choices={choices}
            value={selected}
            onChange={onChange}
            maxSelections={question.validationConfig?.maxSelections}
          />
        );
      }
      return (
        <Select
          value={typeof value === "string" ? value : ""}
          onChange={(e) => onChange(e.target.value || null)}
        >
          <option value="">Select an option…</option>
          {choices.map((c) => (
            <option key={c} value={c}>
              {c}
            </option>
          ))}
        </Select>
      );
    }

    if (question.type === "checkbox") {
      const selected = Array.isArray(value) ? value : [];
      const maxSelections = question.validationConfig?.maxSelections;
      return (
        <div className="space-y-2.5">
          {choices.map((opt, i) => {
            const isChecked = selected.includes(opt);
            const limitReached =
              !isChecked &&
              maxSelections != null &&
              selected.length >= maxSelections;
            return (
              <button
                key={opt}
                type="button"
                disabled={limitReached}
                onClick={() =>
                  onChange(
                    isChecked
                      ? selected.filter((c) => c !== opt)
                      : [...selected, opt]
                  )
                }
                className={cn(
                  "flex w-full items-center gap-3 rounded-[11px] border-[1.5px] px-4 py-3.5 text-left text-sm font-semibold transition-colors sm:text-[14.5px]",
                  isChecked
                    ? "border-primary bg-[#EFF6FF] text-primary"
                    : "border-border hover:border-accent hover:bg-[#F8FBFF]",
                  limitReached && "cursor-not-allowed opacity-50 hover:border-border hover:bg-transparent"
                )}
              >
                <span
                  className={cn(
                    "flex h-[18px] w-[18px] shrink-0 items-center justify-center rounded-[5px] border-[1.5px]",
                    isChecked ? "border-primary bg-primary text-white" : "border-border"
                  )}
                  aria-hidden
                >
                  {isChecked && <Check className="h-3 w-3" strokeWidth={3} />}
                </span>
                {opt}
                <span className="ml-auto rounded border border-border px-1.5 py-0.5 font-mono text-[11px] text-muted">
                  {i + 1}
                </span>
              </button>
            );
          })}
          {maxSelections != null && (
            <p className="text-xs text-muted">
              Select up to {maxSelections} option{maxSelections === 1 ? "" : "s"}
              {selected.length > 0 ? ` (${selected.length} selected)` : ""}.
            </p>
          )}
        </div>
      );
    }

    return (
      <div className="space-y-2.5">
        {choices.map((opt, i) => (
          <button
            key={opt}
            type="button"
            onClick={() => onChange(opt)}
            className={cn(
              "flex w-full items-center gap-3 rounded-[11px] border-[1.5px] px-4 py-3.5 text-left text-sm font-semibold transition-colors sm:text-[14.5px]",
              value === opt
                ? "border-primary bg-[#EFF6FF] text-primary"
                : "border-border hover:border-accent hover:bg-[#F8FBFF]"
            )}
          >
            <span
              className={cn(
                "flex h-[18px] w-[18px] shrink-0 items-center justify-center rounded-full border-[1.5px]",
                value === opt ? "border-primary" : "border-border"
              )}
              aria-hidden
            >
              {value === opt && (
                <span className="h-[9px] w-[9px] rounded-full bg-primary" />
              )}
            </span>
            {opt}
            <span className="ml-auto rounded border border-border px-1.5 py-0.5 font-mono text-[11px] text-muted">
              {i + 1}
            </span>
          </button>
        ))}
      </div>
    );
  }

  if (question.type === "nps") {
    return (
      <div>
        <div className="flex flex-wrap gap-1.5 sm:gap-[7px]">
          {Array.from({ length: 11 }, (_, i) => (
            <button
              key={i}
              type="button"
              onClick={() => onChange(i)}
              className={cn(
                "h-9 w-9 rounded-[9px] border-[1.5px] font-mono text-[13px] font-bold sm:h-[38px] sm:w-[38px]",
                value === i
                  ? "border-primary bg-primary text-white"
                  : "border-border bg-white hover:border-accent"
              )}
            >
              {i}
            </button>
          ))}
        </div>
        <div className="mt-2 flex justify-between text-[11.5px] text-muted">
          <span>Not likely</span>
          <span>Very likely</span>
        </div>
      </div>
    );
  }

  if (question.type === "rating") {
    const min = question.optionsConfig?.minRating ?? 1;
    const max = question.optionsConfig?.maxRating ?? 5;
    const score = typeof value === "number" ? value : null;
    return (
      <StarRatingScale
        min={min}
        max={max}
        value={score}
        onChange={onChange}
      />
    );
  }

  if (question.type === "open_text") {
    return (
      <textarea
        className="min-h-[100px] w-full resize-y rounded-[10px] border-[1.5px] border-border p-3.5 text-sm focus:border-primary focus:outline-none"
        placeholder={
          question.optionsConfig?.placeholder ?? "Type your answer…"
        }
        value={typeof value === "string" ? value : ""}
        onChange={(e) => onChange(e.target.value)}
      />
    );
  }

  if (question.type === "date_time") {
    return (
      <Input
        type="datetime-local"
        value={typeof value === "string" ? value : ""}
        onChange={(e) => onChange(e.target.value || null)}
      />
    );
  }

  if (question.type === "file_upload") {
    return <FileUploadInput question={question} value={value} onChange={onChange} />;
  }

  if (question.type === "matrix") {
    const rows = question.optionsConfig?.rows ?? [];
    const cols = question.optionsConfig?.columns ?? [];
    const matrixVal = (value as Record<string, string>) ?? {};

    // Safety check: if no rows or columns defined, show fallback message
    if (rows.length === 0 || cols.length === 0) {
      return (
        <div className="rounded-lg border-2 border-dashed border-border bg-[#F8FAFC] p-4 text-center">
          <p className="text-sm text-muted">
            Matrix configuration incomplete.
          </p>
        </div>
      );
    }

    return (
      <div className="space-y-3 overflow-x-auto">
        {rows.map((row) => (
          <div key={row} className="min-w-[280px]">
            <p className="mb-1.5 text-xs font-semibold">{row}</p>
            <div className="flex flex-wrap gap-1.5">
              {cols.map((col) => (
                <button
                  key={col}
                  type="button"
                  onClick={() =>
                    onChange({ ...matrixVal, [row]: col })
                  }
                  className={cn(
                    "rounded-md border px-2 py-1 text-xs",
                    matrixVal[row] === col
                      ? "border-primary bg-[#EFF6FF] text-primary"
                      : "border-border"
                  )}
                >
                  {col}
                </button>
              ))}
            </div>
          </div>
        ))}
      </div>
    );
  }

  return null;
}

function FileUploadInput({
  question,
  value,
  onChange,
}: {
  question: FillerQuestion;
  value: FillerAnswerValue;
  onChange: (v: FillerAnswerValue) => void;
}) {
  const [fileError, setFileError] = useState<string | null>(null);
  const maxSizeMb = question.validationConfig?.maxFileSizeMb ?? 10;
  const allowedTypes = question.validationConfig?.allowedFileTypes ?? [];

  return (
    <div>
      <Input
        type="file"
        accept={allowedTypes.length ? allowedTypes.join(",") : undefined}
        onChange={(e) => {
          const file = e.target.files?.[0];
          if (!file) {
            setFileError(null);
            onChange(null);
            return;
          }

          if (file.size > maxSizeMb * 1024 * 1024) {
            setFileError(`File is too large. Maximum size is ${maxSizeMb} MB.`);
            e.target.value = "";
            onChange(null);
            return;
          }

          if (allowedTypes.length > 0) {
            const ext = `.${file.name.split(".").pop()?.toLowerCase() ?? ""}`;
            const matches = allowedTypes.some(
              (t) =>
                t.toLowerCase() === ext ||
                t.toLowerCase() === file.type.toLowerCase()
            );
            if (!matches) {
              setFileError(
                `File type not allowed. Accepted: ${allowedTypes.join(", ")}`
              );
              e.target.value = "";
              onChange(null);
              return;
            }
          }

          setFileError(null);
          onChange(file.name);
        }}
      />
      {fileError ? (
        <p className="mt-2 text-xs text-danger">{fileError}</p>
      ) : (
        <p className="mt-2 text-xs text-muted">
          {allowedTypes.length > 0 ? `Accepted: ${allowedTypes.join(", ")} · ` : ""}
          Max {maxSizeMb} MB. File content is not uploaded in this demo — filename is
          recorded.
        </p>
      )}
      {typeof value === "string" && value && !fileError && (
        <p className="mt-1 text-xs font-medium text-text">Selected: {value}</p>
      )}
    </div>
  );
}
