<script setup lang="ts">
import { useQuery } from '@vue/apollo-composable';
import {
  CatalogProductTypeSettingsDocument,
  ClientProductsDocument,
  type CatalogProductTypeSettingsQuery,
  type ClientProductsQuery,
} from '~/composables/graphql/generated';
import { catalogProductImageSrc } from '~/utils/catalogProductImages';
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 productImageSrc(name: string, sku: string) {
  return catalogProductImageSrc(name) ?? createProductCover(name, sku);
}

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 totalAvailableQty(product: ParsedProduct) {
  return product.availableInWarehouses.reduce((sum, balance) => sum + Number(balance.availableQty || 0), 0);
}

function warehouseAvailability(product: ParsedProduct) {
  return product.availableInWarehouses
    .filter((balance) => Number(balance.availableQty || 0) > 0)
    .map((balance) => `${balance.warehouse.code}: ${balance.availableQty}`)
    .join(' · ');
}

function availabilityTone(product: ParsedProduct) {
  const qty = totalAvailableQty(product);

  if (qty <= 0) {
    return 'bg-[#d95c5c]';
  }
  if (qty < 20) {
    return 'bg-[#e2b534]';
  }

  return 'bg-[#2aa36b]';
}

function availabilityLabel(product: ParsedProduct) {
  const qty = totalAvailableQty(product);

  if (qty <= 0) {
    return 'Нет в наличии';
  }
  if (qty < 20) {
    return 'Остаток ограничен';
  }

  return 'В наличии';
}

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 pb-10">
      <NuxtLink
        v-if="previousGroup"
        :to="productDetailPath(previousGroup)"
        class="absolute left-[-212px] top-28 z-10 hidden w-44 rounded-[28px] border border-[#e6efe9] bg-white p-3 shadow-[0_20px_40px_rgba(18,56,36,0.08)] transition hover:-translate-x-2 hover:shadow-[0_28px_48px_rgba(18,56,36,0.12)] 2xl:block"
      >
        <img
          :src="productImageSrc(previousGroup.typeLabel, previousGroup.key)"
          :alt="`Перейти к товару ${previousGroup.typeLabel}`"
          class="aspect-square w-full rounded-[20px] bg-[#f7fbf8] object-contain"
          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-[-212px] top-28 z-10 hidden w-44 rounded-[28px] border border-[#e6efe9] bg-white p-3 shadow-[0_20px_40px_rgba(18,56,36,0.08)] transition hover:translate-x-2 hover:shadow-[0_28px_48px_rgba(18,56,36,0.12)] 2xl:block"
      >
        <img
          :src="productImageSrc(nextGroup.typeLabel, nextGroup.key)"
          :alt="`Перейти к товару ${nextGroup.typeLabel}`"
          class="aspect-square w-full rounded-[20px] bg-[#f7fbf8] object-contain"
          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">
        <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 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="productImageSrc(previousGroup.typeLabel, previousGroup.key)"
            :alt="`Перейти к товару ${previousGroup.typeLabel}`"
            class="h-16 w-16 rounded-2xl bg-[#f7fbf8] object-contain"
            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="productImageSrc(nextGroup.typeLabel, nextGroup.key)"
            :alt="`Перейти к товару ${nextGroup.typeLabel}`"
            class="h-16 w-16 rounded-2xl bg-[#f7fbf8] object-contain"
            loading="lazy"
          >
          <span class="text-sm font-semibold text-[#163624]">{{ nextGroup.typeLabel }}</span>
        </NuxtLink>
      </div>

      <div class="grid gap-5 xl:grid-cols-[minmax(0,0.92fr)_minmax(0,1.15fr)_320px]">
        <div class="space-y-3">
          <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="productImageSrc(selectedGroup.typeLabel, articleLabel(selectedGroup))"
              :alt="selectedGroup.typeLabel"
              class="aspect-[5/4] w-full rounded-[26px] bg-[#f7fbf8] object-contain"
              loading="lazy"
            >
          </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-4 shadow-[0_18px_36px_rgba(18,56,36,0.05)]"
          >
            <div class="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'
                    : 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>

            <details class="mt-3 rounded-[20px] bg-[#f7fbf8] px-4 py-3 text-sm text-[#587064]">
              <summary class="cursor-pointer font-medium text-[#355947]">Подробнее</summary>
              <p class="mt-2 leading-6">{{ fieldHelperText(selectedGroup, field.key) }}</p>
            </details>
          </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="mt-1 text-lg font-medium leading-tight text-[#163624]">{{ articleLabel(selectedGroup) }}</p>

            <button
              v-if="selectedQty(selectedGroup) === 0"
              class="btn mt-4 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-4 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">
        <p class="mb-4 text-base font-semibold text-[#163624]">Доступные варианты</p>

        <div class="overflow-x-auto rounded-[24px] border border-[#edf4ef] bg-white">
          <table class="table bg-white">
            <thead>
              <tr class="text-[#587064]">
                <th>SKU</th>
                <th>Ширина</th>
                <th>Длина</th>
                <th>Толщина</th>
                <th>Втулка</th>
                <th>Цвет</th>
                <th>Надпись</th>
                <th>Остаток</th>
                <th class="text-right">Действие</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="product in selectedGroup.products" :key="`${selectedGroup.key}-${product.id}`" class="align-middle">
                <td class="font-semibold text-[#163624]">{{ product.sku }}</td>
                <td>{{ product.widthMm ?? '—' }}</td>
                <td>{{ product.lengthM ?? '—' }}</td>
                <td>{{ product.thicknessMicron ?? '—' }}</td>
                <td>{{ product.sleeveBrand ?? '—' }}</td>
                <td>{{ product.colorTags.join(', ') || '—' }}</td>
                <td>{{ product.labelTags.join(', ') || '—' }}</td>
                <td>
                  <div class="flex min-w-[180px] items-center gap-3">
                    <span class="h-3 w-3 rounded-sm" :class="availabilityTone(product)" />
                    <div class="min-w-0">
                      <p class="text-sm font-medium text-[#163624]">{{ availabilityLabel(product) }}</p>
                      <p class="text-xs text-[#607569]">
                        {{ totalAvailableQty(product) }}<span v-if="warehouseAvailability(product)"> · {{ warehouseAvailability(product) }}</span>
                      </p>
                    </div>
                  </div>
                </td>
                <td class="text-right">
                  <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>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </div>
    </div>
    <div v-else class="alert surface-card border-0">Такой тип товара не найден.</div>
  </section>
</template>
