"use client";

import Image from "next/image";
import { type FormEvent, useEffect, useMemo, useRef, useState } from "react";
import { usePathname, useSearchParams } from "next/navigation";
import { HttpTypes } from "@medusajs/types";
import { addToCartAction } from "@/app/actions/cart";
import {
  DEFAULT_CUSTOM_THEME_FIELDS,
  isCustomizationImageValue,
  readProductPersonalizationFields,
  resolvePersonalizationInputType,
  type CartCustomizationValue,
  type ProductPersonalizationField,
} from "@/lib/personalization";
import {
  getCustomizationSurchargePerUnitCentsFromEntries,
  isFlowerEffectField,
  isSurchargedFlowerEffect,
} from "@/lib/personalization-pricing";
import { formatEuro } from "@/lib/utils/money";
import { useSiteText } from "@/components/providers/site-text-provider";
import { ProductImageGallery } from "@/components/store/product-image-gallery";
import { ProductTabs } from "@/components/store/product-tabs";
import { trackAddToCart } from "@/lib/analytics";
import type { SanityPortableTextBlock } from "@/lib/sanity/queries";

type ProductCharacteristic = {
  label: string;
  value: string;
};

type VariantDescription = {
  title?: string;
  sku?: string;
  description?: string;
  descriptionRich?: SanityPortableTextBlock[];
  selections?: Array<{ axisKey?: string; value?: string }>;
};

type Props = {
  title: string;
  category?: string;
  displayBadge?: string;
  careText?: string;
  shortDescription?: string;
  isCustomizable?: boolean;
  sanityShowPaperFinish?: boolean;
  sanityPaperFinishRequired?: boolean;
  sanityShowThemes?: boolean;
  sanityThemeOptions?: string[];
  sanityThemeDetails?: Array<{ title: string; imageUrl?: string }>;
  sanityCustomThemeRequest?: {
    isEnabled?: boolean;
    optionLabel?: string;
    modalTitle?: string;
    helperText?: string;
    modalDescription?: string;
  };
  sanityPersonalizationTextField?: {
    isEnabled?: boolean;
    label?: string;
    showLabel?: boolean;
    helperText?: string;
    placeholder?: string;
    defaultValue?: string;
    maxLength?: number | null;
    required?: boolean;
  };
  sanityPersonalizationFields?: ProductPersonalizationField[];
  fallbackImage: string;
  galleryImages?: Array<{ url?: string; alt?: string }>;
  fallbackPrice?: number | null;
  fallbackRegularPrice?: number | null;
  medusaProduct: HttpTypes.StoreProduct;
  descriptionText?: string;
  richDescription?: SanityPortableTextBlock[] | null;
  characteristics?: ProductCharacteristic[];
  personalizationText?: string;
  showPersonalizationTab?: boolean;
  variantDescriptions?: VariantDescription[];
};

type ConfiguredVariantAxis = {
  key: string;
  label: string;
  kind: "type" | "theme" | "finish" | "color" | "quantity" | "other";
  order: number;
  values: string[];
  source: "configured";
};

type SelectedCustomizationValue = {
  label: string;
  value: string;
  priceCents?: number;
};

const COLOR_OPTION_KEYWORDS = ["color", "couleur", "coloris", "teinte"];
const PACK_LABEL_REGEX = /\bx\s*\d+\b/i;
const DEFAULT_PAPER_FINISH_OPTIONS = ["Brillant", "Mat"];
const TONIES_PAPER_FINISH_OPTIONS = ["Brillant", "Holographique"];
const TYPE_KEYWORDS = ["sticker", "stickers", "box", "toniebox", "chargeur", "ensemble"];
const FINISH_KEYWORDS = ["brillant", "mat", "holographique", "satin", "glossy"];
const GIFT_COMPARTMENT_FIELD_KEY = "compartiment-arriere-cadeau";
const GIFT_COMPARTMENT_OPTIONS = ["Avec", "Sans"];
const PRIMARY_TYPE_LABELS = [
  { key: "box", label: "Box" },
  { key: "sticker", label: "Stickers" },
  { key: "ensemble", label: "L'ensemble" },
];

const COLOR_HEX_BY_NAME: Record<string, string> = {
  blanc: "#f5f5f2",
  "blanc casse": "#f3efe4",
  ecru: "#ece6d5",
  noir: "#111111",
  gris: "#b8b8b8",
  rouge: "#b5232f",
  bordeaux: "#6e1f33",
  rose: "#e8a7b4",
  orange: "#d6752e",
  jaune: "#d6b11f",
  vert: "#4d7f4c",
  kaki: "#6e7151",
  bleu: "#335d93",
  marine: "#1f2c45",
  violet: "#69548e",
  marron: "#7a5b45",
  beige: "#d7c2a0",
};

function normalizeText(value: string) {
  return value
    .toLowerCase()
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .trim();
}

function portableTextToPlainText(blocks?: SanityPortableTextBlock[] | null) {
  if (!Array.isArray(blocks)) return "";

  return blocks
    .map((block) => (Array.isArray(block.children) ? block.children.map((child) => child.text || "").join("") : ""))
    .filter(Boolean)
    .join("\n")
    .trim();
}

function normalizeFieldKey(value: string) {
  return value
    .normalize("NFD")
    .replace(/[\u0300-\u036f]/g, "")
    .toLowerCase()
    .replace(/[^a-z0-9]+/g, "-")
    .replace(/^-+|-+$/g, "");
}

function isColorOptionTitle(value: string) {
  const normalized = normalizeText(value);
  return COLOR_OPTION_KEYWORDS.some((keyword) => normalized.includes(keyword));
}

function cleanHexColor(value?: string | null) {
  if (!value) return null;
  const normalized = value.trim();
  const hex = normalized.startsWith("#") ? normalized : `#${normalized}`;
  return /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6})$/.test(hex) ? hex : null;
}

function findColorHex(label?: string | null) {
  if (!label) return null;
  const normalizedLabel = normalizeText(label);
  if (COLOR_HEX_BY_NAME[normalizedLabel]) return COLOR_HEX_BY_NAME[normalizedLabel];
  const containsColor = Object.entries(COLOR_HEX_BY_NAME).find(([name]) => normalizedLabel.includes(name));
  return containsColor?.[1] || null;
}

function resolveOptionHex(optionLabel?: string | null) {
  return cleanHexColor(optionLabel) || findColorHex(optionLabel);
}

function isHexPalette(options: string[]) {
  if (options.length === 0) return false;
  const recognizedCount = options.filter((option) => Boolean(resolveOptionHex(option))).length;
  return recognizedCount > 0 && recognizedCount >= Math.ceil(options.length / 2);
}

function resolveCustomizationOptionPriceCents(input: {
  fieldKey: string;
  fieldLabel: string;
  optionLabel: string;
  explicitPriceCents?: number;
}) {
  if (typeof input.explicitPriceCents === "number" && Number.isFinite(input.explicitPriceCents)) {
    return Math.max(0, Math.round(input.explicitPriceCents));
  }

  if (
    (isFlowerEffectField(input.fieldKey) || isFlowerEffectField(input.fieldLabel)) &&
    isSurchargedFlowerEffect(input.optionLabel)
  ) {
    return 10;
  }

  return 0;
}

function isGiftCompartmentFieldKey(value: string) {
  return normalizeFieldKey(value) === GIFT_COMPARTMENT_FIELD_KEY;
}

function isCakeTopperTypeValue(value: string) {
  return normalizeText(value).includes("cake topper");
}

function getVariantColorLabel(variant: HttpTypes.StoreProductVariant) {
  const variantData = variant as unknown as {
    option_values?: Array<{ value?: string; title?: string; option?: { title?: string } }>;
    options?: Array<{ value?: string; title?: string; option?: { title?: string } }>;
  };
  const optionValues = variantData.option_values || variantData.options || [];

  for (const optionValue of optionValues) {
    const optionTitle = optionValue.option?.title || optionValue.title || "";
    if (!isColorOptionTitle(optionTitle)) continue;
    if (optionValue.value) return optionValue.value;
  }

  return null;
}

function getVariantColorHex(variant: HttpTypes.StoreProductVariant, colorLabel?: string | null) {
  const metadata = (variant.metadata || {}) as Record<string, unknown>;
  const hexFromMetadata =
    cleanHexColor(typeof metadata.color_hex === "string" ? metadata.color_hex : null) ||
    cleanHexColor(typeof metadata.colorHex === "string" ? metadata.colorHex : null) ||
    cleanHexColor(typeof metadata.hex === "string" ? metadata.hex : null);

  if (hexFromMetadata) return hexFromMetadata;
  return findColorHex(colorLabel);
}

function extractPackLabel(variant: HttpTypes.StoreProductVariant) {
  const variantData = variant as unknown as {
    option_values?: Array<{ value?: string; title?: string; option?: { title?: string } }>;
    options?: Array<{ value?: string; title?: string; option?: { title?: string } }>;
  };

  const optionValues = variantData.option_values || variantData.options || [];

  for (const optionValue of optionValues) {
    const optionTitle = optionValue.option?.title || optionValue.title || "";
    if (isColorOptionTitle(optionTitle)) continue;
    const value = optionValue.value || "";
    const match = value.match(PACK_LABEL_REGEX);
    if (match) return match[0].replace(/\s+/g, "").toLowerCase();
  }

  const titleMatch = (variant.title || "").match(PACK_LABEL_REGEX);
  if (titleMatch) return titleMatch[0].replace(/\s+/g, "").toLowerCase();

  return null;
}

function extractPackNumber(label?: string | null) {
  if (!label) return null;
  const match = label.match(/\d+/);
  if (!match) return null;
  return Number.parseInt(match[0], 10) || null;
}

function splitVariantTitleParts(variant: HttpTypes.StoreProductVariant) {
  return (variant.title || "")
    .split("/")
    .map((part) => part.trim())
    .filter(Boolean);
}

function extractPrimaryTypeLabel(variant: HttpTypes.StoreProductVariant) {
  const variantData = variant as unknown as {
    option_values?: Array<{ value?: string; title?: string; option?: { title?: string } }>;
    options?: Array<{ value?: string; title?: string; option?: { title?: string } }>;
  };

  const optionValues = variantData.option_values || variantData.options || [];

  for (const optionValue of optionValues) {
    const optionTitle = optionValue.option?.title || optionValue.title || "";
    if (isColorOptionTitle(optionTitle)) continue;
    const value = normalizeText(optionValue.value || "");
    if (!value) continue;

    for (const item of PRIMARY_TYPE_LABELS) {
      if (value.includes(item.key)) return item.label;
    }
  }

  const title = normalizeText(variant.title || "");
  for (const item of PRIMARY_TYPE_LABELS) {
    if (title.includes(item.key)) return item.label;
  }

  return null;
}

function getVariantImageUrl(variant?: HttpTypes.StoreProductVariant | null) {
  if (!variant) {
    return "";
  }

  const variantData = variant as HttpTypes.StoreProductVariant & {
    image?: { url?: string | null; src?: string | null } | null;
    images?: Array<{ url?: string | null; src?: string | null }> | null;
  };

  return (
    variant.thumbnail ||
    variantData.image?.url ||
    variantData.image?.src ||
    variantData.images?.[0]?.url ||
    variantData.images?.[0]?.src ||
    ""
  );
}

function getHelperImageToggleLabel(field: ProductPersonalizationField) {
  const explicitLabel = typeof field.helperImageLabel === "string" ? field.helperImageLabel.trim() : "";
  if (explicitLabel) return explicitLabel;
  return `Afficher l'emplacement de ${field.label.toLowerCase()}`;
}

function isConfiguredVariantAxis(value: unknown): value is Record<string, unknown> {
  return Boolean(value) && typeof value === "object" && !Array.isArray(value);
}

export function ProductDetailDynamic({
  title,
  category,
  displayBadge,
  careText,
  shortDescription,
  isCustomizable = true,
  sanityShowPaperFinish = true,
  sanityPaperFinishRequired = false,
  sanityShowThemes = false,
  sanityThemeOptions = [],
  sanityThemeDetails = [],
  sanityCustomThemeRequest,
  sanityPersonalizationTextField,
  sanityPersonalizationFields = [],
  fallbackImage,
  galleryImages = [],
  fallbackPrice,
  fallbackRegularPrice,
  medusaProduct,
  descriptionText,
  richDescription,
  characteristics = [],
  personalizationText,
  showPersonalizationTab = true,
  variantDescriptions = [],
}: Props) {
  const { t } = useSiteText();
  const variants = useMemo(() => medusaProduct.variants || [], [medusaProduct.variants]);
  const firstVariantId = variants[0]?.id || "";
  const [selectedVariantId, setSelectedVariantId] = useState(firstVariantId);
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const [selectedTheme, setSelectedTheme] = useState("");
  const [selectedPaperFinish, setSelectedPaperFinish] = useState("");
  const [selectedQuantity, setSelectedQuantity] = useState(1);
  const [selectedCustomizations, setSelectedCustomizations] = useState<Record<string, SelectedCustomizationValue>>({});
  const [uploadStates, setUploadStates] = useState<Record<string, { pending?: boolean; error?: string | null }>>({});
  const [personalizationErrors, setPersonalizationErrors] = useState<Record<string, string>>({});
  const [activeHelperOverlay, setActiveHelperOverlay] = useState<{ url: string; alt: string } | null>(null);
  const [usesCustomTheme, setUsesCustomTheme] = useState(false);
  const [isCustomThemeModalOpen, setIsCustomThemeModalOpen] = useState(false);
  const productMetadata = useMemo(() => {
    return (medusaProduct.metadata || {}) as Record<string, unknown>;
  }, [medusaProduct.metadata]);
  const fileInputRefs = useRef<Record<string, HTMLInputElement | null>>({});
  const personalizationFieldRefs = useRef<Record<string, HTMLElement | null>>({});
  const personalizationTextareaRef = useRef<HTMLTextAreaElement | null>(null);
  const paperFinishFieldRef = useRef<HTMLDivElement | null>(null);

  const selectedVariant = variants.find((variant) => variant.id === selectedVariantId) || variants[0];
  const selectedColorLabel = getVariantColorLabel(selectedVariant) || selectedVariant?.title || "";
  const [selectedStructuredValues, setSelectedStructuredValues] = useState<Record<string, string>>({});

  const resolveBestVariantId = (
    structuredChecks: NonNullable<typeof structuredVariantChecks>,
    nextValues: Record<string, string>,
    lockType = false,
  ) => {
    const matchesValue = (left?: string, right?: string) => normalizeText(left || "") === normalizeText(right || "");
    const typeGroup = structuredChecks.groups.find((group) => group.kind === "type");
    const expectedType = typeGroup ? nextValues[typeGroup.key] : "";

    let candidateRows = structuredChecks.rows;
    if (lockType && typeGroup && expectedType) {
      const lockedRows = structuredChecks.rows.filter((row) => matchesValue(row.valuesByGroup[typeGroup.key], expectedType));
      if (lockedRows.length > 0) {
        candidateRows = lockedRows;
      }
    }

    const exact = candidateRows.find((row) =>
      structuredChecks.groups.every((group) => {
        const expected = nextValues[group.key];
        if (!expected) return true;
        return matchesValue(row.valuesByGroup[group.key], expected);
      }),
    );
    if (exact) return exact.variantId;

    const scoredRows = candidateRows.map((row) => {
      let score = 0;
      for (const group of structuredChecks.groups) {
        const expected = nextValues[group.key];
        if (!expected) continue;
        if (matchesValue(row.valuesByGroup[group.key], expected)) {
          score += group.kind === "type" ? 6 : group.kind === "theme" ? 4 : group.kind === "finish" ? 3 : 1;
        }
      }
      return { variantId: row.variantId, score };
    });

    scoredRows.sort((a, b) => b.score - a.score);
    return scoredRows[0]?.variantId || candidateRows[0]?.variantId || structuredChecks.rows[0]?.variantId || "";
  };

  const buildVariantSelectionMap = (structuredChecks: NonNullable<typeof structuredVariantChecks>, overrides?: Record<string, string>) => {
    const selection: Record<string, string> = {};

    for (const group of structuredChecks.groups) {
      if (group.kind === "theme") {
        selection[group.key] = overrides?.[group.key] ?? selectedTheme ?? selectedStructuredValues[group.key] ?? "";
        continue;
      }

      if (group.kind === "finish") {
        selection[group.key] = overrides?.[group.key] ?? selectedPaperFinish ?? selectedStructuredValues[group.key] ?? "";
        continue;
      }

      selection[group.key] = overrides?.[group.key] ?? selectedStructuredValues[group.key] ?? "";
    }

    return selection;
  };

  const hasColorSwatches = useMemo(() => {
    return variants.some((variant) => {
      const colorLabel = getVariantColorLabel(variant);
      const colorHex = getVariantColorHex(variant, colorLabel);
      return Boolean(colorLabel || colorHex);
    });
  }, [variants]);

  const hasSecondaryOptions = useMemo(() => {
    return (medusaProduct.options || []).some((option) => {
      const optionTitle = option.title || "";
      return !isColorOptionTitle(optionTitle);
    });
  }, [medusaProduct.options]);

  const configuredVariantAxes = useMemo(() => {
    const raw = productMetadata.variant_axes;
    if (!Array.isArray(raw)) return [];

    return raw
      .map((item, index) => {
        if (!isConfiguredVariantAxis(item)) return null;
        const label = typeof item.label === "string" ? item.label.trim() : "";
        const key = typeof item.key === "string" ? item.key.trim() : "";
        const values = Array.isArray(item.values)
          ? item.values.map((value) => (typeof value === "string" ? value.trim() : "")).filter(Boolean)
          : [];
        if (!label || !key || values.length === 0) return null;

        return {
          key,
          label,
          kind:
            item.kind === "type" ||
            item.kind === "theme" ||
            item.kind === "finish" ||
            item.kind === "color" ||
            item.kind === "quantity"
              ? item.kind
              : "other",
          order: typeof item.order === "number" ? item.order : index,
          values,
          source: "configured" as const,
        };
      })
      .filter((value): value is ConfiguredVariantAxis => value !== null)
      .sort((left, right) => left.order - right.order);
  }, [productMetadata.variant_axes]);

  const swatches = useMemo(() => {
    if (configuredVariantAxes.length > 0) return [];

    const uniqueByColor = new Map<string, { variantId: string; label: string; hex: string | null }>();

    for (const variant of variants) {
      const label = getVariantColorLabel(variant) || variant.title || t("product.variant");
      const key = normalizeText(label);
      if (uniqueByColor.has(key)) continue;
      uniqueByColor.set(key, {
        variantId: variant.id,
        label,
        hex: getVariantColorHex(variant, label),
      });
    }

    return Array.from(uniqueByColor.values());
  }, [variants, t, configuredVariantAxes.length]);

  const packCircles = useMemo(() => {
    if (configuredVariantAxes.length > 0) return [];

    const items = variants.map((variant) => {
      const label = extractPackLabel(variant);
      return {
        variantId: variant.id,
        label,
        size: extractPackNumber(label),
      };
    });

    const matchedCount = items.filter((item) => item.label).length;
    if (matchedCount < 1) return [];

    const missing = items.filter((item) => !item.label);
    if (missing.length === 1) {
      missing[0].label = "x1";
      missing[0].size = 1;
    }

    if (items.some((item) => !item.label)) return [];

    return items
      .map((item) => ({
        variantId: item.variantId,
        label: item.label as string,
        size: item.size ?? 0,
      }))
      .sort((a, b) => a.size - b.size);
  }, [variants, configuredVariantAxes.length]);

  const hasPackCircles = useMemo(() => {
    return !hasColorSwatches && packCircles.length >= 2;
  }, [hasColorSwatches, packCircles]);

  const structuredVariantChecks = useMemo(() => {
    if (configuredVariantAxes.length > 0) {
      const rows = variants.map((variant) => {
        const variantData = variant as unknown as {
          option_values?: Array<{ value?: string; title?: string; option?: { title?: string } }>;
          options?: Array<{ value?: string; title?: string; option?: { title?: string } }>;
        };
        const optionValues = variantData.option_values || variantData.options || [];
        const valuesByGroup: Record<string, string> = {};

        for (const axis of configuredVariantAxes) {
          const matchingOption = optionValues.find((optionValue) => {
            const optionTitle = optionValue.option?.title || optionValue.title || "";
            return normalizeText(optionTitle) === normalizeText(axis.label);
          });
          valuesByGroup[axis.key] = matchingOption?.value || "";
        }

        return {
          variantId: variant.id,
          valuesByGroup,
        };
      });

      return {
        groups: configuredVariantAxes,
        rows,
      };
    }

    if (variants.length < 2 || hasColorSwatches || hasPackCircles) return null;

    const rows = variants.map((variant) => ({
      variantId: variant.id,
      parts: splitVariantTitleParts(variant),
    }));

    const maxPartCount = rows.reduce((max, row) => Math.max(max, row.parts.length), 0);
    if (maxPartCount < 2) return null;

    const indexValues = Array.from({ length: maxPartCount }, (_, index) => {
      const values = Array.from(new Set(rows.map((row) => row.parts[index] || "").filter(Boolean)));
      return { index, values };
    }).filter((entry) => entry.values.length > 1);

    if (indexValues.length < 2) return null;

    let typeAssigned = false;
    let finishAssigned = false;
    let optionNumber = 1;

    const groups = indexValues.map((entry) => {
      const normalizedValues = entry.values.map((value) => normalizeText(value));
      const hasType = normalizedValues.some((value) => TYPE_KEYWORDS.some((keyword) => value.includes(keyword)));
      const hasFinish = normalizedValues.some((value) => FINISH_KEYWORDS.some((keyword) => value.includes(keyword)));

      let kind: "type" | "theme" | "finish" | "other" = "theme";
      if (hasType && !typeAssigned) {
        kind = "type";
        typeAssigned = true;
      } else if (hasFinish && !finishAssigned) {
        kind = "finish";
        finishAssigned = true;
      } else if (kind !== "theme") {
        kind = "other";
      }

      if (kind === "finish") {
        finishAssigned = true;
      }

      const label =
        kind === "type"
          ? "Choix du produit"
          : kind === "theme"
            ? "Thème"
            : kind === "finish"
              ? "Effet"
              : `Option ${optionNumber++}`;

      const order = kind === "type" ? 0 : kind === "theme" ? 1 : kind === "finish" ? 2 : 3;

      return {
        key: `group-${entry.index}`,
        index: entry.index,
        kind,
        label,
        order,
        values: entry.values,
        source: "legacy" as const,
      };
    });

    const sortedGroups = [...groups].sort((a, b) => a.order - b.order || a.index - b.index);
    const structuredRows = rows.map((row) => {
      const valuesByGroup: Record<string, string> = {};
      for (const group of sortedGroups) {
        valuesByGroup[group.key] = row.parts[group.index] || "";
      }
      return {
        variantId: row.variantId,
        valuesByGroup,
      };
    });

    return {
      groups: sortedGroups,
      rows: structuredRows,
    };
  }, [variants, hasColorSwatches, hasPackCircles, configuredVariantAxes]);

  const hasStructuredVariantChecks = Boolean(structuredVariantChecks && structuredVariantChecks.groups.length >= 2);
  const structuredHasThemeGroup = Boolean(structuredVariantChecks?.groups.some((group) => group.kind === "theme"));

  const primaryTypeCards = useMemo(() => {
    const uniqueByType = new Map<string, { variantId: string; label: string; priceLabel: string }>();
    for (const variant of variants) {
      const label = extractPrimaryTypeLabel(variant);
      if (!label) continue;
      const key = normalizeText(label);
      if (uniqueByType.has(key)) continue;
      uniqueByType.set(key, {
        variantId: variant.id,
        label,
        priceLabel: formatEuro(variant.calculated_price?.calculated_amount),
      });
    }
    return Array.from(uniqueByType.values());
  }, [variants]);

  const hasPrimaryTypeCards = useMemo(() => {
    return primaryTypeCards.length >= 2;
  }, [primaryTypeCards]);

  const hasSimpleVariantButtons = useMemo(() => {
    return !hasColorSwatches && !hasPackCircles && !hasStructuredVariantChecks && !hasPrimaryTypeCards && variants.length > 1;
  }, [hasColorSwatches, hasPackCircles, hasStructuredVariantChecks, hasPrimaryTypeCards, variants.length]);

  const defaultDisplayedVariantId = useMemo(() => {
    if (hasPackCircles) return packCircles[0]?.variantId || "";
    if (hasPrimaryTypeCards) return primaryTypeCards[0]?.variantId || "";
    if (hasColorSwatches) return swatches[0]?.variantId || "";
    return firstVariantId;
  }, [firstVariantId, hasColorSwatches, hasPackCircles, hasPrimaryTypeCards, packCircles, primaryTypeCards, swatches]);

  useEffect(() => {
    const selectedVariantStillExists = variants.some((variant) => variant.id === selectedVariantId);

    if (selectedVariantStillExists) {
      return;
    }

    if (defaultDisplayedVariantId) {
      setSelectedVariantId(defaultDisplayedVariantId);
    }
  }, [defaultDisplayedVariantId, selectedVariantId, variants]);

  const variantImageUrls = useMemo(() => {
    return Array.from(new Set(variants.map((variant) => getVariantImageUrl(variant)).filter(Boolean)));
  }, [variants]);

  const variantImage = useMemo(() => {
    return getVariantImageUrl(selectedVariant) || medusaProduct.images?.[0]?.url || medusaProduct.thumbnail || fallbackImage || "";
  }, [selectedVariant, medusaProduct.images, medusaProduct.thumbnail, fallbackImage]);

  const nonVariantGalleryImages = useMemo(() => {
    return [{ url: fallbackImage, alt: title }, ...galleryImages].filter((image) => {
      const url = (image.url || "").trim();
      return Boolean(url) && !variantImageUrls.includes(url);
    });
  }, [fallbackImage, galleryImages, title, variantImageUrls]);

  const returnTo = useMemo(() => {
    const currentQuery = searchParams.toString();
    return currentQuery ? `${pathname}?${currentQuery}` : pathname;
  }, [pathname, searchParams]);

  const themeOptions = useMemo(() => {
    if (sanityShowThemes) {
      return Array.isArray(sanityThemeOptions)
        ? sanityThemeOptions.map((value) => (typeof value === "string" ? value.trim() : "")).filter(Boolean)
        : [];
    }

    const sanityOptions = Array.isArray(sanityThemeOptions)
      ? sanityThemeOptions.map((value) => (typeof value === "string" ? value.trim() : "")).filter(Boolean)
      : [];
    if (sanityOptions.length > 0) return sanityOptions;

    const optionsRaw = productMetadata.theme_options;
    if (!Array.isArray(optionsRaw)) return [];
    return optionsRaw.map((value) => (typeof value === "string" ? value.trim() : "")).filter(Boolean);
  }, [productMetadata.theme_options, sanityShowThemes, sanityThemeOptions]);

  const themeDetails = useMemo(() => {
    const normalizedImageMap = new Map(
      (Array.isArray(sanityThemeDetails) ? sanityThemeDetails : [])
        .map((item) => ({
          title: typeof item?.title === "string" ? item.title.trim() : "",
          imageUrl: typeof item?.imageUrl === "string" ? item.imageUrl.trim() : "",
        }))
        .filter((item) => item.title)
        .map((item) => [normalizeText(item.title), item.imageUrl] as const),
    );

    return themeOptions.map((title) => ({
      title,
      imageUrl: normalizedImageMap.get(normalizeText(title)) || "",
    }));
  }, [sanityThemeDetails, themeOptions]);

  const hasThemeImages = useMemo(() => {
    return themeDetails.some((item) => Boolean(item.imageUrl));
  }, [themeDetails]);

  const normalizedThemeOptionKeys = useMemo(() => {
    return new Set(themeOptions.map((value) => normalizeText(value)));
  }, [themeOptions]);

  const supportsThemeChoice = useMemo(() => {
    if (!themeOptions.length) return false;
    if (productMetadata.supports_custom_theme === false) return false;
    return true;
  }, [themeOptions, productMetadata.supports_custom_theme]);

  const normalizePersonalizationFields = (rawFields: ProductPersonalizationField[]) => {
    return rawFields
      .map((field, index) => {
        const label = typeof field?.label === "string" ? field.label.trim() : "";
        const rawOptions = Array.isArray((field as { options?: unknown }).options)
          ? ((field as { options?: unknown[] }).options ?? [])
          : [];
        const options = rawOptions.length
          ? rawOptions
              .map((option) => {
                if (typeof option === "string") {
                  const optionLabel = option.trim();
                  if (!optionLabel) return null;
                  return {
                    label: optionLabel,
                    priceCents: 0,
                  };
                }

                if (!option || typeof option !== "object" || Array.isArray(option)) {
                  return null;
                }

                const optionRecord = option as { label?: unknown; priceCents?: unknown };
                const optionLabel = typeof optionRecord.label === "string" ? optionRecord.label.trim() : "";
                if (!optionLabel) return null;

                return {
                  label: optionLabel,
                  priceCents:
                    typeof optionRecord.priceCents === "number" && Number.isFinite(optionRecord.priceCents)
                      ? Math.max(0, Math.round(optionRecord.priceCents))
                      : 0,
                };
              })
              .filter((option): option is ProductPersonalizationField["options"][number] => option !== null)
          : [];
        const normalizedKey = normalizeFieldKey(
          typeof field?.key === "string" && field.key.trim() ? field.key : label || `field-${index + 1}`,
        );
        const isGiftCompartmentField = isGiftCompartmentFieldKey(normalizedKey || label);
        const normalizedOptions = isGiftCompartmentField
          ? GIFT_COMPARTMENT_OPTIONS.map((optionLabel) => ({
              label: optionLabel,
              priceCents: 0,
            }))
          : options;

        if (!label || !normalizedKey) {
          return null;
        }

        return {
          ...field,
          key: normalizedKey,
          label,
          inputType: isGiftCompartmentField ? "choices" : field.inputType,
          options: normalizedOptions,
          defaultValue: isGiftCompartmentField ? field.defaultValue || "Sans" : field.defaultValue,
          helperText: isGiftCompartmentField ? "" : field.helperText,
          showLabel: isGiftCompartmentField ? true : field.showLabel,
          required: isGiftCompartmentField ? true : field.required,
        };
      })
      .filter((field): field is ProductPersonalizationField => field !== null);
  };

  const personalizationFields = useMemo(() => {
    const rawFields =
      Array.isArray(sanityPersonalizationFields) && sanityPersonalizationFields.length > 0
        ? sanityPersonalizationFields
        : readProductPersonalizationFields(productMetadata);

    return normalizePersonalizationFields(rawFields);
  }, [productMetadata, sanityPersonalizationFields]);

  const selectedTypeValue = useMemo(() => {
    const candidates: string[] = [];
    let typeGroupLabel = "Type";

    if (structuredVariantChecks) {
      const typeGroup = structuredVariantChecks.groups.find((group) => group.kind === "type");
      if (typeGroup) {
        typeGroupLabel = typeGroup.label;

        const selectedRow =
          structuredVariantChecks.rows.find((row) => row.variantId === selectedVariantId) || structuredVariantChecks.rows[0];

        candidates.push(selectedStructuredValues[typeGroup.key] || "");
        candidates.push(selectedRow?.valuesByGroup[typeGroup.key] || "");
      }
    }

    const variantData = selectedVariant as unknown as {
      option_values?: Array<{ value?: string; title?: string; option?: { title?: string } }>;
      options?: Array<{ value?: string; title?: string; option?: { title?: string } }>;
    };
    const optionValues = variantData?.option_values || variantData?.options || [];

    for (const optionValue of optionValues) {
      const optionTitle = optionValue.option?.title || optionValue.title || "";
      if (normalizeText(optionTitle) === normalizeText(typeGroupLabel) || normalizeText(optionTitle).includes("type")) {
        candidates.push(optionValue.value || "");
      }
    }

    candidates.push(selectedVariant?.title || "");
    candidates.push(selectedVariant?.sku || "");

    return candidates.find((value) => value && isCakeTopperTypeValue(value)) || candidates.find((value) => value.trim()) || "";
  }, [selectedStructuredValues, selectedVariant, selectedVariantId, structuredVariantChecks]);

  const shouldHideGiftCompartmentField = isCakeTopperTypeValue(selectedTypeValue);

  const visiblePersonalizationFields = useMemo(() => {
    if (!shouldHideGiftCompartmentField) return personalizationFields;
    return personalizationFields.filter((field) => !isGiftCompartmentFieldKey(field.key));
  }, [personalizationFields, shouldHideGiftCompartmentField]);

  const activeVariantDescription = useMemo(() => {
    if (!variantDescriptions.length) return null;

    const selectedSku = normalizeText(selectedVariant?.sku || "");
    const selectedTitle = normalizeText(selectedVariant?.title || "");
    const selectedType = normalizeText(selectedTypeValue || "");
    const selectedSelectionMap = new Map<string, string>();
    const selectedValueSet = new Set<string>();
    const activeVariantId = selectedVariantId || selectedVariant?.id || "";
    const selectedRow =
      structuredVariantChecks?.rows.find((row) => row.variantId === activeVariantId) || structuredVariantChecks?.rows[0];

    for (const [axisKey, value] of Object.entries(selectedRow?.valuesByGroup || {})) {
      const normalizedAxisKey = normalizeText(axisKey);
      const normalizedValue = normalizeText(value);
      if (normalizedAxisKey && normalizedValue) {
        selectedSelectionMap.set(normalizedAxisKey, normalizedValue);
        selectedValueSet.add(normalizedValue);
      }
    }

    if (selectedType) {
      selectedValueSet.add(selectedType);
    }

    const variantData = selectedVariant as unknown as {
      option_values?: Array<{ value?: string; title?: string; option?: { title?: string } }>;
      options?: Array<{ value?: string; title?: string; option?: { title?: string } }>;
    };
    const selectedOptionValues = variantData?.option_values || variantData?.options || [];

    for (const optionValue of selectedOptionValues) {
      const optionTitle = normalizeText(optionValue.option?.title || optionValue.title || "");
      const value = normalizeText(optionValue.value || "");
      if (!value) continue;
      selectedValueSet.add(value);
      if (optionTitle) {
        selectedSelectionMap.set(optionTitle, value);
      }
    }

    return variantDescriptions
      .map((item) => {
        const itemSku = normalizeText(item.sku || "");
        const itemTitle = normalizeText(item.title || "");
        const itemSelections = (item.selections || [])
          .map((selection) => ({
            axisKey: normalizeText(selection.axisKey || ""),
            value: normalizeText(selection.value || ""),
          }))
          .filter((selection) => selection.value);
        const itemSelectionValues = itemSelections.map((selection) => selection.value);
        let score = 0;

        if (itemSku && selectedSku && itemSku === selectedSku) score = Math.max(score, 10000);

        if (itemSelections.length > 0) {
          const exactSelectionMatches = itemSelections.filter((selection) => {
            if (!selection.axisKey) return false;
            return selectedSelectionMap.get(selection.axisKey) === selection.value;
          }).length;
          const valueMatches = itemSelections.filter((selection) => selectedValueSet.has(selection.value)).length;

          if (exactSelectionMatches === itemSelections.length) score = Math.max(score, 9000 + exactSelectionMatches);
          else if (valueMatches === itemSelections.length) score = Math.max(score, 8500 + valueMatches);
          else if (selectedType && itemSelectionValues.includes(selectedType)) score = Math.max(score, 5000 + valueMatches);
          else if (itemSelections.length === 1 && valueMatches === 1) score = Math.max(score, 3000);
        }

        if (itemTitle && selectedTitle && itemTitle === selectedTitle) {
          score = Math.max(score, 6000);
        } else if (itemTitle && selectedTitle && !itemSelections.length && selectedTitle.includes(itemTitle)) {
          score = Math.max(score, 2000);
        }

        return { item, score };
      })
      .filter((entry) => entry.score > 0)
      .sort((left, right) => right.score - left.score)[0]?.item || null;
  }, [selectedTypeValue, selectedVariant, selectedVariantId, structuredVariantChecks, variantDescriptions]);

  const activeVariantDescriptionText = activeVariantDescription?.description?.trim() || "";
  const activeVariantRichDescription =
    Array.isArray(activeVariantDescription?.descriptionRich) && activeVariantDescription.descriptionRich.length > 0
      ? activeVariantDescription.descriptionRich
      : null;
  const activeDescriptionText = activeVariantDescriptionText || descriptionText;
  const activeRichDescription = activeVariantRichDescription || (activeVariantDescriptionText ? null : richDescription);
  const activeShortDescription =
    activeVariantDescriptionText ||
    portableTextToPlainText(activeVariantRichDescription) ||
    shortDescription?.trim() ||
    careText?.trim() ||
    t("product.careText");

  const customThemeConfig = useMemo(() => {
    return {
      isEnabled: sanityCustomThemeRequest?.isEnabled !== false && isCustomizable !== false,
      optionLabel: sanityCustomThemeRequest?.optionLabel?.trim() || "Je personnalise mon thème",
      modalTitle: sanityCustomThemeRequest?.modalTitle?.trim() || "Personnaliser mon thème",
      helperText: sanityCustomThemeRequest?.helperText?.trim() || "",
      modalDescription:
        sanityCustomThemeRequest?.modalDescription?.trim() ||
        "Décrivez votre univers, vos couleurs et vos inspirations pour une création sur mesure.",
    };
  }, [isCustomizable, sanityCustomThemeRequest]);

  const customThemeFields = useMemo(() => normalizePersonalizationFields(DEFAULT_CUSTOM_THEME_FIELDS), []);
  const customThemeFieldKeys = useMemo(() => new Set(customThemeFields.map((field) => field.key)), [customThemeFields]);

  const activeBasePrice = selectedVariant?.calculated_price?.calculated_amount ?? fallbackPrice ?? null;
  const activeRegularPrice =
    typeof fallbackRegularPrice === "number" && typeof activeBasePrice === "number" && fallbackRegularPrice > activeBasePrice
      ? fallbackRegularPrice
      : null;
  const activeCustomizations = useMemo<CartCustomizationValue[]>(() => {
    return Object.entries(selectedCustomizations)
      .filter(([key, entry]) => {
        if (customThemeFieldKeys.has(key) && !usesCustomTheme) {
          return false;
        }

        if (shouldHideGiftCompartmentField && isGiftCompartmentFieldKey(key)) {
          return false;
        }

        return Boolean(entry?.value?.trim());
      })
      .map(([key, entry]) => ({
        key,
        label: entry.label,
        value: entry.value.trim(),
        priceCents: typeof entry.priceCents === "number" ? entry.priceCents : undefined,
      }));
  }, [customThemeFieldKeys, selectedCustomizations, shouldHideGiftCompartmentField, usesCustomTheme]);
  const activeCustomizationSurchargePerUnit = useMemo(() => {
    return getCustomizationSurchargePerUnitCentsFromEntries(activeCustomizations);
  }, [activeCustomizations]);
  const activePrice =
    typeof activeBasePrice === "number" ? activeBasePrice + activeCustomizationSurchargePerUnit : activeBasePrice;
  const activeTotalPrice = typeof activePrice === "number" ? activePrice * Math.max(1, selectedQuantity) : activePrice;
  const activeRegularDisplayPrice =
    typeof activeRegularPrice === "number" ? activeRegularPrice + activeCustomizationSurchargePerUnit : null;
  const activeRegularTotalPrice =
    typeof activeRegularDisplayPrice === "number" ? activeRegularDisplayPrice * Math.max(1, selectedQuantity) : null;

  const hasCustomPersonalizationFields = visiblePersonalizationFields.length > 0;

  const customizationPayload = useMemo(() => {
    return JSON.stringify(
      Object.fromEntries(
        Object.entries(selectedCustomizations).filter(([key, entry]) => {
          if (customThemeFieldKeys.has(key) && !usesCustomTheme) {
            return false;
          }

          if (shouldHideGiftCompartmentField && isGiftCompartmentFieldKey(key)) {
            return false;
          }

          return Boolean(entry?.value?.trim());
        }),
      ),
    );
  }, [customThemeFieldKeys, selectedCustomizations, shouldHideGiftCompartmentField, usesCustomTheme]);

  const handleImageUpload = async (field: ProductPersonalizationField, file: File | null) => {
    if (!file) return;

    setUploadStates((current) => ({
      ...current,
      [field.key]: { pending: true, error: null },
    }));

    try {
      const formData = new FormData();
      formData.set("file", file);
      formData.set("fieldKey", field.key);
      formData.set("productHandle", pathname.split("/").filter(Boolean).pop() || "product");

      const response = await fetch("/api/personalization/upload", {
        method: "POST",
        body: formData,
      });

      const payload = (await response.json()) as { ok?: boolean; url?: string; message?: string };
      if (!response.ok || !payload.ok || !payload.url) {
        throw new Error(payload.message || "Upload failed.");
      }

      setSelectedCustomizations((current) => ({
        ...current,
        [field.key]: { label: field.label, value: payload.url || "" },
      }));
      setUploadStates((current) => ({
        ...current,
        [field.key]: { pending: false, error: null },
      }));
    } catch (error) {
      setUploadStates((current) => ({
        ...current,
        [field.key]: {
          pending: false,
          error: error instanceof Error ? error.message : "Upload impossible.",
        },
      }));
    }
  };

  const renderCustomizationField = (field: ProductPersonalizationField) => {
    const selectedValue = selectedCustomizations[field.key]?.value || "";
    const helperImageUrl = (field.helperImageUrl || "").trim();
    const isHelperOverlayActive = helperImageUrl && activeHelperOverlay?.url === helperImageUrl;
    const fieldHeader = field.showLabel || field.helperText || helperImageUrl ? (
      <div className="space-y-1">
        {field.showLabel ? <span className="block font-medium text-[var(--foreground)]">{field.label}</span> : null}
        {field.helperText ? <span className="block text-xs leading-5 text-[var(--muted)]">{field.helperText}</span> : null}
        {helperImageUrl ? (
          <button
            type="button"
            onClick={() =>
              setActiveHelperOverlay((current) =>
                current?.url === helperImageUrl ? null : { url: helperImageUrl, alt: field.label || "Aide de positionnement" },
              )
            }
            className={`inline-flex items-center gap-1.5 text-xs font-medium transition ${
              isHelperOverlayActive ? "text-[var(--accent-3)]" : "text-[var(--muted)] hover:text-[var(--accent-3)]"
            }`}
          >
            <svg viewBox="0 0 20 20" fill="none" className="h-4 w-4" aria-hidden="true">
              <path
                d="M1.4 10c1.64-2.78 4.55-4.5 8.6-4.5 4.05 0 6.96 1.72 8.6 4.5-1.64 2.78-4.55 4.5-8.6 4.5-4.05 0-6.96-1.72-8.6-4.5Z"
                stroke="currentColor"
                strokeWidth="1.4"
              />
              <circle cx="10" cy="10" r="2.6" stroke="currentColor" strokeWidth="1.4" />
            </svg>
            {isHelperOverlayActive ? "Masquer l'emplacement" : getHelperImageToggleLabel(field)}
          </button>
        ) : null}
      </div>
    ) : null;

    if (field.inputType === "choices" || field.inputType === "radio") {
      return (
        <div key={field.key} className="space-y-2">
          {fieldHeader}
          <div className="flex flex-wrap gap-2">
            {field.options.map((option) => {
              const isSelected = selectedValue === option.label;
              const optionPriceLabel = option.priceCents > 0 ? ` (+${formatEuro(option.priceCents)})` : "";
              return (
                <button
                  key={`${field.key}-${option.label}`}
                  type="button"
                  onClick={() =>
                    setSelectedCustomizations((current) => ({
                      ...current,
                      [field.key]: { label: field.label, value: option.label, priceCents: option.priceCents },
                    }))
                  }
                  className={`rounded-full border px-3 py-1.5 text-xs font-medium transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent-2)] focus-visible:ring-offset-2 active:scale-[0.98] ${
                    isSelected
                      ? "border-[var(--accent-3)] bg-[var(--accent-3)] text-white"
                      : "border-[#d7d7d7] bg-white text-[#262626] hover:border-[var(--accent-3)] hover:bg-[#fff4f4]"
                  }`}
                >
                  {option.label}
                  {optionPriceLabel}
                </button>
              );
            })}
          </div>
        </div>
      );
    }

    if (field.inputType === "select") {
      return (
        <label key={field.key} className="block space-y-1 text-sm">
          {fieldHeader}
          <select
            value={selectedValue}
            onChange={(event) =>
              setSelectedCustomizations((current) => ({
                ...current,
                [field.key]: {
                  label: field.label,
                  value: event.target.value,
                  priceCents: field.options.find((option) => option.label === event.target.value)?.priceCents,
                },
              }))
            }
            className="w-full rounded-xl border border-[var(--line)] bg-white px-3 py-2 outline-none focus:border-[#f5505080]"
          >
            {!field.required ? <option value="">Sélectionner...</option> : null}
            {field.options.map((option) => (
              <option key={`${field.key}-${option.label}`} value={option.label}>
                {option.label}
                {option.priceCents > 0 ? ` (+${formatEuro(option.priceCents)})` : ""}
              </option>
            ))}
          </select>
        </label>
      );
    }

    if (field.inputType === "image") {
      const uploadState = uploadStates[field.key];
      const previewUrl = isCustomizationImageValue(selectedValue) ? selectedValue : "";

      return (
        <div key={field.key} className="space-y-2 text-sm">
          {fieldHeader}
          <input
            ref={(node) => {
              fileInputRefs.current[field.key] = node;
            }}
            type="file"
            accept="image/png,image/jpeg,image/webp,image/heic,image/heif"
            className="hidden"
            onChange={(event) => {
              void handleImageUpload(field, event.target.files?.[0] || null);
              event.currentTarget.value = "";
            }}
          />
          <div
            role="button"
            tabIndex={0}
            onClick={() => fileInputRefs.current[field.key]?.click()}
            onKeyDown={(event) => {
              if (event.key === "Enter" || event.key === " ") {
                event.preventDefault();
                fileInputRefs.current[field.key]?.click();
              }
            }}
            onDragOver={(event) => {
              event.preventDefault();
            }}
            onDrop={(event) => {
              event.preventDefault();
              void handleImageUpload(field, event.dataTransfer.files?.[0] || null);
            }}
            className="rounded-2xl border border-dashed border-[var(--line)] bg-white px-4 py-5 text-center text-sm text-[var(--muted)] transition hover:border-[var(--accent-3)] hover:bg-[#fff8f8]"
          >
            <p className="font-medium text-[var(--foreground)]">
              {uploadState?.pending ? "Upload en cours..." : "Glissez une image ici ou cliquez pour choisir"}
            </p>
            <p className="mt-1 text-xs">PNG, JPG, WEBP, HEIC, HEIF - 10 Mo max</p>
          </div>
          {uploadState?.error ? <p className="text-xs text-[#b42318]">{uploadState.error}</p> : null}
          {previewUrl ? (
            <div className="space-y-2">
              <div className="relative h-28 w-28 overflow-hidden rounded-xl border border-[var(--line)]">
                <Image src={previewUrl} alt={field.label} fill sizes="112px" className="object-cover" unoptimized />
              </div>
              <button
                type="button"
                onClick={() =>
                  setSelectedCustomizations((current) => ({
                    ...current,
                    [field.key]: { label: field.label, value: "" },
                  }))
                }
                className="text-xs font-medium text-[var(--accent-3)]"
              >
                Supprimer cette image
              </button>
            </div>
          ) : null}
        </div>
      );
    }

    if (field.inputType === "color") {
      const displayValue = selectedValue || field.defaultValue || "#d7c2a0";

      return (
        <div key={field.key} className="space-y-2 text-sm">
          {fieldHeader}
          <div className="flex items-center gap-3 rounded-2xl border border-[var(--line)] bg-white px-3 py-2">
            <input
              type="color"
              value={displayValue}
              onChange={(event) =>
                setSelectedCustomizations((current) => ({
                  ...current,
                  [field.key]: { label: field.label, value: event.target.value },
                }))
              }
              className="h-11 w-14 cursor-pointer rounded-lg border-0 bg-transparent p-0"
            />
            <div className="space-y-1">
              <p className="text-sm font-medium text-[var(--foreground)]">{displayValue.toUpperCase()}</p>
              <p className="text-xs text-[var(--muted)]">Cliquez pour choisir une couleur</p>
            </div>
          </div>
        </div>
      );
    }

    return (
      <label key={field.key} className="block space-y-1 text-sm">
        {fieldHeader}
        {field.inputType === "textarea" ? (
          <textarea
            rows={3}
            value={selectedValue}
            placeholder={field.placeholder || undefined}
            maxLength={field.maxLength || undefined}
            onChange={(event) =>
              setSelectedCustomizations((current) => ({
                ...current,
                [field.key]: { label: field.label, value: event.target.value },
              }))
            }
            className="w-full rounded-xl border border-[var(--line)] bg-white px-3 py-2 outline-none focus:border-[#f5505080]"
          />
        ) : (
          <input
            type={resolvePersonalizationInputType(field.inputType)}
            value={selectedValue}
            placeholder={field.placeholder || undefined}
            maxLength={field.maxLength || undefined}
            onChange={(event) =>
              setSelectedCustomizations((current) => ({
                ...current,
                [field.key]: { label: field.label, value: event.target.value },
              }))
            }
            className="w-full rounded-xl border border-[var(--line)] bg-white px-3 py-2 outline-none focus:border-[#f5505080]"
          />
        )}
      </label>
    );
  };

  const paperFinishOptions = useMemo(() => {
    const productLabel = normalizeText(title);
    const hasToniesMarker =
      productLabel.includes("tonie") ||
      variants.some((variant) => normalizeText(variant.title || "").includes("tonie"));

    return hasToniesMarker ? TONIES_PAPER_FINISH_OPTIONS : DEFAULT_PAPER_FINISH_OPTIONS;
  }, [title, variants]);

  const supportsPaperFinishChoice = useMemo(() => {
    if (!sanityShowPaperFinish) return false;
    return paperFinishOptions.length > 0;
  }, [paperFinishOptions, sanityShowPaperFinish]);

  const personalizationTextField = useMemo(() => {
    const source =
      sanityPersonalizationTextField && typeof sanityPersonalizationTextField === "object"
        ? sanityPersonalizationTextField
        : null;

    return {
      isEnabled: source?.isEnabled !== false,
      label: typeof source?.label === "string" && source.label.trim() ? source.label.trim() : "Instructions de personnalisation",
      showLabel: source?.showLabel === true,
      helperText: typeof source?.helperText === "string" && source.helperText.trim() ? source.helperText.trim() : "",
      placeholder:
        typeof source?.placeholder === "string" && source.placeholder.trim()
          ? source.placeholder.trim()
          : t("product.personalizationPlaceholder"),
      defaultValue: typeof source?.defaultValue === "string" ? source.defaultValue : "",
      maxLength: typeof source?.maxLength === "number" && Number.isFinite(source.maxLength) ? source.maxLength : undefined,
      required: source?.required === true,
    };
  }, [sanityPersonalizationTextField, t]);

  const selectedThemeValue = usesCustomTheme ? customThemeConfig.optionLabel : selectedTheme;
  const clearPersonalizationError = (key: string) => {
    setPersonalizationErrors((current) => {
      if (!current[key]) return current;
      const next = { ...current };
      delete next[key];
      return next;
    });
  };

  const getRequiredFieldError = (label?: string) => {
    const trimmedLabel = typeof label === "string" ? label.trim() : "";
    return trimmedLabel ? `Veuillez renseigner "${trimmedLabel}".` : "Veuillez remplir ce champ.";
  };

  const scrollToPersonalizationError = (key: string) => {
    const target =
      key === "personalization"
        ? personalizationTextareaRef.current
        : key === "paperFinish"
          ? paperFinishFieldRef.current
          : personalizationFieldRefs.current[key];

    if (!target) return;

    target.scrollIntoView({
      behavior: "smooth",
      block: "center",
    });

    if (target instanceof HTMLTextAreaElement || target instanceof HTMLInputElement || target instanceof HTMLSelectElement) {
      target.focus({preventScroll: true});
      return;
    }

    const focusable = target.querySelector("textarea, input, select, button, [tabindex]");
    if (focusable instanceof HTMLElement) {
      focusable.focus({preventScroll: true});
    }
  };

  const validatePersonalization = () => {
    const nextErrors: Record<string, string> = {};

    if (isCustomizable) {
      if (
        personalizationTextField.isEnabled &&
        personalizationTextField.required &&
        !String(personalizationTextareaRef.current?.value || "").trim()
      ) {
        nextErrors.personalization = getRequiredFieldError(personalizationTextField.showLabel ? personalizationTextField.label : "");
      }

      for (const field of visiblePersonalizationFields) {
        if (!field.required) continue;
        const value = String(selectedCustomizations[field.key]?.value || "").trim();
        if (!value) nextErrors[field.key] = getRequiredFieldError(field.label);
      }

      if (usesCustomTheme) {
        for (const field of customThemeFields) {
          if (!field.required) continue;
          const value = String(selectedCustomizations[field.key]?.value || "").trim();
          if (!value) nextErrors[field.key] = getRequiredFieldError(field.label);
        }
      }

      if (
        !hasStructuredVariantChecks &&
        supportsPaperFinishChoice &&
        sanityPaperFinishRequired &&
        !String(selectedPaperFinish || "").trim()
      ) {
        nextErrors.paperFinish = getRequiredFieldError("Effet du papier");
      }
    }

    if (Object.keys(nextErrors).length > 0) {
      setPersonalizationErrors(nextErrors);
    }
    return {
      isValid: Object.keys(nextErrors).length === 0,
      errors: nextErrors,
    };
  };

  const handleAddToCartSubmit = (event: FormEvent<HTMLFormElement>) => {
    const validation = validatePersonalization();
    if (!validation.isValid) {
      event.preventDefault();
      const firstErrorKey = Object.keys(validation.errors)[0] || "personalization";
      window.setTimeout(() => {
        scrollToPersonalizationError(firstErrorKey || "personalization");
      }, 0);
      return;
    }

    // Track add to cart
    const formData = new FormData(event.currentTarget);
    const quantityValue = formData.get("quantity");
    const quantity = typeof quantityValue === "string" ? parseInt(quantityValue, 10) || 1 : 1;

    trackAddToCart({
      id: selectedVariantId || firstVariantId,
      title,
      price: activePrice || activeBasePrice || fallbackPrice || 0,
      quantity,
      category,
    });
  };

  const customThemeSummary = useMemo(() => {
    return customThemeFields
      .map((field) => {
        const value = selectedCustomizations[field.key]?.value || "";
        if (!value.trim()) return null;
        return `${field.label}: ${isCustomizationImageValue(value) ? "Image ajoutée" : value}`;
      })
      .filter((value): value is string => Boolean(value));
  }, [customThemeFields, selectedCustomizations]);

  useEffect(() => {
    if (!structuredVariantChecks) return;
    const activeRow =
      structuredVariantChecks.rows.find((row) => row.variantId === selectedVariantId) || structuredVariantChecks.rows[0];
    if (!activeRow) return;

    setSelectedStructuredValues((current) => {
      const next: Record<string, string> = {};
      for (const group of structuredVariantChecks.groups) {
        next[group.key] = activeRow.valuesByGroup[group.key] || "";
      }

      const unchanged = structuredVariantChecks.groups.every((group) => current[group.key] === next[group.key]);
      return unchanged ? current : next;
    });

    const themeGroup = structuredVariantChecks.groups.find((group) => group.kind === "theme");
    const finishGroup = structuredVariantChecks.groups.find((group) => group.kind === "finish");

    if (themeGroup && !selectedTheme) {
      const activeThemeValue = activeRow.valuesByGroup[themeGroup.key] || "";
      const preferredThemeValue =
        themeOptions.find((value) => normalizeText(value) === normalizeText(activeThemeValue)) ||
        themeOptions[0] ||
        themeGroup.values[0] ||
        "";
      setSelectedTheme(preferredThemeValue);
    }

    if (finishGroup && !selectedPaperFinish) {
      setSelectedPaperFinish(activeRow.valuesByGroup[finishGroup.key] || finishGroup.values[0] || paperFinishOptions[0] || "");
    }
  }, [selectedVariantId, selectedTheme, selectedPaperFinish, structuredVariantChecks, paperFinishOptions, themeOptions]);

  useEffect(() => {
    if (!supportsThemeChoice || structuredHasThemeGroup || selectedTheme) return;
    setSelectedTheme(themeOptions[0] || "");
  }, [selectedTheme, structuredHasThemeGroup, supportsThemeChoice, themeOptions]);

  useEffect(() => {
    if (customThemeConfig.isEnabled) return;
    setUsesCustomTheme(false);
    setIsCustomThemeModalOpen(false);
  }, [customThemeConfig.isEnabled]);

  useEffect(() => {
    if (!visiblePersonalizationFields.length && !customThemeFields.length) return;

    setSelectedCustomizations((current) => {
      const next: Record<string, SelectedCustomizationValue> = {};

      for (const field of [...visiblePersonalizationFields, ...customThemeFields]) {
        const existing = current[field.key];
        const selectedValue =
          existing?.value ||
          field.defaultValue ||
          ((field.inputType === "choices" || field.inputType === "select" || field.inputType === "radio") &&
          field.required &&
          field.options.length === 1
            ? field.options[0].label
            : "");
        const selectedOption = field.options.find((option) => option.label === selectedValue);
        const selectedPriceCents =
          typeof existing?.priceCents === "number" && Number.isFinite(existing.priceCents)
            ? Math.max(0, Math.round(existing.priceCents))
            : selectedOption
              ? resolveCustomizationOptionPriceCents({
                  fieldKey: field.key,
                  fieldLabel: field.label,
                  optionLabel: selectedOption.label,
                  explicitPriceCents: selectedOption.priceCents,
                })
              : 0;

        next[field.key] = {
          label: field.label,
          value: selectedValue,
          priceCents: selectedPriceCents > 0 ? selectedPriceCents : undefined,
        };
      }

      const unchanged =
        Object.keys(next).length === Object.keys(current).length &&
        Object.entries(next).every(([key, value]) => current[key]?.label === value.label && current[key]?.value === value.value);

      return unchanged ? current : next;
    });
  }, [customThemeFields, visiblePersonalizationFields]);

  return (
    <>
      <div className="grid gap-8 md:grid-cols-[1.1fr_1fr]">
        <div className="space-y-4">
          <ProductImageGallery
            key={selectedVariantId || "default"}
            title={title}
            images={nonVariantGalleryImages}
            externalImageUrl={variantImage}
            helperOverlayImageUrl={activeHelperOverlay?.url || null}
            helperOverlayAlt={activeHelperOverlay?.alt}
            onClearHelperOverlay={() => setActiveHelperOverlay(null)}
          />
          {activeShortDescription ? (
            <div className="rounded-2xl border border-[var(--line)] bg-[var(--surface)] p-4 text-sm text-[var(--muted)]">
              {activeShortDescription}
            </div>
          ) : null}
        </div>

        <div className="space-y-5 md:sticky md:top-28 md:h-fit">
          {displayBadge?.trim() ? (
            <p className="text-xs font-semibold uppercase tracking-[0.16em] text-[var(--accent-3)]">{displayBadge}</p>
          ) : null}
          <h1 className="font-serif text-4xl text-[var(--foreground)]">{title}</h1>
            <div className="flex items-end gap-3">
             {typeof activeRegularTotalPrice === "number" &&
             typeof activeTotalPrice === "number" &&
             activeRegularTotalPrice > activeTotalPrice ? (
               <p className="text-base font-medium text-[var(--muted)] line-through decoration-[1.5px]">
                 {formatEuro(activeRegularTotalPrice)}
               </p>
             ) : null}
             <p className="text-2xl font-semibold text-[var(--accent)]">{formatEuro(activeTotalPrice)}</p>
            </div>
          <form action={addToCartAction} onSubmit={handleAddToCartSubmit} className="space-y-4 rounded-[1.5rem] border border-[var(--line)] bg-[var(--surface)] p-5 shadow-[0_12px_24px_rgba(49,18,18,0.08)]">
          <input type="hidden" name="returnTo" value={returnTo} />
          {hasColorSwatches ? (
            <div className="space-y-2 text-sm">
              <span className="block font-medium text-[var(--foreground)]">{t("product.variant")}</span>
              <div className="flex flex-wrap gap-2.5">
                {swatches.map((swatch) => {
                  const isSelected = swatch.variantId === selectedVariantId;
                  return (
                    <button
                      key={swatch.variantId}
                      type="button"
                      title={swatch.label}
                      aria-label={swatch.label}
                      onClick={() => setSelectedVariantId(swatch.variantId)}
                      className={`flex h-9 w-9 items-center justify-center rounded-full border transition ${
                        isSelected ? "border-[var(--accent-3)]" : "border-[#d7d7d7] hover:border-[#a8a8a8]"
                      }`}
                    >
                      <span
                        className="h-6 w-6 rounded-full border border-black/10"
                        style={{ backgroundColor: swatch.hex || "#efefef" }}
                      />
                    </button>
                  );
                })}
              </div>
              <p className="text-xs text-[var(--muted)]">
                {selectedColorLabel}
                {selectedVariant?.sku ? ` - Ref. ${selectedVariant.sku}` : ""}
              </p>
              {!hasSecondaryOptions ? <input type="hidden" name="variantId" value={selectedVariantId} /> : null}
            </div>
          ) : null}

          {hasPackCircles ? (
            <div className="space-y-2 text-sm">
              <span className="block font-medium text-[var(--foreground)]">{t("product.variant")}</span>
              <div className="flex flex-wrap gap-2.5">
                {packCircles.map((pack) => {
                  const isSelected = pack.variantId === selectedVariantId;
                  return (
                    <button
                      key={pack.variantId}
                      type="button"
                      title={pack.label}
                      aria-label={pack.label}
                      onClick={() => setSelectedVariantId(pack.variantId)}
                      className={`flex h-10 min-w-10 items-center justify-center rounded-full border bg-transparent px-2 text-[11px] font-medium uppercase tracking-[0.02em] transition ${
                        isSelected
                          ? "border-[var(--accent-3)] text-[var(--accent-3)] shadow-[inset_0_0_0_1px_var(--accent-3)]"
                          : "border-[#d7d7d7] text-[#3f3f3f] hover:border-[#a8a8a8]"
                      }`}
                    >
                      {pack.label}
                    </button>
                  );
                })}
              </div>
              <input type="hidden" name="variantId" value={selectedVariantId} />
            </div>
          ) : null}

                    {hasStructuredVariantChecks && structuredVariantChecks ? (
            <div className="space-y-3 text-sm">
              {structuredVariantChecks.groups.map((group) => (
                <div key={group.key} className="space-y-2">
                  <span className="block font-medium text-[var(--foreground)]">
                    {group.kind === "quantity" ? t("product.quantity") : group.label}
                  </span>
                  <div className={`flex flex-wrap ${group.kind === "quantity" ? "gap-2.5" : "gap-2"}`}>
                    {(group.kind === "theme"
                      ? group.source === "configured"
                        ? group.values
                        : (
                          themeOptions.length
                            ? group.values.filter((value) => normalizedThemeOptionKeys.has(normalizeText(value)))
                            : group.values
                        )
                      : group.kind === "finish"
                        ? group.source === "configured"
                          ? group.values
                          : Array.from(new Set([...group.values, ...paperFinishOptions]))
                        : group.values
                    ).map((value) => {
                      const isSelected =
                        group.kind === "theme"
                          ? selectedTheme === value
                          : group.kind === "finish"
                            ? selectedPaperFinish === value
                            : selectedStructuredValues[group.key] === value;

                      return (
                        <button
                          key={`${group.key}-${value}`}
                          type="button"
                          onClick={() => {
                            if (!structuredVariantChecks) return;

                            if (group.kind === "theme") {
                              setSelectedTheme(value);
                              const nextValues = buildVariantSelectionMap(structuredVariantChecks, { [group.key]: value });
                              const bestVariantId = resolveBestVariantId(structuredVariantChecks, nextValues, true);
                              if (bestVariantId && bestVariantId !== selectedVariantId) {
                                setSelectedVariantId(bestVariantId);
                              }
                              return;
                            }

                            if (group.kind === "finish") {
                              setSelectedPaperFinish(value);
                              const nextValues = buildVariantSelectionMap(structuredVariantChecks, { [group.key]: value });
                              const bestVariantId = resolveBestVariantId(structuredVariantChecks, nextValues, true);
                              if (bestVariantId && bestVariantId !== selectedVariantId) {
                                setSelectedVariantId(bestVariantId);
                              }
                              return;
                            }

                            const nextValues = buildVariantSelectionMap(structuredVariantChecks, { [group.key]: value });
                            setSelectedStructuredValues(nextValues);
                            const bestVariantId = resolveBestVariantId(structuredVariantChecks, nextValues, true);

                            if (bestVariantId && bestVariantId !== selectedVariantId) {
                              setSelectedVariantId(bestVariantId);
                            }
                          }}
                          className={`transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent-2)] focus-visible:ring-offset-2 active:scale-[0.98] ${
                            group.kind === "quantity"
                              ? isSelected
                                ? "flex h-10 min-w-10 items-center justify-center rounded-full border bg-transparent px-2 text-[11px] font-medium uppercase tracking-[0.02em] border-[var(--accent-3)] text-[var(--accent-3)] shadow-[inset_0_0_0_1px_var(--accent-3)]"
                                : "flex h-10 min-w-10 items-center justify-center rounded-full border bg-transparent px-2 text-[11px] font-medium uppercase tracking-[0.02em] border-[#d7d7d7] text-[#3f3f3f] hover:border-[#a8a8a8]"
                              : isSelected
                                ? "rounded-full border px-3 py-1.5 text-xs font-medium whitespace-nowrap border-[var(--accent-3)] bg-[var(--accent-3)] text-white"
                                : "rounded-full border px-3 py-1.5 text-xs font-medium whitespace-nowrap border-[#d7d7d7] bg-white text-[#262626] hover:border-[var(--accent-3)] hover:bg-[#fff4f4]"
                          }`}
                        >
                          {value}
                        </button>
                      );
                    })}
                  </div>
                </div>
              ))}
              <input type="hidden" name="variantId" value={selectedVariantId} />
              <input type="hidden" name="theme" value={selectedThemeValue} />
              <input type="hidden" name="paperFinish" value={selectedPaperFinish} />
            </div>
          ) : null}

          {hasPrimaryTypeCards && !hasStructuredVariantChecks ? (
            <div className="space-y-2 text-sm">
              <span className="block font-medium text-[var(--foreground)]">Choix du produit</span>
              <div className="grid grid-cols-1 gap-2 sm:grid-cols-3">
                {primaryTypeCards.map((item) => {
                  const isSelected = item.variantId === selectedVariantId;
                  return (
                    <button
                      key={item.variantId}
                      type="button"
                      onClick={() => setSelectedVariantId(item.variantId)}
                      className={`rounded-xl border px-3 py-3 text-left transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent-2)] focus-visible:ring-offset-2 active:scale-[0.985] ${
                        isSelected
                          ? "border-[var(--accent-3)] bg-[#fff4f4] shadow-[inset_0_0_0_1px_var(--accent-3)]"
                          : "border-[var(--line)] bg-white hover:border-[var(--accent-3)] hover:bg-[#fff8f8]"
                      }`}
                    >
                      <span className="block text-sm font-semibold text-[var(--foreground)]">{item.label}</span>
                      <span className="block text-xs text-[var(--muted)]">{item.priceLabel}</span>
                    </button>
                  );
                })}
              </div>
              <input type="hidden" name="variantId" value={selectedVariantId} />
            </div>
          ) : null}

          {hasSimpleVariantButtons ? (
            <div className="space-y-2 text-sm">
              <span className="block font-medium text-[var(--foreground)]">{t("product.variant")}</span>
              <div className="flex flex-wrap gap-2">
                {variants.map((variant) => {
                  const isSelected = variant.id === selectedVariantId;
                  return (
                    <button
                      key={variant.id}
                      type="button"
                      onClick={() => setSelectedVariantId(variant.id)}
                      className={`rounded-full border px-3 py-1.5 text-xs font-medium transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent-2)] focus-visible:ring-offset-2 active:scale-[0.98] ${
                        isSelected
                          ? "border-[var(--accent-3)] bg-[var(--accent-3)] text-white"
                          : "border-[#d7d7d7] bg-white text-[#262626] hover:border-[var(--accent-3)] hover:bg-[#fff4f4]"
                      }`}
                    >
                      {variant.title}
                    </button>
                  );
                })}
              </div>
              <input type="hidden" name="variantId" value={selectedVariantId} />
            </div>
          ) : null}

          {!hasSimpleVariantButtons &&
          !hasPackCircles &&
          !hasStructuredVariantChecks &&
          !hasPrimaryTypeCards &&
          (!hasColorSwatches || hasSecondaryOptions) &&
          variants.length > 1 ? (
            <label className="block space-y-1 text-sm">
              <span className="font-medium text-[var(--foreground)]">{t("product.variant")}</span>
              <select
                name="variantId"
                value={selectedVariantId}
                onChange={(event) => setSelectedVariantId(event.target.value)}
                className="w-full rounded-xl border border-[var(--line)] bg-white px-3 py-2 outline-none focus:border-[#f5505080]"
              >
                {variants.map((variant) => (
                  <option key={variant.id} value={variant.id}>
                    {variant.title}
                  </option>
                ))}
              </select>
            </label>
          ) : null}

          {!hasSimpleVariantButtons &&
          !hasPackCircles &&
          !hasStructuredVariantChecks &&
          !hasPrimaryTypeCards &&
          (!hasColorSwatches || hasSecondaryOptions) &&
          variants.length <= 1 ? <input type="hidden" name="variantId" value={selectedVariantId} /> : null}

          <label className="block space-y-1 text-sm">
            <span className="font-medium text-[var(--foreground)]">{t("product.quantity")}</span>
             <input
               type="number"
               name="quantity"
               min={1}
               value={selectedQuantity}
               onChange={(event) => setSelectedQuantity(Math.max(1, parseInt(event.target.value, 10) || 1))}
               className="w-full rounded-xl border border-[var(--line)] bg-white px-3 py-2 outline-none focus:border-[#f5505080]"
             />
          </label>

          {isCustomizable ? (
            <section className="space-y-3 rounded-xl border border-[var(--line)] bg-white/70 p-3 text-sm">
              <p className="font-medium text-[var(--foreground)]">Personnalisation</p>
              {hasCustomPersonalizationFields ? (
                <div className="space-y-3 text-sm">
                  {visiblePersonalizationFields.map((field) => {
                    const selectedValue = selectedCustomizations[field.key]?.value || "";
                    const helperImageUrl = (field.helperImageUrl || "").trim();
                    const isHelperOverlayActive = helperImageUrl && activeHelperOverlay?.url === helperImageUrl;
                    const fieldHeader = field.showLabel || field.helperText || helperImageUrl ? (
                      <div className="space-y-1">
                        {field.showLabel ? <span className="block font-medium text-[var(--foreground)]">{field.label}</span> : null}
                        {field.helperText ? <span className="block text-xs leading-5 text-[var(--muted)]">{field.helperText}</span> : null}
                        {helperImageUrl ? (
                          <button
                            type="button"
                            onClick={() =>
                              setActiveHelperOverlay((current) =>
                                current?.url === helperImageUrl ? null : { url: helperImageUrl, alt: field.label || "Aide de positionnement" },
                              )
                            }
                            className={`inline-flex items-center gap-1.5 text-xs font-medium transition ${
                              isHelperOverlayActive ? "text-[var(--accent-3)]" : "text-[var(--muted)] hover:text-[var(--accent-3)]"
                            }`}
                          >
                            <svg viewBox="0 0 20 20" fill="none" className="h-4 w-4" aria-hidden="true">
                              <path
                                d="M1.4 10c1.64-2.78 4.55-4.5 8.6-4.5 4.05 0 6.96 1.72 8.6 4.5-1.64 2.78-4.55 4.5-8.6 4.5-4.05 0-6.96-1.72-8.6-4.5Z"
                                stroke="currentColor"
                                strokeWidth="1.4"
                              />
                              <circle cx="10" cy="10" r="2.6" stroke="currentColor" strokeWidth="1.4" />
                            </svg>
                            {isHelperOverlayActive ? "Masquer l'emplacement" : getHelperImageToggleLabel(field)}
                          </button>
                        ) : null}
                      </div>
                    ) : null;

                    if (field.inputType === "choices" || field.inputType === "radio") {
                      const optionLabels = field.options.map((option) => option.label);
                      const renderAsColorSwatches = isColorOptionTitle(field.label) || isHexPalette(optionLabels);

                      return (
                        <div
                          key={field.key}
                          ref={(node) => {
                            personalizationFieldRefs.current[field.key] = node;
                          }}
                          className="space-y-2"
                        >
                          {fieldHeader}
                          <div className="flex flex-wrap gap-2">
                            {field.options.map((option) => {
                              const isSelected = selectedValue === option.label;
                              const optionHex = resolveOptionHex(option.label);
                              const optionLabel = option.label;
                              const optionPriceCents = resolveCustomizationOptionPriceCents({
                                fieldKey: field.key,
                                fieldLabel: field.label,
                                optionLabel,
                                explicitPriceCents: option.priceCents,
                              });
                              const optionPriceLabel = optionPriceCents > 0 ? ` (+${formatEuro(optionPriceCents)})` : "";
                              return (
                                <button
                                  key={`${field.key}-${option.label}`}
                                  type="button"
                                  title={renderAsColorSwatches ? option.label.toUpperCase() : undefined}
                                  aria-label={renderAsColorSwatches ? `${field.label} ${option.label.toUpperCase()}` : optionLabel}
                                  onClick={() => {
                                    clearPersonalizationError(field.key);
                                    setSelectedCustomizations((current) => ({
                                      ...current,
                                      [field.key]: {
                                        label: field.label,
                                        value: option.label,
                                        priceCents: optionPriceCents > 0 ? optionPriceCents : undefined,
                                      },
                                    }));
                                  }}
                                  className={
                                    renderAsColorSwatches
                                      ? `grid h-9 w-9 place-items-center rounded-full border transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent-2)] focus-visible:ring-offset-2 active:scale-[0.98] ${
                                          isSelected
                                            ? "border-[var(--accent-3)] bg-white shadow-[inset_0_0_0_2px_white,0_0_0_1px_var(--accent-3)]"
                                            : "border-[#d7d7d7] bg-white hover:border-[var(--accent-3)]"
                                        }`
                                      : `rounded-full border px-3 py-1.5 text-xs font-medium transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent-2)] focus-visible:ring-offset-2 active:scale-[0.98] ${
                                          isSelected
                                            ? "border-[var(--accent-3)] bg-[var(--accent-3)] text-white"
                                            : "border-[#d7d7d7] bg-white text-[#262626] hover:border-[var(--accent-3)] hover:bg-[#fff4f4]"
                                        }`
                                  }
                                >
                                  {renderAsColorSwatches && optionHex ? (
                                    <span
                                      className="block h-6 w-6 rounded-full border border-black/10"
                                      style={{ backgroundColor: optionHex || "#efefef" }}
                                    />
                                  ) : renderAsColorSwatches ? (
                                    <span className="px-1 text-[10px] font-semibold uppercase text-[#555]">{optionLabel.slice(0, 2)}</span>
                                  ) : (
                                    `${optionLabel}${optionPriceLabel}`
                                  )}
                                </button>
                              );
                            })}
                          </div>
                          {personalizationErrors[field.key] ? (
                            <p className="text-xs text-[#b42318]">{personalizationErrors[field.key]}</p>
                          ) : null}
                        </div>
                      );
                    }

                    if (field.inputType === "select") {
                      return (
                        <label
                          key={field.key}
                          ref={(node) => {
                            personalizationFieldRefs.current[field.key] = node;
                          }}
                          className="block space-y-1 text-sm"
                        >
                          {fieldHeader}
                          <select
                            value={selectedValue}
                            onChange={(event) => {
                              const selectedOption = field.options.find((option) => option.label === event.target.value);
                              const selectedPriceCents = selectedOption
                                ? resolveCustomizationOptionPriceCents({
                                    fieldKey: field.key,
                                    fieldLabel: field.label,
                                    optionLabel: selectedOption.label,
                                    explicitPriceCents: selectedOption.priceCents,
                                  })
                                : 0;
                              clearPersonalizationError(field.key);
                              setSelectedCustomizations((current) => ({
                                ...current,
                                [field.key]: {
                                  label: field.label,
                                  value: event.target.value,
                                  priceCents: selectedPriceCents > 0 ? selectedPriceCents : undefined,
                                },
                              }));
                            }}
                            className={`w-full rounded-xl border bg-white px-3 py-2 outline-none focus:border-[#f5505080] ${
                              personalizationErrors[field.key] ? "border-[#b42318]" : "border-[var(--line)]"
                            }`}
                          >
                            {!field.required ? <option value="">Sélectionner...</option> : null}
                            {field.options.map((option) => {
                              const optionPriceCents = resolveCustomizationOptionPriceCents({
                                fieldKey: field.key,
                                fieldLabel: field.label,
                                optionLabel: option.label,
                                explicitPriceCents: option.priceCents,
                              });

                              return (
                                <option key={`${field.key}-${option.label}`} value={option.label}>
                                  {option.label}
                                  {optionPriceCents > 0 ? ` (+${formatEuro(optionPriceCents)})` : ""}
                                </option>
                              );
                            })}
                          </select>
                          {personalizationErrors[field.key] ? (
                            <p className="text-xs text-[#b42318]">{personalizationErrors[field.key]}</p>
                          ) : null}
                        </label>
                      );
                    }

                    if (field.inputType === "image") {
                      const uploadState = uploadStates[field.key];
                      const previewUrl = isCustomizationImageValue(selectedValue) ? selectedValue : "";

                      return (
                        <div
                          key={field.key}
                          ref={(node) => {
                            personalizationFieldRefs.current[field.key] = node;
                          }}
                          className="space-y-2 text-sm"
                        >
                          {fieldHeader}
                          <input
                            ref={(node) => {
                              fileInputRefs.current[field.key] = node;
                            }}
                            type="file"
                            accept="image/png,image/jpeg,image/webp,image/heic,image/heif"
                            className="hidden"
                            onChange={(event) => {
                              void handleImageUpload(field, event.target.files?.[0] || null);
                              clearPersonalizationError(field.key);
                              event.currentTarget.value = "";
                            }}
                          />
                          <div
                            role="button"
                            tabIndex={0}
                            onClick={() => fileInputRefs.current[field.key]?.click()}
                            onKeyDown={(event) => {
                              if (event.key === "Enter" || event.key === " ") {
                                event.preventDefault();
                                fileInputRefs.current[field.key]?.click();
                              }
                            }}
                            onDragOver={(event) => {
                              event.preventDefault();
                            }}
                            onDrop={(event) => {
                              event.preventDefault();
                              void handleImageUpload(field, event.dataTransfer.files?.[0] || null);
                              clearPersonalizationError(field.key);
                            }}
                            className={`rounded-2xl border border-dashed bg-white px-4 py-5 text-center text-sm text-[var(--muted)] transition hover:border-[var(--accent-3)] hover:bg-[#fff8f8] ${
                              personalizationErrors[field.key] ? "border-[#b42318]" : "border-[var(--line)]"
                            }`}
                          >
                            <p className="font-medium text-[var(--foreground)]">
                              {uploadState?.pending ? "Upload en cours..." : "Glissez une image ici ou cliquez pour choisir"}
                            </p>
                            <p className="mt-1 text-xs">PNG, JPG, WEBP, HEIC, HEIF • 10 Mo max</p>
                          </div>
                          {uploadState?.error ? <p className="text-xs text-[#b42318]">{uploadState.error}</p> : null}
                          {personalizationErrors[field.key] ? (
                            <p className="text-xs text-[#b42318]">{personalizationErrors[field.key]}</p>
                          ) : null}
                          {previewUrl ? (
                            <div className="space-y-2">
                              <div className="relative h-28 w-28 overflow-hidden rounded-xl border border-[var(--line)]">
                                <Image src={previewUrl} alt={field.label} fill sizes="112px" className="object-cover" unoptimized />
                              </div>
                              <button
                                type="button"
                                onClick={() =>
                                  setSelectedCustomizations((current) => ({
                                    ...current,
                                    [field.key]: { label: field.label, value: "" },
                                  }))
                                }
                                className="text-xs font-medium text-[var(--accent-3)]"
                              >
                                Supprimer cette image
                              </button>
                            </div>
                          ) : null}
                        </div>
                      );
                    }

                    if (field.inputType === "color") {
                      const displayValue = selectedValue || field.defaultValue || "#d7c2a0";

                      return (
                        <div
                          key={field.key}
                          ref={(node) => {
                            personalizationFieldRefs.current[field.key] = node;
                          }}
                          className="space-y-2 text-sm"
                        >
                          {fieldHeader}
                          <div className={`flex items-center gap-3 rounded-2xl border bg-white px-3 py-2 ${
                            personalizationErrors[field.key] ? "border-[#b42318]" : "border-[var(--line)]"
                          }`}>
                            <input
                              type="color"
                              value={displayValue}
                              onChange={(event) => {
                                clearPersonalizationError(field.key);
                                setSelectedCustomizations((current) => ({
                                  ...current,
                                  [field.key]: { label: field.label, value: event.target.value },
                                }));
                              }}
                              className="h-11 w-14 cursor-pointer rounded-lg border-0 bg-transparent p-0"
                            />
                            <div className="space-y-1">
                              <p className="text-sm font-medium text-[var(--foreground)]">{displayValue.toUpperCase()}</p>
                              <p className="text-xs text-[var(--muted)]">Cliquez pour choisir une couleur</p>
                            </div>
                          </div>
                          {personalizationErrors[field.key] ? (
                            <p className="text-xs text-[#b42318]">{personalizationErrors[field.key]}</p>
                          ) : null}
                        </div>
                      );
                    }

                    return (
                      <label
                        key={field.key}
                        ref={(node) => {
                          personalizationFieldRefs.current[field.key] = node;
                        }}
                        className="block space-y-1 text-sm"
                      >
                        {fieldHeader}
                        {field.inputType === "textarea" ? (
                          <textarea
                            rows={3}
                            value={selectedValue}
                            placeholder={field.placeholder || undefined}
                            maxLength={field.maxLength || undefined}
                            onChange={(event) => {
                              clearPersonalizationError(field.key);
                              setSelectedCustomizations((current) => ({
                                ...current,
                                [field.key]: { label: field.label, value: event.target.value },
                              }));
                            }}
                            className={`w-full rounded-xl border bg-white px-3 py-2 outline-none focus:border-[#f5505080] ${
                              personalizationErrors[field.key] ? "border-[#b42318]" : "border-[var(--line)]"
                            }`}
                          />
                        ) : (
                          <input
                            type={resolvePersonalizationInputType(field.inputType)}
                            value={selectedValue}
                            placeholder={field.placeholder || undefined}
                            maxLength={field.maxLength || undefined}
                            onChange={(event) => {
                              clearPersonalizationError(field.key);
                              setSelectedCustomizations((current) => ({
                                ...current,
                                [field.key]: { label: field.label, value: event.target.value },
                              }));
                            }}
                            className={`w-full rounded-xl border bg-white px-3 py-2 outline-none focus:border-[#f5505080] ${
                              personalizationErrors[field.key] ? "border-[#b42318]" : "border-[var(--line)]"
                            }`}
                          />
                        )}
                        {personalizationErrors[field.key] ? (
                          <p className="text-xs text-[#b42318]">{personalizationErrors[field.key]}</p>
                        ) : null}
                      </label>
                    );
                  })}
                  <input type="hidden" name="customizations" value={customizationPayload} />
                </div>
              ) : null}

              {supportsThemeChoice && (!hasStructuredVariantChecks || !structuredHasThemeGroup) ? (
                <div className="space-y-2 text-sm">
                  <span className="block font-medium text-[var(--foreground)]">Theme</span>
                  {hasThemeImages ? (
                    <div className="grid grid-cols-2 gap-3 sm:grid-cols-3">
                      {themeDetails.map((theme) => {
                        const isSelected = !usesCustomTheme && selectedTheme === theme.title;
                        return (
                          <button
                            key={theme.title}
                            type="button"
                            onClick={() => {
                              setUsesCustomTheme(false);
                              setSelectedTheme(theme.title);
                            }}
                            className={`overflow-hidden rounded-2xl border bg-white text-left transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent-2)] focus-visible:ring-offset-2 active:scale-[0.985] ${
                              isSelected
                                ? "border-[var(--accent-3)] shadow-[inset_0_0_0_1px_var(--accent-3)]"
                                : "border-[var(--line)] hover:border-[var(--accent-3)] hover:bg-[#fff8f8]"
                            }`}
                          >
                            <div className="relative aspect-square bg-[#f7f2ee]">
                              {theme.imageUrl ? (
                                <Image
                                  src={theme.imageUrl}
                                  alt={theme.title}
                                  fill
                                  sizes="(max-width: 640px) 50vw, 33vw"
                                  className="object-cover"
                                  unoptimized
                                />
                              ) : null}
                            </div>
                            <div className="px-3 py-2">
                              <span className="line-clamp-2 block text-xs font-medium text-[var(--foreground)]">{theme.title}</span>
                            </div>
                          </button>
                        );
                      })}
                    </div>
                  ) : (
                    <div className="flex flex-wrap gap-2">
                      {themeOptions.map((theme) => {
                        const isSelected = !usesCustomTheme && selectedTheme === theme;
                        return (
                          <button
                            key={theme}
                            type="button"
                            onClick={() => {
                              setUsesCustomTheme(false);
                              setSelectedTheme(theme);
                            }}
                            className={`rounded-full border px-3 py-1.5 text-xs font-medium transition focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-[var(--accent-2)] focus-visible:ring-offset-2 active:scale-[0.98] ${
                              isSelected
                                ? "border-[var(--accent-3)] bg-[var(--accent-3)] text-white"
                                : "border-[#d7d7d7] bg-white text-[#262626] hover:border-[var(--accent-3)] hover:bg-[#fff4f4]"
                            }`}
                          >
                            {theme}
                          </button>
                        );
                      })}
                    </div>
                  )}
                  {customThemeConfig.isEnabled ? (
                    <div className="space-y-2 rounded-2xl border border-dashed border-[var(--accent-3)] bg-[#fff8f8] p-3">
                      <button
                        type="button"
                        onClick={() => {
                          setUsesCustomTheme(true);
                          setIsCustomThemeModalOpen(true);
                        }}
                        className={`inline-flex rounded-xl border px-4 py-2 text-sm font-semibold transition ${
                          usesCustomTheme
                            ? "border-[var(--accent-3)] bg-[var(--accent-3)] text-white"
                            : "border-[var(--accent-3)] bg-white text-[var(--accent-3)] hover:bg-[#fff1f1]"
                        }`}
                      >
                        {usesCustomTheme ? "Modifier mon thème personnalisé" : customThemeConfig.optionLabel}
                      </button>
                      {customThemeConfig.helperText ? <p className="text-xs text-[var(--muted)]">{customThemeConfig.helperText}</p> : null}
                      {usesCustomTheme && customThemeSummary.length > 0 ? (
                        <div className="space-y-1 text-xs text-[var(--muted)]">
                          {customThemeSummary.slice(0, 3).map((line) => (
                            <p key={line}>{line}</p>
                          ))}
                        </div>
                      ) : null}
                    </div>
                  ) : null}
                  <input type="hidden" name="theme" value={selectedThemeValue} />
                </div>
              ) : null}

              {!supportsThemeChoice && customThemeConfig.isEnabled ? (
                <div className="space-y-2 rounded-2xl border border-dashed border-[var(--accent-3)] bg-[#fff8f8] p-3 text-sm">
                  <span className="block font-medium text-[var(--foreground)]">Thème sur mesure</span>
                  <button
                    type="button"
                    onClick={() => {
                      setUsesCustomTheme(true);
                      setIsCustomThemeModalOpen(true);
                    }}
                    className={`inline-flex rounded-xl border px-4 py-2 text-sm font-semibold transition ${
                      usesCustomTheme
                        ? "border-[var(--accent-3)] bg-[var(--accent-3)] text-white"
                        : "border-[var(--accent-3)] bg-white text-[var(--accent-3)] hover:bg-[#fff1f1]"
                    }`}
                  >
                    {usesCustomTheme ? "Modifier mon thème personnalisé" : customThemeConfig.optionLabel}
                  </button>
                  {customThemeConfig.helperText ? <p className="text-xs text-[var(--muted)]">{customThemeConfig.helperText}</p> : null}
                  {usesCustomTheme && customThemeSummary.length > 0 ? (
                    <div className="space-y-1 text-xs text-[var(--muted)]">
                      {customThemeSummary.slice(0, 3).map((line) => (
                        <p key={line}>{line}</p>
                      ))}
                    </div>
                  ) : null}
                  <input type="hidden" name="theme" value={selectedThemeValue} />
                </div>
              ) : null}

              {!hasStructuredVariantChecks && supportsPaperFinishChoice ? (
                <div
                  ref={paperFinishFieldRef}
                  className="space-y-2 text-sm"
                >
                  <span className="block font-medium text-[var(--foreground)]">Effet</span>
                  <div className="flex flex-wrap gap-2">
                    {paperFinishOptions.map((finish) => {
                      const isSelected = selectedPaperFinish === finish;
                      return (
                        <button
                          key={finish}
                          type="button"
                          onClick={() => {
                            clearPersonalizationError("paperFinish");
                            setSelectedPaperFinish(finish);
                          }}
                          className={`rounded-full border px-3 py-1.5 text-xs font-medium transition ${
                            isSelected
                              ? "border-[var(--accent-3)] bg-[var(--accent-3)] text-white"
                              : personalizationErrors.paperFinish
                                ? "border-[#b42318] bg-white text-[#262626] hover:border-[#b42318]"
                                : "border-[#d7d7d7] bg-white text-[#262626] hover:border-[#a8a8a8]"
                          }`}
                        >
                          {finish}
                        </button>
                      );
                    })}
                  </div>
                  {personalizationErrors.paperFinish ? (
                    <p className="text-xs text-[#b42318]">{personalizationErrors.paperFinish}</p>
                  ) : null}
                  <input type="hidden" name="paperFinish" value={selectedPaperFinish} />
                </div>
              ) : null}

              {personalizationTextField.isEnabled ? (
                <label className="block space-y-1 text-sm">
                  {personalizationTextField.showLabel ? (
                    <span className="font-medium text-[var(--foreground)]">{personalizationTextField.label}</span>
                  ) : null}
                  {personalizationTextField.helperText ? (
                    <span className="text-xs text-[var(--muted)]">{personalizationTextField.helperText}</span>
                  ) : null}
                  <textarea
                    ref={personalizationTextareaRef}
                    name="personalization"
                    rows={3}
                    placeholder={personalizationTextField.placeholder}
                    defaultValue={personalizationTextField.defaultValue || undefined}
                    maxLength={personalizationTextField.maxLength}
                    required={personalizationTextField.required}
                    onChange={() => clearPersonalizationError("personalization")}
                    className={`w-full rounded-xl border bg-white px-3 py-2 outline-none focus:border-[#f5505080] ${
                      personalizationErrors.personalization ? "border-[#b42318]" : "border-[var(--line)]"
                    }`}
                  />
                  {personalizationErrors.personalization ? (
                    <p className="text-xs text-[#b42318]">{personalizationErrors.personalization}</p>
                  ) : null}
                </label>
              ) : null}
            </section>
          ) : null}

          {!hasCustomPersonalizationFields ? <input type="hidden" name="customizations" value={customizationPayload} /> : null}

          <button
            type="submit"
            className="w-full rounded-xl bg-[var(--accent-3)] px-4 py-3 text-sm font-semibold text-white transition hover:bg-[var(--accent-2)]"
          >
            {t("product.addToCart")}
          </button>
          </form>
        </div>
      </div>
      <ProductTabs
        descriptionText={activeDescriptionText}
        richDescription={activeRichDescription}
        characteristics={characteristics}
        personalizationText={personalizationText}
        showPersonalizationTab={showPersonalizationTab}
      />
      {isCustomThemeModalOpen ? (
        <div
          className="fixed inset-0 z-50 h-[100dvh] w-full overflow-y-auto overscroll-contain bg-[rgba(24,18,18,0.6)] px-4 py-4 [padding-bottom:max(1rem,env(safe-area-inset-bottom))] sm:px-6 sm:py-6"
          onClick={() => setIsCustomThemeModalOpen(false)}
        >
          <div className="flex min-h-[100dvh] items-start justify-center sm:items-center">
            <div
              className="max-h-[calc(100dvh-2rem)] w-full max-w-2xl overflow-y-auto rounded-[1.8rem] bg-white p-5 shadow-[0_24px_80px_rgba(24,18,18,0.28)] [scrollbar-color:var(--accent-3)_transparent] [scrollbar-width:thin] [&::-webkit-scrollbar]:w-2 [&::-webkit-scrollbar-track]:bg-transparent [&::-webkit-scrollbar-thumb]:rounded-full [&::-webkit-scrollbar-thumb]:bg-[rgba(166,89,72,0.55)] [&::-webkit-scrollbar-thumb:hover]:bg-[rgba(166,89,72,0.78)] md:max-h-[calc(100dvh-3rem)] md:p-6"
              onClick={(event) => event.stopPropagation()}
            >
              <div className="flex items-start justify-between gap-4">
                <div className="space-y-2">
                  <p className="text-xs font-semibold uppercase tracking-[0.16em] text-[var(--accent-3)]">Thème sur mesure</p>
                  <h3 className="font-serif text-3xl text-[var(--foreground)]">{customThemeConfig.modalTitle}</h3>
                  <p className="text-sm text-[var(--muted)]">{customThemeConfig.modalDescription}</p>
                </div>
                <button
                  type="button"
                  onClick={() => setIsCustomThemeModalOpen(false)}
                  className="rounded-full border border-[var(--line)] px-3 py-1 text-sm font-medium text-[var(--foreground)]"
                >
                  Fermer
                </button>
              </div>

              <div className="mt-5 space-y-4 pb-1">
                {customThemeFields.map(renderCustomizationField)}
              </div>

              <div className="sticky bottom-0 mt-6 flex min-h-[88px] flex-wrap items-center justify-between gap-3 rounded-[1.35rem] border border-[rgba(166,89,72,0.22)] bg-[rgba(252,248,246,0.98)] px-4 py-4 shadow-[0_-10px_24px_rgba(24,18,18,0.06)] backdrop-blur">
                <button
                  type="button"
                  onClick={() => {
                    setUsesCustomTheme(false);
                    setIsCustomThemeModalOpen(false);
                  }}
                  className="rounded-xl border border-[var(--line)] px-4 py-2 text-sm font-semibold text-[var(--foreground)] hover:bg-[#faf6f6]"
                >
                  {supportsThemeChoice ? "Revenir aux themes existants" : "Annuler"}
                </button>
                <button
                  type="button"
                  onClick={() => {
                    setUsesCustomTheme(true);
                    setIsCustomThemeModalOpen(false);
                  }}
                  className="rounded-xl bg-[var(--accent-3)] px-4 py-2 text-sm font-semibold text-white hover:bg-[var(--accent-2)]"
                >
                  Enregistrer ma personnalisation
                </button>
              </div>
            </div>
          </div>
        </div>
      ) : null}
    </>
  );
}
