"use client";

import { useQueryClient } from "@tanstack/react-query";
import {
  createContext,
  type ReactNode,
  useCallback,
  useContext,
  useEffect,
  useMemo,
  useRef,
  useState,
} from "react";
import { pathParam } from "@/hooks/marriage/path-param";
import { marriageQueryKeys } from "@/hooks/marriage/query-keys";
import type {
  MarriageField,
  MarriageFieldValue,
  MarriagePhoneFieldValue,
  MarriageBirthplaceFieldValue,
  UpdateMarriageSectionDataPayload,
} from "@/hooks/marriage/types";
import { useMarriageProfileQuery } from "@/hooks/marriage/use-profile-main";
import {
  applyProfilePatchResultToCache,
  useMarriageSectionDataQuery,
  useUpdateMarriageSectionDataMutation,
} from "@/hooks/marriage/use-section-data";
import { getApiRequestUrl } from "@/lib/http";
import { authBridge } from "@/lib/auth-bridge";
import type { QuestionField } from "@/lib/schema-adapter";
import {
  getScopedSectionDraftKey,
  readScopedSectionDraft,
  removeScopedSectionDraft,
  writeScopedSectionDraft,
  type ScopedPendingField,
} from "@/lib/user-scoped-storage";

const STORAGE_VERSION = 2;

type QuestionAnswersByKey = Record<string, MarriageField>;

type StoredQuestionAnswers = {
  version: typeof STORAGE_VERSION;
  slug: string;
  current_step: number;
  fields: MarriageField[];
  pending_sync: boolean;
  pending_keys: string[];
  updated_at: string;
};

type FlushAnswersOptions = {
  force?: boolean;
};

type QuestionAnswersContextValue = {
  flushAnswers: (options?: FlushAnswersOptions) => Promise<void>;
  getAnswerValue: (question: QuestionField) => MarriageFieldValue | undefined;
  hasPendingSync: boolean;
  isSaving: boolean;
  isLoading: boolean;
  setAnswerValue: (question: QuestionField, value: MarriageFieldValue) => void;
  backendFields: MarriageField[];
  answers: QuestionAnswersByKey;
};

type QuestionAnswersProviderProps = {
  children: ReactNode;
  questions: readonly QuestionField[];
  slug: string;
  locale?: string;
};

const QuestionAnswersContext =
  createContext<QuestionAnswersContextValue | null>(null);

export function getQuestionAnswersStorageKey(slug: string) {
  return `marriage:sections:${slug}:answers:v${STORAGE_VERSION}`;
}

export function hasQuestionAnswerValue(value: MarriageFieldValue) {
  if (value === null || value === undefined) {
    return false;
  }

  if (typeof value === "string") {
    return value.trim().length > 0;
  }

  if (typeof value === "object") {
    if (Array.isArray(value)) {
      return value.length > 0;
    }
    const phone = value as Partial<MarriagePhoneFieldValue>;
    if (
      typeof phone.countryCode === "string" ||
      typeof phone.phoneNumber === "string"
    ) {
      return Boolean(phone.countryCode?.trim() || phone.phoneNumber?.trim());
    }
    const bp = value as Partial<MarriageBirthplaceFieldValue>;
    if (typeof bp.country === "string" || typeof bp.city === "string") {
      return Boolean(bp.country?.trim() || bp.city?.trim());
    }
  }

  return true;
}

function isMarriageBirthplaceFieldValue(
  value: unknown,
): value is MarriageBirthplaceFieldValue {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    return false;
  }

  const bpValue = value as Partial<MarriageBirthplaceFieldValue>;

  return (
    typeof bpValue.country === "string" && typeof bpValue.city === "string"
  );
}

function isMarriageField(value: unknown): value is MarriageField {
  if (!value || typeof value !== "object") {
    return false;
  }

  const field = value as Partial<MarriageField>;

  return (
    typeof field.key === "string" &&
    typeof field.label === "string" &&
    typeof field.type === "string" &&
    (field.value === null ||
      typeof field.value === "string" ||
      typeof field.value === "number" ||
      typeof field.value === "boolean" ||
      Array.isArray(field.value) ||
      isMarriagePhoneFieldValue(field.value) ||
      isMarriageBirthplaceFieldValue(field.value))
  );
}

function isMarriagePhoneFieldValue(
  value: unknown,
): value is MarriagePhoneFieldValue {
  if (!value || typeof value !== "object" || Array.isArray(value)) {
    return false;
  }

  const phoneValue = value as Partial<MarriagePhoneFieldValue>;

  return (
    typeof phoneValue.countryCode === "string" &&
    typeof phoneValue.phoneNumber === "string"
  );
}

function createQuestionField(
  question: QuestionField,
  value: MarriageFieldValue,
): MarriageField {
  let option_id: string | string[] | undefined;

  const isChoiceType =
    question.type === "dropdown" ||
    question.type === "radio" ||
    question.type === "checkbox" ||
    question.type === "scale";

  if (isChoiceType && question.options && Array.isArray(question.options) && question.options.length > 0) {
    if (Array.isArray(value)) {
      option_id = value;
    } else if (typeof value === "string" && value) {
      const strVal = value.trim().toLowerCase();
      const selectedOpt = question.options.find(
        (opt) =>
          opt.id === value ||
          opt.value === value ||
          opt.label === value ||
          opt.id.toLowerCase() === strVal ||
          (typeof opt.value === "string" && opt.value.toLowerCase() === strVal) ||
          (typeof opt.label === "string" && opt.label.toLowerCase() === strVal) ||
          opt.id.toLowerCase().endsWith(`.${strVal}`)
      );
      option_id = selectedOpt ? selectedOpt.id : value;
    }
  }

  const key = question.id;

  return {
    key,
    label: question.title,
    type: question.type,
    value,
    private: question.private,
    option_id: isChoiceType ? option_id : undefined,
  } as MarriageField;
}

function getOrderedFields(
  answers: QuestionAnswersByKey,
  questions: readonly QuestionField[],
  backendFields?: MarriageField[],
) {
  const orderedFields: MarriageField[] = [];
  const orderedKeys = new Set<string>();

  questions.forEach((question) => {
    const key = question.id;
    const field = answers[key];

    if (field) {
      orderedFields.push(field);
      orderedKeys.add(key);
    }
  });

  Object.entries(answers).forEach(([key, field]) => {
    if (!orderedKeys.has(key)) {
      orderedFields.push(field);
    }
  });

  return orderedFields;
}

function getCurrentStep(
  fields: MarriageField[],
  questions: readonly QuestionField[],
  backendFields?: MarriageField[],
) {
  return questions.filter((question) => {
    if (!question.required || !question.isVisible) {
      return false;
    }
    const key = question.id;
    const field = fields.find((f) => f.key === key);
    return field && hasQuestionAnswerValue(field.value);
  }).length;
}

function createPayload(
  answers: QuestionAnswersByKey,
  questions: readonly QuestionField[],
  backendFields?: MarriageField[],
): UpdateMarriageSectionDataPayload {
  const fields = getOrderedFields(answers, questions, backendFields);
  const targetQuestions = questions.filter((q) => q.required && q.isVisible);

  return {
    current_step: getCurrentStep(fields, questions, backendFields),
    total_steps: targetQuestions.length,
    fields,
  };
}

function fieldsToAnswers(fields: MarriageField[]) {
  return fields.reduce<QuestionAnswersByKey>((nextAnswers, field) => {
    const isChoice =
      field.type === "dropdown" ||
      field.type === "radio" ||
      field.type === "checkbox" ||
      field.type === "scale";

    if (
      isChoice &&
      field.option_id !== undefined &&
      field.option_id !== null &&
      (!Array.isArray(field.option_id) || field.option_id.length > 0 || field.type === "checkbox")
    ) {
      nextAnswers[field.key] = {
        ...field,
        value: field.option_id,
      };
    } else {
      nextAnswers[field.key] = field;
    }
    return nextAnswers;
  }, {});
}

function readStoredAnswers(storageKey: string, slug: string) {
  try {
    const rawValue = window.localStorage.getItem(storageKey);

    if (!rawValue) {
      return {
        answers: {},
        pendingSync: false,
        pendingKeys: [],
      };
    }

    const storedValue = JSON.parse(rawValue) as Partial<StoredQuestionAnswers>;
    const fields = Array.isArray(storedValue.fields)
      ? storedValue.fields.filter(isMarriageField)
      : [];

    return {
      answers: fieldsToAnswers(fields),
      pendingSync:
        storedValue.slug === slug &&
        (storedValue.pending_sync ?? fields.length > 0),
      pendingKeys: Array.isArray(storedValue.pending_keys)
        ? storedValue.pending_keys.filter(
            (key): key is string => typeof key === "string",
          )
        : fields.map((field) => field.key),
    };
  } catch {
    return {
      answers: {},
      pendingSync: false,
      pendingKeys: [],
    };
  }
}

function writeStoredAnswers(
  storageKey: string,
  slug: string,
  questions: readonly QuestionField[],
  answers: QuestionAnswersByKey,
  pendingSync: boolean,
  backendFields?: MarriageField[],
  pendingKeys: readonly string[] = [],
) {
  try {
    const payload = createPayload(answers, questions, backendFields);

    if (payload.fields.length === 0) {
      window.localStorage.removeItem(storageKey);
      return;
    }

    const storedValue: StoredQuestionAnswers = {
      version: STORAGE_VERSION,
      slug,
      current_step: payload.current_step,
      fields: payload.fields,
      pending_sync: pendingSync,
      pending_keys: pendingSync ? [...pendingKeys] : [],
      updated_at: new Date().toISOString(),
    };

    window.localStorage.setItem(storageKey, JSON.stringify(storedValue));
  } catch {
    // localStorage can fail in private mode or when storage quota is exhausted.
  }
}

function getKeepalivePatchUrl(slug: string) {
  return getApiRequestUrl(`/api/marriage/forms/profile/answers/`);
}

function getCsrfToken() {
  if (typeof document === "undefined") return "";
  const match = document.cookie.match(/(^|;)\s*csrftoken\s*=\s*([^;]+)/);
  return match ? match[2] : "";
}

export function QuestionAnswersProvider({
  children,
  questions,
  slug,
  locale = "en",
}: QuestionAnswersProviderProps) {
  const storageKey = useMemo(() => getQuestionAnswersStorageKey(slug), [slug]);
  const queryClient = useQueryClient();
  const [answers, setAnswers] = useState<QuestionAnswersByKey>({});
  const [hasPendingSync, setHasPendingSync] = useState(false);
  const { isPending: isSaving, mutateAsync } =
    useUpdateMarriageSectionDataMutation(slug, locale);
  const answersRef = useRef<QuestionAnswersByKey>({});
  const hasPendingSyncRef = useRef(false);
  const answersRevisionRef = useRef(0);
  const flushPromiseRef = useRef<Promise<void> | null>(null);
  const questionsRef = useRef(questions);
  const storageKeyRef = useRef(storageKey);
  const slugRef = useRef(slug);
  const backendFieldsRef = useRef<MarriageField[]>([]);
  const versionRef = useRef<number | undefined>(undefined);
  const dirtyKeysRef = useRef(new Set<string>());

  const { data: profile } = useMarriageProfileQuery();
  const canEdit = profile?.can_edit_profile !== false;

  const { data: serverSectionData, isLoading: isLoadingData } =
    useMarriageSectionDataQuery(slug, locale);

  useEffect(() => {
    answersRef.current = answers;
    hasPendingSyncRef.current = hasPendingSync;
    slugRef.current = slug;
    questionsRef.current = questions;
    backendFieldsRef.current = serverSectionData?.data || [];
    versionRef.current = serverSectionData?.version;
  }, [
    answers,
    hasPendingSync,
    slug,
    questions,
    serverSectionData?.data,
    serverSectionData?.version,
  ]);

  useEffect(() => {
    storageKeyRef.current = storageKey;
    slugRef.current = slug;

    const profileId = profile?.id;

    // ── NEW RECONCILIATION (V3): Server is canonical ──────────────
    //
    // displayAnswers = canonicalServerAnswers
    //   THEN overlay ONLY local fields explicitly listed as pending/dirty
    //   for the CURRENT user.
    //
    // A single pending question must never make every historical local
    // answer override server data. A successful server response containing
    // no answers must render an empty section unless the current user has
    // genuine unsynced pending fields.

    if (serverSectionData?.data) {
      // Step 1: Server is canonical
      const serverAnswers = fieldsToAnswers(serverSectionData.data);
      let finalAnswers: QuestionAnswersByKey = { ...serverAnswers };
      let finalPendingSync = false;
      const nextDirtyKeys = new Set<string>();

      // Preserve active in-memory dirty edits so a server query update doesn't overwrite unsynced keystrokes
      for (const dirtyKey of dirtyKeysRef.current) {
        if (answersRef.current[dirtyKey]) {
          finalAnswers[dirtyKey] = answersRef.current[dirtyKey];
          nextDirtyKeys.add(dirtyKey);
          finalPendingSync = true;
        }
      }

      // Step 2: If we have a valid profile, read the user-scoped draft
      if (profileId && canEdit) {
        const scopedDraft = readScopedSectionDraft(profileId, slug);
        if (scopedDraft && Object.keys(scopedDraft.pending).length > 0) {
          // Overlay ONLY the pending dirty fields from the current user
          for (const [key, pendingField] of Object.entries(scopedDraft.pending)) {
            finalAnswers[key] = {
              key: pendingField.key,
              label: pendingField.label,
              type: pendingField.type,
              value: pendingField.value,
              option_id: pendingField.option_id ?? undefined,
              private: pendingField.private,
            } as MarriageField;
            nextDirtyKeys.add(key);
          }
          finalPendingSync = true;
        }
      }

      // Also check the old V2 storage for backward compatibility during
      // the transition period. Any old V2 pending edits are treated as
      // a one-time overlay but NOT migrated (ownership can't be proven).
      // After this render they won't be re-read because the V2 key is
      // cleaned up by the legacy migration in AuthDataBoundary.
      if (nextDirtyKeys.size === 0) {
        const legacyStored = readStoredAnswers(storageKey, slug);
        if (legacyStored.pendingSync && legacyStored.pendingKeys.length > 0 && canEdit) {
          for (const pendingKey of legacyStored.pendingKeys) {
            const pendingField = legacyStored.answers[pendingKey];
            if (pendingField) {
              finalAnswers[pendingKey] = pendingField;
              nextDirtyKeys.add(pendingKey);
            }
          }
          if (nextDirtyKeys.size > 0) {
            finalPendingSync = true;
            // Migrate these to scoped storage if we have a profile
            if (profileId) {
              const pendingMap: Record<string, ScopedPendingField> = {};
              for (const key of nextDirtyKeys) {
                const field = finalAnswers[key];
                if (field) {
                  pendingMap[key] = {
                    key: field.key,
                    label: field.label,
                    type: field.type,
                    value: field.value,
                    option_id: (field as any).option_id,
                    private: field.private,
                  };
                }
              }
              writeScopedSectionDraft(profileId, slug, pendingMap);
            }
            // Remove old V2 key
            try { window.localStorage.removeItem(storageKey); } catch {}
          }
        }
      }

      dirtyKeysRef.current = nextDirtyKeys;
      answersRef.current = finalAnswers;
      hasPendingSyncRef.current = finalPendingSync;

      setAnswers((prev) => {
        const prevKeys = Object.keys(prev);
        const nextKeys = Object.keys(finalAnswers);
        if (prevKeys.length === nextKeys.length) {
          const isSame = prevKeys.every(
            (k) => prev[k]?.value === finalAnswers[k]?.value,
          );
          if (isSame) return prev;
        }
        return finalAnswers;
      });
      setHasPendingSync(finalPendingSync);
    }
  }, [slug, storageKey, serverSectionData, questions, canEdit, profile?.id]);

  const syncTimeoutRef = useRef<NodeJS.Timeout | null>(null);

  useEffect(() => {
    return () => {
      if (syncTimeoutRef.current !== null) {
        clearTimeout(syncTimeoutRef.current);
      }
    };
  }, []);

  const getAnswerValue = useCallback(
    (question: QuestionField) => {
      const key = question.id;
      return answers[key]?.value;
    },
    [answers],
  );

  const setAnswerValue = useCallback(
    (question: QuestionField, value: MarriageFieldValue) => {
      if (!canEdit) {
        return;
      }
      const field = createQuestionField(question, value);

      setAnswers((currentAnswers) => {
        const nextAnswers = {
          ...currentAnswers,
          [field.key]: field,
        };

        answersRef.current = nextAnswers;
        hasPendingSyncRef.current = true;
        answersRevisionRef.current += 1;
        dirtyKeysRef.current.add(field.key);

        // Write only dirty fields to profile-scoped localStorage
        const currentProfileId = profile?.id;
        if (currentProfileId) {
          const pendingMap: Record<string, ScopedPendingField> = {};
          for (const dirtyKey of dirtyKeysRef.current) {
            const dirtyField = nextAnswers[dirtyKey];
            if (dirtyField) {
              pendingMap[dirtyKey] = {
                key: dirtyField.key,
                label: dirtyField.label,
                type: dirtyField.type,
                value: dirtyField.value,
                option_id: (dirtyField as any).option_id,
                private: dirtyField.private,
              };
            }
          }
          writeScopedSectionDraft(currentProfileId, slugRef.current, pendingMap);
        }

        if (syncTimeoutRef.current !== null) {
          clearTimeout(syncTimeoutRef.current);
        }

        const isTextLike =
          question.type === "text" ||
          question.type === "textarea" ||
          question.type === "number";

        // The date wheel applies its value on every scroll settle; debounce
        // it like text inputs so each spin does not fire a PATCH.
        const isDebounced = isTextLike || question.type === "date";

        const delay = isDebounced ? 1000 : 0;

        syncTimeoutRef.current = setTimeout(() => {
          void flushAnswersRef.current().catch((err) => {
            console.error("[FLUSH] Auto-flush FAILED:", err);
            // The draft stays pending so the next edit or exit can retry.
          });
        }, delay);

        return nextAnswers;
      });
      setHasPendingSync(true);
    },
    [canEdit, serverSectionData?.data],
  );

  const flushAnswers = useCallback(
    async (options?: FlushAnswersOptions) => {
      if (!canEdit) {
        return;
      }

      if (flushPromiseRef.current) {
        await flushPromiseRef.current;
      }

      if (!hasPendingSyncRef.current && !options?.force) {
        return;
      }

      const fullPayload = createPayload(
        answersRef.current,
        questionsRef.current,
        backendFieldsRef.current,
      );
      const pendingFields = fullPayload.fields.filter((field) =>
        dirtyKeysRef.current.has(field.key),
      );
      const payload = {
        ...fullPayload,
        fields: pendingFields,
        version: versionRef.current,
      };
      const revision = answersRevisionRef.current;

      if (payload.fields.length === 0) {
        return;
      }

      const request = mutateAsync(payload).then((result) => {
        const cleared = new Set(result?.cleared_answer_ids ?? []);
        const nextAnswers = { ...answersRef.current };
        const questionById = new Map(
          questionsRef.current.map((question) => [question.id, question]),
        );
        Object.entries(result?.answers ?? {}).forEach(([key, answer]) => {
          const question = questionById.get(key);
          if (question) {
            // Only update if there are no newer local dirty edits for this key
            if (!dirtyKeysRef.current.has(key)) {
              const isChoice =
                question.type === "dropdown" ||
                question.type === "radio" ||
                question.type === "checkbox" ||
                question.type === "scale";

              const resolvedVal =
                isChoice &&
                answer.option_id !== undefined &&
                answer.option_id !== null &&
                (!Array.isArray(answer.option_id) ||
                  answer.option_id.length > 0 ||
                  question.type === "checkbox")
                  ? answer.option_id
                  : answer.value;

              nextAnswers[key] = {
                key,
                label: question.title,
                type: question.type,
                value: resolvedVal,
                option_id: answer.option_id,
              } as MarriageField;
            }
          }
        });
        cleared.forEach((key) => {
          delete nextAnswers[key];
        });
        answersRef.current = nextAnswers;
        setAnswers(nextAnswers);
        applyProfilePatchResultToCache(queryClient, locale, result);
        return undefined;
      });
      flushPromiseRef.current = request;

      try {
        await request;
      } finally {
        if (flushPromiseRef.current === request) {
          flushPromiseRef.current = null;
        }
      }

      payload.fields.forEach((field) => {
        dirtyKeysRef.current.delete(field.key);
      });

      // Do not mark a newer edit as synced just because an older request
      // completed. A forced exit waits for and saves that newer revision too.
      if (revision !== answersRevisionRef.current) {
        if (options?.force) {
          await flushAnswersRef.current();
        }
        return;
      }


      if (dirtyKeysRef.current.size > 0) {
        await flushAnswersRef.current();
        return;
      }

      hasPendingSyncRef.current = false;
      setHasPendingSync(false);
      // All dirty fields acknowledged — remove the draft entirely
      const currentProfileId = profile?.id;
      if (currentProfileId) {
        removeScopedSectionDraft(currentProfileId, slugRef.current);
      }
    },
    [mutateAsync, canEdit, locale, queryClient, profile?.id],
  );
  const flushAnswersRef = useRef(flushAnswers);

  useEffect(() => {
    flushAnswersRef.current = flushAnswers;
  }, [flushAnswers]);

  useEffect(() => {
    const flushWithKeepalive = () => {
      if (!canEdit) {
        return;
      }
      if (!hasPendingSyncRef.current) {
        return;
      }

      if (flushPromiseRef.current) {
        return;
      }

      const fullPayload = createPayload(
        answersRef.current,
        questionsRef.current,
        backendFieldsRef.current,
      );
      const pendingFields = fullPayload.fields.filter((field) =>
        dirtyKeysRef.current.has(field.key),
      );
      const payload = { ...fullPayload, fields: pendingFields, version: versionRef.current };
      const revision = answersRevisionRef.current;

      if (payload.fields.length === 0) {
        return;
      }

      const headers: Record<string, string> = {
        Accept: "application/json",
        "Content-Type": "application/json",
      };
      const csrfToken = getCsrfToken();
      if (csrfToken) {
        headers["X-CSRFToken"] = csrfToken;
      }
      // Include auth token so the proxy / backend can authenticate the keepalive request
      const token = authBridge.getToken();
      if (token) {
        headers["Authorization"] = `Token ${token}`;
      }

      const answersPayload = payload.fields.map((f) => ({
        question_id: f.key,
        value: f.value,
        option_id: (f as any).option_id ?? undefined,
      }));

      fetch(getKeepalivePatchUrl(slugRef.current), {
        body: JSON.stringify({
          answers: answersPayload,
          version: payload.version,
        }),
        credentials: "include",
        headers,
        keepalive: true,
        method: "PATCH",
      })
        .then(async (response) => {
          if (!response.ok) {
            const text = await response.text().catch(() => '');
            console.error('[KEEPALIVE] Response NOT OK:', response.status, response.statusText, text.slice(0, 500));
            return;
          }


          if (revision !== answersRevisionRef.current) {
            return;
          }

          hasPendingSyncRef.current = false;
          payload.fields.forEach((field) => {
            dirtyKeysRef.current.delete(field.key);
          });
          const stillPending = dirtyKeysRef.current.size > 0;
          hasPendingSyncRef.current = stillPending;
          setHasPendingSync(stillPending);

          // Update scoped draft: remove acknowledged fields or delete draft
          const currentProfileId = profile?.id;
          if (currentProfileId) {
            if (stillPending) {
              const remainingPending: Record<string, ScopedPendingField> = {};
              for (const dirtyKey of dirtyKeysRef.current) {
                const field = answersRef.current[dirtyKey];
                if (field) {
                  remainingPending[dirtyKey] = {
                    key: field.key,
                    label: field.label,
                    type: field.type,
                    value: field.value,
                    option_id: (field as any).option_id,
                    private: field.private,
                  };
                }
              }
              writeScopedSectionDraft(currentProfileId, slugRef.current, remainingPending);
            } else {
              removeScopedSectionDraft(currentProfileId, slugRef.current);
            }
          }

          void queryClient.invalidateQueries({
            queryKey: marriageQueryKeys.profile(),
          });
          void queryClient.invalidateQueries({
            queryKey: marriageQueryKeys.sections(),
          });
          void queryClient.invalidateQueries({
            queryKey: marriageQueryKeys.sectionData(slugRef.current),
          });
        })
        .catch((err) => {
          console.error('[KEEPALIVE] Fetch FAILED:', err);
          // The local draft stays marked pending so a later exit can retry.
        });
    };

    window.addEventListener("pagehide", flushWithKeepalive);

    return () => {
      window.removeEventListener("pagehide", flushWithKeepalive);
      flushWithKeepalive();
    };
  }, [canEdit, queryClient.invalidateQueries]);

  const contextValue = useMemo<QuestionAnswersContextValue>(
    () => ({
      flushAnswers,
      getAnswerValue,
      hasPendingSync,
      isSaving,
      isLoading: isLoadingData,
      setAnswerValue,
      backendFields: serverSectionData?.data || [],
      answers,
    }),
    [
      flushAnswers,
      getAnswerValue,
      hasPendingSync,
      isSaving,
      isLoadingData,
      setAnswerValue,
      serverSectionData?.data,
      answers,
    ],
  );

  return (
    <QuestionAnswersContext.Provider value={contextValue}>
      {children}
    </QuestionAnswersContext.Provider>
  );
}

export function useQuestionAnswers() {
  const context = useContext(QuestionAnswersContext);

  if (!context) {
    throw new Error(
      "useQuestionAnswers must be used inside QuestionAnswersProvider",
    );
  }

  return context;
}

export function useQuestionAnswer(question: QuestionField) {
  const context = useContext(QuestionAnswersContext);

  return {
    setValue: (value: MarriageFieldValue) => {
      context?.setAnswerValue(question, value);
    },
    value: context?.getAnswerValue(question),
  };
}
