import {
  type AllergyView,
  type DietView,
  ErrorCode,
  type IngredientView,
} from "@batch-cooking/shared";
import { useEffect, useRef, useState } from "react";
import { useTranslation } from "react-i18next";
import { ApiError, apiClient } from "../../api/client";
import { useAuth } from "../../features/auth/AuthContext";
import { AllergySelect } from "../../features/profile/AllergySelect";
import { DietSelect } from "../../features/profile/DietSelect";
import { DislikedIngredientsField } from "../../features/profile/DislikedIngredientsField";
import { errorMessageService } from "../../services/error-message.service";
import "./settings-pages.scss";

/** Status of one section's own autosave — sections save independently, each with its own feedback. */
type SaveState = "idle" | "saving" | "saved" | "error";

/** Debounce for the allergen checkboxes — coalesces a quick burst of several toggles into one request. */
const ALLERGIES_DEBOUNCE_MS = 500;

/**
 * Dietary preferences — regime and allergies/intolerances — routed at
 * `/parametres/preferences`. The always-available counterpart to the
 * onboarding wizard's regime/allergens steps (`pages/onboarding/`): same
 * concerns, same shared field components, but editable at any time rather
 * than run once. Split out of what used to be `HouseholdPage` — these are
 * personal profile attributes (`UserProfile.dietId`/allergies), not
 * household ones, hence their own page distinct from `HouseholdSettingsPage`.
 *
 * Hot saving (no "Enregistrer" button) — see `HouseholdSettingsPage` for the
 * same pattern and rationale, shared verbatim.
 */
export function PreferencesPage() {
  const { t } = useTranslation();
  const { refreshUser } = useAuth();

  const [isLoading, setIsLoading] = useState(true);
  const [loadError, setLoadError] = useState(false);

  const [diets, setDiets] = useState<DietView[]>([]);
  const [dietId, setDietId] = useState<number | null>(null);
  const [dietSaveState, setDietSaveState] = useState<SaveState>("idle");
  const [dietSaveError, setDietSaveError] = useState<string | null>(null);

  const [allergies, setAllergies] = useState<AllergyView[]>([]);
  const [allergyIds, setAllergyIds] = useState<number[]>([]);
  const [allergySaveState, setAllergySaveState] = useState<SaveState>("idle");
  const [allergySaveError, setAllergySaveError] = useState<string | null>(null);
  const allergiesTimeout = useRef<number | undefined>(undefined);

  const [ingredients, setIngredients] = useState<IngredientView[]>([]);
  const [dislikedIngredientIds, setDislikedIngredientIds] = useState<number[]>([]);
  const [dislikedSaveState, setDislikedSaveState] = useState<SaveState>("idle");
  const [dislikedSaveError, setDislikedSaveError] = useState<string | null>(null);
  const dislikedTimeout = useRef<number | undefined>(undefined);

  useEffect(() => {
    let cancelled = false;
    // `apiClient.me()` here (not `useAuth().user.dietId`) — this page can be
    // revisited many times over a session without a full reload, and
    // AuthContext's `user` only refreshes on app load or after an explicit
    // `refreshUser()` call; relying on it directly would show a stale
    // `dietId` after navigating away and back post-save.
    Promise.all([
      apiClient.getDiets(),
      apiClient.getAllergies(),
      apiClient.getAllergyIds(),
      apiClient.getIngredients(),
      apiClient.getDislikedIngredientIds(),
      apiClient.me(),
    ])
      .then(
        ([
          dietsResult,
          allergiesResult,
          allergyIdsResult,
          ingredientsResult,
          dislikedResult,
          profile,
        ]) => {
          if (cancelled) return;
          setDiets(dietsResult);
          setAllergies(allergiesResult);
          setAllergyIds(allergyIdsResult);
          setIngredients(ingredientsResult);
          setDislikedIngredientIds(dislikedResult);
          setDietId(profile.dietId);
        },
      )
      .catch(() => {
        if (!cancelled) setLoadError(true);
      })
      .finally(() => {
        if (!cancelled) setIsLoading(false);
      });
    return () => {
      cancelled = true;
    };
  }, []);

  // A pending debounced save must not fire after unmount (e.g. the user
  // navigates away mid-debounce).
  useEffect(() => {
    return () => {
      window.clearTimeout(allergiesTimeout.current);
      window.clearTimeout(dislikedTimeout.current);
    };
  }, []);

  async function handleDietChange(newDietId: number | null) {
    setDietId(newDietId);
    setDietSaveState("saving");
    try {
      await apiClient.updateDiet(newDietId);
      // Keeps AuthContext's `user.dietId` in sync — nothing else reads it
      // today, but anything that might in the future shouldn't have to
      // know this page exists to stay correct.
      await refreshUser();
      setDietSaveState("saved");
    } catch (err) {
      const code = err instanceof ApiError ? err.code : ErrorCode.INTERNAL_ERROR;
      setDietSaveError(errorMessageService.getLabel(code));
      setDietSaveState("error");
    }
  }

  function handleAllergyIdsChange(newAllergyIds: number[]) {
    setAllergyIds(newAllergyIds);
    window.clearTimeout(allergiesTimeout.current);
    setAllergySaveState("saving");
    allergiesTimeout.current = window.setTimeout(async () => {
      try {
        await apiClient.updateAllergyIds(newAllergyIds);
        setAllergySaveState("saved");
      } catch (err) {
        const code = err instanceof ApiError ? err.code : ErrorCode.INTERNAL_ERROR;
        setAllergySaveError(errorMessageService.getLabel(code));
        setAllergySaveState("error");
      }
    }, ALLERGIES_DEBOUNCE_MS);
  }

  function handleDislikedIngredientIdsChange(newIds: number[]) {
    setDislikedIngredientIds(newIds);
    window.clearTimeout(dislikedTimeout.current);
    setDislikedSaveState("saving");
    dislikedTimeout.current = window.setTimeout(async () => {
      try {
        await apiClient.updateDislikedIngredientIds(newIds);
        setDislikedSaveState("saved");
      } catch (err) {
        const code = err instanceof ApiError ? err.code : ErrorCode.INTERNAL_ERROR;
        setDislikedSaveError(errorMessageService.getLabel(code));
        setDislikedSaveState("error");
      }
    }, ALLERGIES_DEBOUNCE_MS);
  }

  if (isLoading) {
    return (
      <div className="settings-page">
        <h1>{t("preferences.title")}</h1>
        <p className="settings-page__status">{t("onboarding.loading")}</p>
      </div>
    );
  }

  if (loadError) {
    return (
      <div className="settings-page">
        <h1>{t("preferences.title")}</h1>
        <p className="settings-page__status settings-page__status--error">
          {t("common.loadError")}
        </p>
      </div>
    );
  }

  return (
    <div className="settings-page">
      <h1>{t("preferences.title")}</h1>

      <div className="settings-page__section">
        <DietSelect diets={diets} value={dietId} onChange={handleDietChange} />
        <SaveStatus state={dietSaveState} error={dietSaveError} t={t} />
      </div>

      <div className="settings-page__section">
        <AllergySelect
          legend={t("preferences.form.allergiesLabel")}
          allergies={allergies.filter((allergy) => allergy.kind === "ALLERGY")}
          value={allergyIds}
          onChange={handleAllergyIdsChange}
        />
        <AllergySelect
          legend={t("preferences.form.intolerancesLabel")}
          allergies={allergies.filter((allergy) => allergy.kind === "INTOLERANCE")}
          value={allergyIds}
          onChange={handleAllergyIdsChange}
        />
        <SaveStatus state={allergySaveState} error={allergySaveError} t={t} />
      </div>

      <div className="settings-page__section">
        <DislikedIngredientsField
          ingredients={ingredients}
          value={dislikedIngredientIds}
          onChange={handleDislikedIngredientIdsChange}
        />
        <SaveStatus state={dislikedSaveState} error={dislikedSaveError} t={t} />
      </div>
    </div>
  );
}

/** Inline "saving…"/"saved ✓"/error feedback shared by every autosaved section on this page — `idle` renders nothing. */
function SaveStatus({
  state,
  error,
  t,
}: {
  state: SaveState;
  error: string | null;
  t: (key: string) => string;
}) {
  if (state === "saving") {
    return <p className="settings-page__saving">{t("common.saving")}</p>;
  }
  if (state === "saved") {
    return <p className="settings-page__saved">{t("common.saved")}</p>;
  }
  if (state === "error") {
    return <p className="field-error">{error}</p>;
  }
  return null;
}
