<script setup lang="ts">
import { useQuery } from '@vue/apollo-composable';
import {
  CatalogProductTypeSettingsDocument,
  ClientProductsDocument,
  type CatalogProductTypeSettingsQuery,
  type ClientProductsQuery,
} from '~/composables/graphql/generated';
import { useClientCart } from '~/composables/useClientCart';

const props = defineProps<{
  productTypeSlug: string;
}>();

type ProductNode = ClientProductsQuery['clientProducts'][number];
type CatalogProductTypeSettingNode = CatalogProductTypeSettingsQuery['catalogProductTypeSettings'][number];
type ParamFieldKey = 'widthMm' | 'lengthM' | 'thicknessMicron' | 'sleeveBrand' | 'quantityPerBox' | 'colorTag' | 'labelTag';
type ParamValue = number | string;

type ParsedProduct = ProductNode & {
  productTypeLabel: string;
  quantityPerBoxOptions: string[];
  normalizedTags: string[];
  colorTags: string[];
  labelTags: string[];
};

type ProductGroup = {
  key: string;
  typeLabel: string;
  products: ParsedProduct[];
};

type GroupState = {
  widthMm: number | null;
  lengthM: number | null;
  thicknessMicron: number | null;
  sleeveBrand: string | null;
  quantityPerBox: string | null;
  colorTag: string | null;
  labelTag: string | null;
};

const COLOR_TAGS = ['прозрачный', 'коричневый', 'белый', 'черный', 'желтый', 'зеленый', 'красный', 'синий', 'оранжевый', 'красно-белый'];
const LABEL_TAGS = ['хрупкое', 'подарок', 'акция'];
const PARAM_KEYS: ParamFieldKey[] = ['widthMm', 'lengthM', 'thicknessMicron', 'sleeveBrand', 'quantityPerBox', 'colorTag', 'labelTag'];
const DEFAULT_CATALOG_PRODUCT_TYPE_SETTING: CatalogProductTypeSettingNode = {
  productType: '',
  showQuantityPerBox: false,
  allowCustomLength: false,
  customLengthMinM: null,
  customLengthMaxM: null,
  customLengthStepM: null,
  allowCustomSleeveBrand: false,
  allowCustomLabel: false,
  widthOptionsMm: [],
  lengthOptionsM: [],
  thicknessOptionsMicron: [],
  sleeveOptions: [],
  colorOptions: [],
  labelOptions: [],
};
const parameterFields: Array<{ key: ParamFieldKey; label: string }> = [
  { key: 'widthMm', label: 'Ширина' },
  { key: 'lengthM', label: 'Длина' },
  { key: 'thicknessMicron', label: 'Толщина' },
  { key: 'sleeveBrand', label: 'Втулка' },
  { key: 'quantityPerBox', label: 'Короб' },
  { key: 'colorTag', label: 'Цвет' },
  { key: 'labelTag', label: 'Надпись' },
];

const coverPresets = [
  ['#d9f5e6', '#9ce8c1', '#6fd09d'],
  ['#eaf9ef', '#b3e8cb', '#76c89f'],
  ['#e8f5ec', '#b2e0c6', '#7dd0a9'],
];

const productsQuery = useQuery(ClientProductsDocument);
const catalogSettingsQuery = useQuery(CatalogProductTypeSettingsDocument);
const groupStates = reactive<Record<string, GroupState>>({});
const { addProduct, getQuantity, incrementQuantity, decrementQuantity } = useClientCart();

const loading = computed(() => productsQuery.loading.value || catalogSettingsQuery.loading.value);
const error = computed(() => productsQuery.error.value || catalogSettingsQuery.error.value);

const catalogSettingsByType = computed<Record<string, CatalogProductTypeSettingNode>>(() => (
  Object.fromEntries(
    (catalogSettingsQuery.result.value?.catalogProductTypeSettings ?? [])
      .map((setting) => [setting.productType, setting]),
  )
));

function normalizeText(value: string | null | undefined) {
  return String(value ?? '').replaceAll(/\s+/g, ' ').trim();
}

function splitBoxValues(value: string | null | undefined) {
  return normalizeText(value)
    .split('/')
    .map((item) => item.trim())
    .filter(Boolean);
}

function createProductCover(name: string, sku: string) {
  const seed = `${name}${sku}`.split('').reduce((acc, char) => acc + char.charCodeAt(0), 0);
  const [start, middle, finish] = coverPresets[seed % coverPresets.length];
  const firstLetter = name.trim().charAt(0).toUpperCase() || 'P';

  const svg = `
    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 220">
      <defs>
        <linearGradient id="g" x1="0" y1="0" x2="1" y2="1">
          <stop offset="0%" stop-color="${start}" />
          <stop offset="55%" stop-color="${middle}" />
          <stop offset="100%" stop-color="${finish}" />
        </linearGradient>
      </defs>
      <rect width="320" height="220" fill="url(#g)" rx="22" />
      <g opacity="0.15">
        <circle cx="266" cy="45" r="55" fill="#0f7a49" />
        <circle cx="42" cy="198" r="55" fill="#0f7a49" />
      </g>
      <text x="50%" y="56%" text-anchor="middle" fill="#11412c" font-family="Manrope, sans-serif" font-size="84" font-weight="700">${firstLetter}</text>
    </svg>
  `.trim();

  return `data:image/svg+xml;utf8,${encodeURIComponent(svg)}`;
}

function hydrateProduct(product: ProductNode): ParsedProduct {
  const normalizedTags = product.tags.map((tag) => normalizeText(tag)).filter(Boolean).sort((a, b) => a.localeCompare(b, 'ru'));

  return {
    ...product,
    productTypeLabel: normalizeText(product.productType) || 'Без типа',
    quantityPerBoxOptions: splitBoxValues(product.quantityPerBox),
    normalizedTags,
    colorTags: normalizedTags.filter((tag) => COLOR_TAGS.includes(tag)),
    labelTags: normalizedTags.filter((tag) => LABEL_TAGS.includes(tag)),
  };
}

function productSortValue(value: number | null | undefined) {
  return value ?? Number.MAX_SAFE_INTEGER;
}

function compareProducts(a: ParsedProduct, b: ParsedProduct) {
  const byWidth = productSortValue(a.widthMm) - productSortValue(b.widthMm);
  if (byWidth !== 0) {
    return byWidth;
  }

  const byLength = productSortValue(a.lengthM) - productSortValue(b.lengthM);
  if (byLength !== 0) {
    return byLength;
  }

  const byThickness = productSortValue(a.thicknessMicron) - productSortValue(b.thicknessMicron);
  if (byThickness !== 0) {
    return byThickness;
  }

  const bySleeve = normalizeText(a.sleeveBrand).localeCompare(normalizeText(b.sleeveBrand), 'ru');
  if (bySleeve !== 0) {
    return bySleeve;
  }

  return a.sku.localeCompare(b.sku, 'ru');
}

const parsedProducts = computed<ParsedProduct[]>(() => {
  const list = productsQuery.result.value?.clientProducts ?? [];

  return list
    .map(hydrateProduct)
    .sort(compareProducts);
});

const productGroups = computed<ProductGroup[]>(() => {
  const map = new Map<string, ParsedProduct[]>();

  for (const product of parsedProducts.value) {
    const existing = map.get(product.productTypeLabel);
    if (existing) {
      existing.push(product);
    } else {
      map.set(product.productTypeLabel, [product]);
    }
  }

  return [...map.entries()]
    .sort((a, b) => a[0].localeCompare(b[0], 'ru'))
    .map(([typeLabel, products]) => ({
      key: typeLabel
        .toLowerCase()
        .replaceAll(/[^a-z0-9а-яё]+/gi, '-')
        .replaceAll(/-+/g, '-')
        .replaceAll(/^-|-$/g, ''),
      typeLabel,
      products: [...products].sort(compareProducts),
    }));
});

function sortParamValues(values: ParamValue[]) {
  return [...values].sort((a, b) => {
    if (typeof a === 'number' && typeof b === 'number') {
      return a - b;
    }
    return String(a).localeCompare(String(b), 'ru');
  });
}

function getAllFieldOptions(group: ProductGroup, field: ParamFieldKey) {
  const values = new Set<ParamValue>();

  for (const product of group.products) {
    if (field === 'quantityPerBox') {
      for (const option of product.quantityPerBoxOptions) {
        values.add(option);
      }
      continue;
    }

    if (field === 'colorTag') {
      for (const tag of product.colorTags) {
        values.add(tag);
      }
      continue;
    }

    if (field === 'labelTag') {
      for (const tag of product.labelTags) {
        values.add(tag);
      }
      continue;
    }

    const value = product[field];
    if (value !== null && value !== undefined) {
      values.add(value);
    }
  }

  return sortParamValues([...values]);
}

function groupCatalogSetting(group: ProductGroup) {
  return catalogSettingsByType.value[group.typeLabel] ?? {
    ...DEFAULT_CATALOG_PRODUCT_TYPE_SETTING,
    productType: group.typeLabel,
  };
}

function visibleFields(group: ProductGroup) {
  return parameterFields.filter((field) => {
    if (field.key === 'quantityPerBox') {
      return false;
    }

    return getAllFieldOptions(group, field.key).length > 1;
  });
}

const selectedGroup = computed(() => productGroups.value.find((group) => group.key === props.productTypeSlug) ?? null);
const currentGroupIndex = computed(() => productGroups.value.findIndex((group) => group.key === props.productTypeSlug));
const previousGroup = computed(() => {
  if (currentGroupIndex.value <= 0) {
    return null;
  }

  return productGroups.value[currentGroupIndex.value - 1] ?? null;
});
const nextGroup = computed(() => {
  if (currentGroupIndex.value < 0 || currentGroupIndex.value >= productGroups.value.length - 1) {
    return null;
  }

  return productGroups.value[currentGroupIndex.value + 1] ?? null;
});

function requiredKeys(group: ProductGroup) {
  return visibleFields(group).map((field) => field.key);
}

function createGroupState(group: ProductGroup): GroupState {
  const firstProduct = group.products[0];

  return {
    widthMm: firstProduct?.widthMm ?? null,
    lengthM: firstProduct?.lengthM ?? null,
    thicknessMicron: firstProduct?.thicknessMicron ?? null,
    sleeveBrand: firstProduct?.sleeveBrand ?? null,
    quantityPerBox: firstProduct?.quantityPerBoxOptions[0] ?? null,
    colorTag: firstProduct?.colorTags[0] ?? null,
    labelTag: firstProduct?.labelTags[0] ?? null,
  };
}

watch(
  productGroups,
  (groups) => {
    const keys = new Set(groups.map((group) => group.key));

    for (const key of Object.keys(groupStates)) {
      if (!keys.has(key)) {
        Reflect.deleteProperty(groupStates, key);
      }
    }

    for (const group of groups) {
      groupStates[group.key] ??= createGroupState(group);
    }
  },
  { immediate: true },
);

function getGroupState(group: ProductGroup) {
  groupStates[group.key] ??= createGroupState(group);
  return groupStates[group.key];
}

function matchesProductState(product: ParsedProduct, state: GroupState, keys: ParamFieldKey[]) {
  return keys.every((key) => {
    if (state[key] === null) {
      return true;
    }

    if (key === 'quantityPerBox') {
      return product.quantityPerBoxOptions.includes(String(state[key]));
    }

    if (key === 'colorTag') {
      return product.colorTags.includes(String(state[key]));
    }

    if (key === 'labelTag') {
      return product.labelTags.includes(String(state[key]));
    }

    return product[key] === state[key];
  });
}

function selectedProduct(group: ProductGroup) {
  const keys = requiredKeys(group);
  const state = getGroupState(group);

  if (keys.length === 0) {
    return group.products[0] ?? null;
  }

  if (keys.some((key) => state[key] === null)) {
    return null;
  }

  const matches = group.products.filter((product) => matchesProductState(product, state, keys));
  return matches[0] ?? null;
}

function formatOptionLabel(field: ParamFieldKey, value: ParamValue) {
  if (field === 'widthMm') {
    return `${value} мм`;
  }
  if (field === 'lengthM') {
    return `${value} м`;
  }
  if (field === 'thicknessMicron') {
    return `${value} мкм`;
  }
  return String(value);
}

function productHasOption(product: ParsedProduct, field: ParamFieldKey, option: ParamValue) {
  if (field === 'quantityPerBox') {
    return product.quantityPerBoxOptions.includes(String(option));
  }

  if (field === 'colorTag') {
    return product.colorTags.includes(String(option));
  }

  if (field === 'labelTag') {
    return product.labelTags.includes(String(option));
  }

  return product[field] === option;
}

function isOptionAvailable(group: ProductGroup, field: ParamFieldKey, option: ParamValue) {
  const state = getGroupState(group);
  const scopedState = {
    ...state,
    [field]: option,
  } satisfies GroupState;

  return group.products.some((product) => matchesProductState(product, scopedState, requiredKeys(group)));
}

function pickBestProductForOption(group: ProductGroup, field: ParamFieldKey, option: ParamValue) {
  const state = getGroupState(group);
  const candidates = group.products.filter((product) => productHasOption(product, field, option));

  if (candidates.length === 0) {
    return null;
  }

  return [...candidates].sort((a, b) => {
    let aScore = 0;
    let bScore = 0;

    for (const key of PARAM_KEYS) {
      if (key === field) {
        continue;
      }

      const selectedValue = state[key];
      if (selectedValue === null) {
        continue;
      }

      if (productHasOption(a, key, selectedValue)) {
        aScore += 1;
      }
      if (productHasOption(b, key, selectedValue)) {
        bScore += 1;
      }
    }

    if (aScore !== bScore) {
      return bScore - aScore;
    }

    return compareProducts(a, b);
  })[0];
}

function applyProductToState(state: GroupState, product: ParsedProduct, preferredBoxOption: ParamValue | null = null) {
  state.widthMm = product.widthMm ?? null;
  state.lengthM = product.lengthM ?? null;
  state.thicknessMicron = product.thicknessMicron ?? null;
  state.sleeveBrand = product.sleeveBrand ?? null;
  state.colorTag = product.colorTags[0] ?? null;
  state.labelTag = product.labelTags[0] ?? null;

  if (preferredBoxOption !== null && product.quantityPerBoxOptions.includes(String(preferredBoxOption))) {
    state.quantityPerBox = String(preferredBoxOption);
    return;
  }

  state.quantityPerBox = product.quantityPerBoxOptions[0] ?? null;
}

function updateField(group: ProductGroup, field: ParamFieldKey, value: ParamValue) {
  const state = getGroupState(group);
  const fallback = pickBestProductForOption(group, field, value);
  if (!fallback) {
    return;
  }

  applyProductToState(state, fallback, field === 'quantityPerBox' ? value : state.quantityPerBox);

  if (field === 'quantityPerBox') {
    state.quantityPerBox = String(value);
    return;
  }

  state[field] = value as GroupState[typeof field];
}

function articleLabel(group: ProductGroup) {
  return selectedProduct(group)?.sku ?? '—';
}

function formatLengthRange(setting: CatalogProductTypeSettingNode) {
  if (!setting.customLengthMinM || !setting.customLengthMaxM || !setting.customLengthStepM) {
    return null;
  }

  return `${setting.customLengthMinM}-${setting.customLengthMaxM} м, шаг ${setting.customLengthStepM} м`;
}

function fieldHelperText(group: ProductGroup, field: ParamFieldKey) {
  const setting = groupCatalogSetting(group);

  if (field === 'widthMm') {
    return 'Ширина рулона.';
  }

  if (field === 'lengthM') {
    const customRange = formatLengthRange(setting);
    if (setting.allowCustomLength && customRange) {
      return `Стандартная длина или своя: ${customRange}.`;
    }

    return 'Длина рулона в метрах.';
  }

  if (field === 'thicknessMicron') {
    return 'Толщина материала.';
  }

  if (field === 'sleeveBrand') {
    if (setting.allowCustomSleeveBrand) {
      return 'Можно выбрать стандартную втулку или сделать с логотипом.';
    }

    return 'Тип втулки внутри рулона.';
  }

  if (field === 'colorTag') {
    return 'Цвет ленты.';
  }

  if (field === 'labelTag') {
    if (setting.allowCustomLabel) {
      return 'Стандартная маркировка или своя надпись.';
    }

    return 'Готовая надпись или маркировка.';
  }

  return 'Параметр товара.';
}

function customizationDetails(group: ProductGroup) {
  const setting = groupCatalogSetting(group);
  const details: string[] = [];
  const customRange = formatLengthRange(setting);

  if (setting.allowCustomLength && customRange) {
    details.push(`Длина под заказ: ${customRange}.`);
  }

  if (setting.allowCustomSleeveBrand) {
    details.push('Втулка под заказ: можно сделать с вашим логотипом.');
  }

  if (setting.allowCustomLabel) {
    details.push('Надпись под заказ: можно нанести вашу маркировку или текст.');
  }

  return details;
}

function selectedSummary(group: ProductGroup) {
  return visibleFields(group)
    .map((field) => {
      const value = getGroupState(group)[field.key];
      if (value === null) {
        return null;
      }

      return `${field.label}: ${formatOptionLabel(field.key, value)}`;
    })
    .filter((item): item is string => Boolean(item));
}

function productBadges(product: ParsedProduct) {
  const badges: string[] = [];

  if (product.widthMm !== null) {
    badges.push(`Ширина: ${product.widthMm} мм`);
  }
  if (product.lengthM !== null) {
    badges.push(`Длина: ${product.lengthM} м`);
  }
  if (product.thicknessMicron !== null) {
    badges.push(`Толщина: ${product.thicknessMicron} мкм`);
  }
  if (product.sleeveBrand) {
    badges.push(`Втулка: ${product.sleeveBrand}`);
  }
  if (product.colorTags[0]) {
    badges.push(`Цвет: ${product.colorTags[0]}`);
  }
  if (product.labelTags[0]) {
    badges.push(`Надпись: ${product.labelTags[0]}`);
  }

  return badges;
}

function incrementProduct(product: ProductNode) {
  if (getQuantity(product.id) === 0) {
    addProduct({
      id: product.id,
      name: product.name,
      sku: product.sku,
      isCustomizable: product.isCustomizable,
    });
    return;
  }

  incrementQuantity(product.id);
}

function decrementProduct(productId: string) {
  decrementQuantity(productId);
}

function selectedQty(group: ProductGroup) {
  const product = selectedProduct(group);
  return product ? getQuantity(product.id) : 0;
}

function incrementSelected(group: ProductGroup) {
  const product = selectedProduct(group);
  if (product) {
    incrementProduct(product);
  }
}

function decrementSelected(group: ProductGroup) {
  const product = selectedProduct(group);
  if (product) {
    decrementProduct(product.id);
  }
}

function productDetailPath(group: ProductGroup) {
  return `/products/${group.key}`;
}
</script>

<template>
  <section class="space-y-5">
    <div v-if="loading" class="alert surface-card border-0">Загрузка каталога...</div>
    <div v-else-if="error" class="alert alert-error">{{ error.message }}</div>
    <div v-else-if="selectedGroup" class="relative mx-auto max-w-[1380px] px-1 pb-10 sm:px-0">
      <NuxtLink
        v-if="previousGroup"
        :to="productDetailPath(previousGroup)"
        class="absolute left-0 top-28 z-10 hidden w-44 -translate-x-[72%] rounded-[28px] border border-[#e6efe9] bg-white p-3 shadow-[0_20px_40px_rgba(18,56,36,0.08)] transition hover:-translate-x-[76%] hover:shadow-[0_28px_48px_rgba(18,56,36,0.12)] 2xl:block"
      >
        <img
          :src="createProductCover(previousGroup.typeLabel, previousGroup.key)"
          :alt="`Перейти к товару ${previousGroup.typeLabel}`"
          class="aspect-square w-full rounded-[20px] object-cover"
          loading="lazy"
        >
        <p class="mt-3 text-sm font-semibold leading-5 text-[#163624]">{{ previousGroup.typeLabel }}</p>
      </NuxtLink>

      <NuxtLink
        v-if="nextGroup"
        :to="productDetailPath(nextGroup)"
        class="absolute right-0 top-28 z-10 hidden w-44 translate-x-[72%] rounded-[28px] border border-[#e6efe9] bg-white p-3 shadow-[0_20px_40px_rgba(18,56,36,0.08)] transition hover:translate-x-[76%] hover:shadow-[0_28px_48px_rgba(18,56,36,0.12)] 2xl:block"
      >
        <img
          :src="createProductCover(nextGroup.typeLabel, nextGroup.key)"
          :alt="`Перейти к товару ${nextGroup.typeLabel}`"
          class="aspect-square w-full rounded-[20px] object-cover"
          loading="lazy"
        >
        <p class="mt-3 text-sm font-semibold leading-5 text-[#163624]">{{ nextGroup.typeLabel }}</p>
      </NuxtLink>

      <header class="mb-5 flex items-center gap-4 px-3 sm:px-6 xl:px-8">
        <NuxtLink
          to="/products"
          class="inline-flex h-11 w-11 shrink-0 items-center justify-center rounded-full border border-[#dce9e1] bg-white text-xl text-[#163624] shadow-[0_10px_24px_rgba(18,56,36,0.06)] transition hover:-translate-y-0.5"
          aria-label="Назад к списку товаров"
        >
          ←
        </NuxtLink>

        <div class="min-w-0">
          <h1 class="text-3xl font-bold leading-tight text-[#163624] md:text-[2.5rem]">{{ selectedGroup.typeLabel }}</h1>
        </div>
      </header>

      <div class="mb-5 grid gap-3 px-3 sm:px-6 2xl:hidden">
        <NuxtLink
          v-if="previousGroup"
          :to="productDetailPath(previousGroup)"
          class="flex items-center gap-3 rounded-[24px] border border-[#e6efe9] bg-white p-3 shadow-[0_14px_30px_rgba(18,56,36,0.06)]"
        >
          <img
            :src="createProductCover(previousGroup.typeLabel, previousGroup.key)"
            :alt="`Перейти к товару ${previousGroup.typeLabel}`"
            class="h-16 w-16 rounded-2xl object-cover"
            loading="lazy"
          >
          <span class="text-sm font-semibold text-[#163624]">{{ previousGroup.typeLabel }}</span>
        </NuxtLink>

        <NuxtLink
          v-if="nextGroup"
          :to="productDetailPath(nextGroup)"
          class="flex items-center gap-3 rounded-[24px] border border-[#e6efe9] bg-white p-3 shadow-[0_14px_30px_rgba(18,56,36,0.06)]"
        >
          <img
            :src="createProductCover(nextGroup.typeLabel, nextGroup.key)"
            :alt="`Перейти к товару ${nextGroup.typeLabel}`"
            class="h-16 w-16 rounded-2xl object-cover"
            loading="lazy"
          >
          <span class="text-sm font-semibold text-[#163624]">{{ nextGroup.typeLabel }}</span>
        </NuxtLink>
      </div>

      <div class="grid gap-5 px-3 sm:px-6 xl:grid-cols-[minmax(0,0.92fr)_minmax(0,1.15fr)_320px] xl:px-8">
        <div class="space-y-4">
          <div class="overflow-hidden rounded-[32px] border border-[#e6efe9] bg-white p-4 shadow-[0_20px_40px_rgba(18,56,36,0.06)]">
            <img
              :src="createProductCover(selectedGroup.typeLabel, articleLabel(selectedGroup))"
              :alt="selectedGroup.typeLabel"
              class="aspect-[5/4] w-full rounded-[26px] object-cover"
              loading="lazy"
            >
          </div>

          <div
            v-if="selectedSummary(selectedGroup).length"
            class="rounded-[28px] border border-[#e6efe9] bg-white p-4 shadow-[0_18px_36px_rgba(18,56,36,0.05)]"
          >
            <div class="flex flex-wrap gap-2">
              <span
                v-for="item in selectedSummary(selectedGroup)"
                :key="`${selectedGroup.key}-${item}`"
                class="rounded-full border border-[#dce9e1] bg-[#f8fbf9] px-3 py-1.5 text-sm font-medium text-[#355947]"
              >
                {{ item }}
              </span>
            </div>
          </div>

          <div
            v-if="customizationDetails(selectedGroup).length"
            class="rounded-[28px] border border-[#dce9e1] bg-[#f7fbf8] p-4"
          >
            <div class="space-y-2">
              <p
                v-for="note in customizationDetails(selectedGroup)"
                :key="`${selectedGroup.key}-${note}`"
                class="text-sm leading-6 text-[#456555]"
              >
                {{ note }}
              </p>
            </div>
          </div>
        </div>

        <div class="space-y-4">
          <article
            v-for="field in visibleFields(selectedGroup)"
            :key="`${selectedGroup.key}-${field.key}`"
            class="rounded-[28px] border border-[#e6efe9] bg-white p-5 shadow-[0_18px_36px_rgba(18,56,36,0.05)]"
          >
            <p class="text-base font-semibold text-[#163624]">{{ field.label }}</p>

            <div class="mt-3 flex flex-wrap gap-2">
              <label
                v-for="option in getAllFieldOptions(selectedGroup, field.key)"
                :key="`${selectedGroup.key}-${field.key}-${option}`"
                class="cursor-pointer rounded-2xl border px-4 py-2 text-sm font-medium transition"
                :class="[
                  getGroupState(selectedGroup)[field.key] === option
                    ? 'border-[#163624] bg-[#163624] text-white shadow-[0_12px_24px_rgba(22,54,36,0.18)]'
                    : isOptionAvailable(selectedGroup, field.key, option)
                      ? 'border-[#dce9e1] bg-white text-[#163624] hover:border-[#163624]'
                      : 'border-[#e6eaee] bg-[#f3f5f7] text-[#8a949d]',
                ]"
              >
                <input
                  type="radio"
                  class="sr-only"
                  :name="`${selectedGroup.key}-${field.key}`"
                  :checked="getGroupState(selectedGroup)[field.key] === option"
                  @change="updateField(selectedGroup, field.key, option)"
                >
                <span>{{ formatOptionLabel(field.key, option) }}</span>
              </label>
            </div>

            <p class="mt-3 text-sm leading-6 text-[#607569]">{{ fieldHelperText(selectedGroup, field.key) }}</p>
          </article>
        </div>

        <aside class="self-start xl:sticky xl:top-24">
          <div class="rounded-[30px] border border-[#e6efe9] bg-white p-5 shadow-[0_24px_48px_rgba(18,56,36,0.08)]">
            <p class="text-xs font-semibold uppercase tracking-[0.18em] text-[#789182]">Артикул</p>
            <p class="mt-2 text-2xl font-bold leading-tight text-[#163624]">{{ articleLabel(selectedGroup) }}</p>

            <div
              v-if="selectedSummary(selectedGroup).length"
              class="mt-5 rounded-[24px] border border-[#edf4ef] bg-[#f8fbf9] p-4"
            >
              <div class="flex flex-wrap gap-2">
                <span
                  v-for="item in selectedSummary(selectedGroup)"
                  :key="`${selectedGroup.key}-summary-${item}`"
                  class="rounded-full bg-white px-3 py-1.5 text-xs font-semibold text-[#355947]"
                >
                  {{ item }}
                </span>
              </div>
            </div>

            <button
              v-if="selectedQty(selectedGroup) === 0"
              class="btn mt-5 h-12 w-full rounded-full border-0 bg-[#139957] px-6 text-base font-semibold text-white hover:bg-[#0d854a]"
              :disabled="!selectedProduct(selectedGroup)"
              @click="incrementSelected(selectedGroup)"
            >
              В корзину
            </button>

            <div
              v-else
              class="mt-5 flex items-center justify-between rounded-[24px] border border-[#dce9e1] bg-[#f8fbf9] px-2 py-2"
            >
              <button
                class="btn btn-square border-0 bg-white text-[#163624] shadow-none hover:bg-white"
                :disabled="selectedQty(selectedGroup) === 0"
                @click="decrementSelected(selectedGroup)"
              >
                -
              </button>
              <div class="min-w-12 text-center text-lg font-semibold text-[#163624]">{{ selectedQty(selectedGroup) }}</div>
              <button
                class="btn btn-square border-0 bg-white text-[#163624] shadow-none hover:bg-white"
                :disabled="!selectedProduct(selectedGroup)"
                @click="incrementSelected(selectedGroup)"
              >
                +
              </button>
            </div>
          </div>
        </aside>
      </div>

      <div class="mt-8 px-3 sm:px-6 xl:px-8">
        <div class="rounded-[32px] border border-[#e6efe9] bg-white p-5 shadow-[0_20px_40px_rgba(18,56,36,0.06)]">
          <p class="text-base font-semibold text-[#163624]">Доступные варианты</p>

          <div class="mt-4 space-y-3">
            <article
              v-for="product in selectedGroup.products"
              :key="`${selectedGroup.key}-${product.id}`"
              class="flex flex-col gap-4 rounded-[24px] border border-[#edf4ef] bg-[#fbfcfb] p-4 lg:flex-row lg:items-center lg:justify-between"
            >
              <div class="min-w-0">
                <p class="text-base font-semibold text-[#163624]">{{ product.sku }}</p>

                <div class="mt-3 flex flex-wrap gap-2">
                  <span
                    v-for="badge in productBadges(product)"
                    :key="`${product.id}-${badge}`"
                    class="rounded-full border border-[#dce9e1] bg-white px-3 py-1.5 text-xs font-medium text-[#355947]"
                  >
                    {{ badge }}
                  </span>
                </div>
              </div>

              <div class="flex shrink-0 items-center justify-end">
                <button
                  v-if="getQuantity(product.id) === 0"
                  class="btn h-10 rounded-full border-0 bg-[#139957] px-5 text-sm font-semibold text-white hover:bg-[#0d854a]"
                  @click="incrementProduct(product)"
                >
                  В корзину
                </button>
                <div v-else class="ml-auto flex w-32 items-center justify-between rounded-[20px] border border-[#dce9e1] bg-white px-2 py-2">
                  <button
                    class="btn btn-xs btn-square border-0 bg-[#f3f7f4] text-[#163624] shadow-none hover:bg-[#eaf2ec]"
                    :disabled="getQuantity(product.id) === 0"
                    @click="decrementProduct(product.id)"
                  >
                    -
                  </button>
                  <span class="min-w-8 text-center text-sm font-semibold text-[#163624]">{{ getQuantity(product.id) }}</span>
                  <button
                    class="btn btn-xs btn-square border-0 bg-[#f3f7f4] text-[#163624] shadow-none hover:bg-[#eaf2ec]"
                    @click="incrementProduct(product)"
                  >
                    +
                  </button>
                </div>
              </div>
            </article>
          </div>
        </div>
      </div>
    </div>
    <div v-else class="alert surface-card border-0">Такой тип товара не найден.</div>
  </section>
</template>
