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

type ProductNode = ClientProductsQuery['clientProducts'][number];
type ParamFieldKey = 'widthMm' | 'lengthM' | 'thicknessMicron' | 'sleeveBrand' | 'quantityPerBox';
type ParamValue = number | string;

type ParsedProduct = ProductNode & {
  productTypeLabel: string;
  widthMm: number | null;
  lengthM: number | null;
  thicknessMicron: number | null;
  sleeveBrand: string | null;
  quantityPerBox: string | null;
};

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;
  isExpanded: boolean;
};

const PARAM_KEYS: ParamFieldKey[] = ['widthMm', 'lengthM', 'thicknessMicron', 'sleeveBrand', 'quantityPerBox'];
const parameterFields: Array<{ key: ParamFieldKey; label: string }> = [
  { key: 'widthMm', label: 'Ширина, мм' },
  { key: 'lengthM', label: 'Длина, м' },
  { key: 'thicknessMicron', label: 'Толщина, мкм' },
  { key: 'sleeveBrand', label: 'Бренд втулки' },
  { key: 'quantityPerBox', label: 'Количество в коробе' },
];

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

const { result, loading, error } = useQuery(ClientProductsDocument);
const search = ref('');
const groupStates = reactive<Record<string, GroupState>>({});
const { addProduct, getQuantity, incrementQuantity, decrementQuantity } = useClientCart();

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

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 parseProductMeta(product: ProductNode) {
  const normalizedName = normalizeText(product.name);
  const [typeLabelRaw] = normalizedName.split(',');
  const typeLabel = typeLabelRaw?.trim() || 'Без типа';

  const sizeMatch = normalizedName.match(/(\d+)\s*[xх*]\s*(\d+)\s*м/i);
  const thicknessMatch = normalizedName.match(/(\d+)\s*мкм/i);
  const sleeveMatch = normalizedName.match(/втулка\s+([^,]+)/i);
  const quantityMatch = normalizedName.match(/короб\s+([^,]+)/i);

  return {
    productTypeLabel: typeLabel,
    widthMm: sizeMatch ? Number.parseInt(sizeMatch[1] ?? '', 10) : null,
    lengthM: sizeMatch ? Number.parseInt(sizeMatch[2] ?? '', 10) : null,
    thicknessMicron: thicknessMatch ? Number.parseInt(thicknessMatch[1] ?? '', 10) : null,
    sleeveBrand: sleeveMatch?.[1]?.trim() ?? null,
    quantityPerBox: quantityMatch?.[1]?.trim() ?? null,
  };
}

function productSortValue(value: number | null) {
  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 = (a.sleeveBrand ?? '').localeCompare(b.sleeveBrand ?? '', 'ru');
  if (bySleeve !== 0) {
    return bySleeve;
  }

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

const parsedProducts = computed<ParsedProduct[]>(() => {
  const list = result.value?.clientProducts ?? [];
  const query = search.value.trim().toLowerCase();

  return list
    .map((product) => ({ ...product, ...parseProductMeta(product) }))
    .filter((product) => {
      if (!query) {
        return true;
      }
      return [product.name, product.sku, product.productTypeLabel, product.sleeveBrand ?? '']
        .some((part) => part.toLowerCase().includes(query));
    })
    .sort(compareProducts);
});

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

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

  return [...map.entries()]
    .sort((a, b) => a[0].localeCompare(b[0], 'ru'))
    .map(([typeLabel, products]) => ({
      key: typeLabel.toLowerCase().replaceAll(/\s+/g, '-'),
      typeLabel,
      products: [...products].sort(compareProducts),
    }));
});

function matchesState(product: ParsedProduct, state: GroupState, skipField?: ParamFieldKey) {
  for (const key of PARAM_KEYS) {
    if (key === skipField) {
      continue;
    }

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

    if (product[key] !== selectedValue) {
      return false;
    }
  }

  return true;
}

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) {
    const value = product[field];
    if (value !== null) {
      values.add(value);
    }
  }

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

function createGroupState(group: ProductGroup): GroupState {
  void group;
  const state: GroupState = {
    widthMm: null,
    lengthM: null,
    thicknessMicron: null,
    sleeveBrand: null,
    quantityPerBox: null,
    isExpanded: false,
  };

  return state;
}

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

    for (const key of Object.keys(groupStates)) {
      if (!keys.has(key)) {
        delete groupStates[key];
      }
    }

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

function getGroupState(group: ProductGroup): GroupState {
  const existing = groupStates[group.key];
  if (existing) {
    return existing;
  }

  const created = createGroupState(group);
  groupStates[group.key] = created;
  return created;
}

function updateField(group: ProductGroup, field: ParamFieldKey, value: ParamValue) {
  const state = getGroupState(group);
  state[field] = value as GroupState[typeof field];
}

function selectedProduct(group: ProductGroup) {
  const state = getGroupState(group);
  if (PARAM_KEYS.some((key) => state[key] === null)) {
    return null;
  }
  return group.products.find((product) => matchesState(product, state)) ?? null;
}

function matchingCount(group: ProductGroup) {
  const state = getGroupState(group);
  return group.products.filter((product) => matchesState(product, state)).length;
}

function toggleExpanded(group: ProductGroup) {
  const state = getGroupState(group);
  state.isExpanded = !state.isExpanded;
}

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

function selectedFieldValue(group: ProductGroup, field: ParamFieldKey) {
  const value = getGroupState(group)[field];
  if (value === null) {
    return 'не выбрано';
  }
  return formatOptionLabel(field, value);
}

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);
  if (!product) {
    return 0;
  }
  return getQuantity(product.id);
}

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

function decrementSelected(group: ProductGroup) {
  const product = selectedProduct(group);
  if (!product) {
    return;
  }
  decrementProduct(product.id);
}
</script>

<template>
  <section class="space-y-5">
    <h1 class="text-3xl font-extrabold text-[#0f2f20]">Каталог</h1>

    <div class="surface-card rounded-3xl p-4 md:p-5">
      <label class="form-control">
        <span class="label-text">Поиск</span>
        <input
          v-model="search"
          type="text"
          class="input input-bordered w-full"
          placeholder="Тип товара, SKU или параметр"
        >
      </label>
    </div>

    <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="productGroups.length" class="space-y-4">
      <article
        v-for="group in productGroups"
        :key="group.key"
        class="surface-card rounded-3xl p-4 md:p-5"
      >
        <div class="flex flex-wrap items-center gap-3">
          <h2 class="text-xl font-bold text-[#133826]">{{ group.typeLabel }}</h2>
          <span class="badge badge-outline">{{ group.products.length }} вариантов</span>
        </div>

        <div class="mt-4 grid gap-4 xl:grid-cols-[360px_1fr]">
          <aside class="rounded-2xl bg-base-100 p-3">
            <img
              :src="createProductCover(group.typeLabel, group.key)"
              :alt="`Превью группы ${group.typeLabel}`"
              class="h-56 w-full rounded-2xl object-cover"
              loading="lazy"
            >

            <div class="mt-3 flex flex-wrap gap-2">
              <span class="badge badge-neutral">SKU: {{ selectedProduct(group)?.sku ?? '—' }}</span>
              <span class="badge badge-outline">Совпадений: {{ matchingCount(group) }}</span>
            </div>

            <div class="mt-3 flex items-center justify-between rounded-2xl border border-base-300 bg-base-100 px-2 py-1">
              <button
                class="btn btn-square btn-sm"
                :disabled="selectedQty(group) === 0"
                @click="decrementSelected(group)"
              >
                -
              </button>
              <span class="min-w-10 text-center font-semibold">{{ selectedQty(group) }}</span>
              <button class="btn btn-square btn-sm" @click="incrementSelected(group)">+</button>
            </div>

            <div class="mt-3 flex flex-wrap gap-2">
              <span
                v-for="field in parameterFields"
                :key="`selected-${group.key}-${field.key}`"
                class="badge badge-outline"
              >
                {{ field.label }}: {{ selectedFieldValue(group, field.key) }}
              </span>
            </div>
          </aside>

          <div class="space-y-4">
            <div class="grid gap-3 md:grid-cols-2 2xl:grid-cols-3">
              <div
                v-for="field in parameterFields"
                :key="`${group.key}-${field.key}`"
                class="rounded-2xl bg-base-100 p-3"
              >
                <p class="text-xs font-semibold uppercase tracking-wide text-base-content/60">{{ field.label }}</p>
                <div class="mt-2 flex flex-wrap gap-2">
                  <button
                    v-for="option in getAllFieldOptions(group, field.key)"
                    :key="`${group.key}-${field.key}-${option}`"
                    class="btn btn-sm"
                    :class="getGroupState(group)[field.key] === option ? 'btn-neutral' : 'btn-outline'"
                    @click="updateField(group, field.key, option)"
                  >
                    {{ formatOptionLabel(field.key, option) }}
                  </button>
                </div>
              </div>
            </div>

            <p class="text-sm text-base-content/70">Совпадающих вариантов: {{ matchingCount(group) }}</p>
          </div>
        </div>

        <div v-if="getGroupState(group).isExpanded" class="mt-4 overflow-x-auto rounded-2xl bg-base-100 p-2">
          <table class="table table-zebra">
            <thead>
              <tr>
                <th>Фото</th>
                <th>SKU</th>
                <th>Ширина</th>
                <th>Длина</th>
                <th>Толщина</th>
                <th>Втулка</th>
                <th>Короб</th>
                <th class="text-right">Действие</th>
              </tr>
            </thead>
            <tbody>
              <tr v-for="product in group.products" :key="`${group.key}-${product.id}`">
                <td>
                  <img
                    :src="createProductCover(product.name, product.sku)"
                    :alt="`Превью ${product.sku}`"
                    class="h-12 w-12 rounded-lg object-cover"
                    loading="lazy"
                  >
                </td>
                <td>{{ product.sku }}</td>
                <td>{{ product.widthMm ?? '—' }}</td>
                <td>{{ product.lengthM ?? '—' }}</td>
                <td>{{ product.thicknessMicron ?? '—' }}</td>
                <td>{{ product.sleeveBrand ?? '—' }}</td>
                <td>{{ product.quantityPerBox ?? '—' }}</td>
                <td class="text-right">
                  <div class="ml-auto flex w-28 items-center justify-between rounded-xl border border-base-300 px-1 py-1">
                    <button class="btn btn-xs btn-square" :disabled="getQuantity(product.id) === 0" @click="decrementProduct(product.id)">-</button>
                    <span class="text-sm font-semibold">{{ getQuantity(product.id) }}</span>
                    <button class="btn btn-xs btn-square" @click="incrementProduct(product)">+</button>
                  </div>
                </td>
              </tr>
            </tbody>
          </table>
        </div>

        <button
          class="btn btn-ghost mt-3 w-full justify-center gap-2"
          @click="toggleExpanded(group)"
        >
          <svg
            class="h-4 w-4 transition-transform"
            :class="{ 'rotate-180': getGroupState(group).isExpanded }"
            viewBox="0 0 20 20"
            fill="none"
            xmlns="http://www.w3.org/2000/svg"
          >
            <path d="M5 7.5L10 12.5L15 7.5" stroke="currentColor" stroke-width="1.8" stroke-linecap="round" stroke-linejoin="round" />
          </svg>
          <span>{{ getGroupState(group).isExpanded ? 'Свернуть все варианты' : 'Развернуть все варианты' }}</span>
        </button>
      </article>
    </div>

    <div v-else class="alert surface-card border-0">По текущему запросу товары не найдены.</div>
  </section>
</template>
