<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 { 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 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) => {
      const parsedMeta = parseProductMeta(product);
      return {
        ...product,
        ...parsedMeta,
      };
    })
    .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);
      continue;
    }

    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 getFieldOptions(group: ProductGroup, state: GroupState, field: ParamFieldKey) {
  const values = new Set<ParamValue>();

  for (const product of group.products) {
    if (!matchesState(product, state, field)) {
      continue;
    }

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

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

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

  for (const key of PARAM_KEYS) {
    const options = getFieldOptions(group, state, key);
    state[key] = (options[0] as GroupState[typeof key] | undefined) ?? null;
  }

  return state;
}

function normalizeState(group: ProductGroup, state: GroupState) {
  for (const key of PARAM_KEYS) {
    const options = getFieldOptions(group, state, key);
    if (options.length === 0) {
      state[key] = null;
      continue;
    }

    const current = state[key];
    if (current === null || !options.some((option) => option === current)) {
      state[key] = (options[0] as GroupState[typeof key] | undefined) ?? null;
    }
  }
}

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

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

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

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

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

function selectedProduct(group: ProductGroup) {
  const state = getGroupState(group);
  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 addProductToCart(product: ProductNode) {
  addProduct({
    id: product.id,
    name: product.name,
    sku: product.sku,
    isCustomizable: product.isCustomizable,
  });
}

function incrementProduct(productId: string) {
  incrementQuantity(productId);
}

function decrementProduct(productId: string) {
  decrementQuantity(productId);
}
</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 justify-between gap-3">
          <div class="flex 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>
          <button class="btn btn-ghost btn-sm" @click="toggleExpanded(group)">
            {{ getGroupState(group).isExpanded ? 'Свернуть таблицу' : 'Показать все варианты' }}
          </button>
        </div>

        <div class="mt-4 grid gap-4 xl:grid-cols-[1fr_320px]">
          <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 getFieldOptions(group, getGroupState(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>

          <aside class="rounded-2xl bg-base-100 p-4">
            <p class="text-xs font-semibold uppercase tracking-wide text-base-content/60">Выбранная позиция</p>

            <template v-if="selectedProduct(group)">
              <p class="mt-2 text-sm font-semibold text-[#133826]">{{ selectedProduct(group)?.name }}</p>
              <p class="mt-1 text-xs text-base-content/65">SKU: {{ selectedProduct(group)?.sku }}</p>

              <div class="mt-4">
                <button
                  v-if="selectedProduct(group) && getQuantity(selectedProduct(group)!.id) === 0"
                  class="btn w-full border-0 bg-[#139957] text-white hover:bg-[#0d854a]"
                  @click="selectedProduct(group) && addProductToCart(selectedProduct(group)!)"
                >
                  Добавить в корзину
                </button>

                <div
                  v-else-if="selectedProduct(group)"
                  class="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" @click="decrementProduct(selectedProduct(group)!.id)">-</button>
                  <span class="min-w-8 text-center font-semibold">{{ getQuantity(selectedProduct(group)!.id) }}</span>
                  <button class="btn btn-square btn-sm" @click="incrementProduct(selectedProduct(group)!.id)">+</button>
                </div>
              </div>
            </template>

            <p v-else class="mt-2 text-sm text-base-content/70">
              Нет точного совпадения. Измените параметры или раскройте полный список вариантов.
            </p>
          </aside>
        </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>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>{{ 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">
                  <button
                    v-if="getQuantity(product.id) === 0"
                    class="btn btn-sm border-0 bg-[#139957] text-white hover:bg-[#0d854a]"
                    @click="addProductToCart(product)"
                  >
                    В корзину
                  </button>

                  <div v-else 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" @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.id)">+</button>
                  </div>
                </td>
              </tr>
            </tbody>
          </table>
        </div>
      </article>
    </div>

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