"use client";

import Image from "next/image";
import { usePathname, useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useMemo, useRef, useState, useTransition } from "react";
import { Button } from "@/components/ui/button";
import { EmptyStateCard } from "@/components/ui/empty-state-card";
import { ProductCard } from "@/components/ui/product-card";
import { detonexFigmaAssets } from "@/lib/detonex-figma";
import type {
  StorefrontArchivePage,
  StorefrontHeaderCategoryChild,
  StorefrontProductCard,
} from "@/types/storefront";

function buildArchiveHref(input: {
  query?: string;
  category?: string;
  brands?: string[];
  sort?: "price-asc" | "price-desc";
  minPrice?: number;
  maxPrice?: number;
  onSale?: boolean;
}) {
  const params = new URLSearchParams();

  if (input.query?.trim()) {
    params.set("q", input.query.trim());
  }

  if (input.category?.trim()) {
    params.set("category", input.category.trim());
  }

  if (input.brands?.length) {
    params.set("brand", input.brands.join(","));
  }

  if (input.sort) {
    params.set("sort", input.sort);
  }

  if (typeof input.minPrice === "number" && Number.isFinite(input.minPrice)) {
    params.set("min_price", String(Math.max(0, Math.floor(input.minPrice))));
  }

  if (typeof input.maxPrice === "number" && Number.isFinite(input.maxPrice)) {
    params.set("max_price", String(Math.max(0, Math.ceil(input.maxPrice))));
  }

  if (input.onSale) {
    params.set("on_sale", "1");
  }

  const serialized = params.toString();
  return serialized ? `/shop?${serialized}` : "/shop";
}

function toggleValue(values: string[], value: string) {
  return values.includes(value)
    ? values.filter((item) => item !== value)
    : [...values, value];
}

function formatPriceValue(value: number) {
  return new Intl.NumberFormat("hr-HR", {
    style: "currency",
    currency: "EUR",
    minimumFractionDigits: 0,
    maximumFractionDigits: 0,
  }).format(value);
}

function parsePriceNumber(value: string) {
  const normalized = value.replace(/[^\d,.-]/g, "").replace(/\./g, "").replace(",", ".");
  const amount = Number.parseFloat(normalized);
  return Number.isFinite(amount) ? amount : null;
}

function FilterLinkRow({
  label,
  href,
  active = false,
  uppercase = false,
}: {
  label: string;
  href: string;
  active?: boolean;
  uppercase?: boolean;
}) {
  return (
    <a
      href={href}
      className="flex items-start gap-3 text-left transition-colors hover:text-primary"
    >
      <span
        className={`mt-px inline-flex size-5 shrink-0 items-center justify-center rounded-[4px] border ${
          active ? "border-accent bg-white" : "border-black/10 bg-white"
        }`}
      >
        <span
          className={`size-3 rounded-[2px] ${
            active ? "bg-accent opacity-100" : "bg-transparent opacity-0"
          }`}
        />
      </span>
      <span
        className={`text-[14px] font-medium leading-[1.5] tracking-[-0.02em] text-primary ${
          active ? "" : "opacity-60"
        } ${uppercase ? "uppercase" : ""}`}
      >
        {label}
      </span>
    </a>
  );
}

function getSortLabel(sort: "price-asc" | "price-desc") {
  return sort === "price-desc" ? "Najviša cijena" : "Najniža cijena";
}

export function ArchiveShell({
  archive,
  query,
  selectedCategory,
  selectedBrands,
  selectedSort,
  selectedMinPrice,
  selectedMaxPrice,
  isOnSaleOnly,
  categoryChildren,
}: {
  archive: StorefrontArchivePage;
  query?: string;
  selectedCategory?: string;
  selectedBrands: string[];
  selectedSort: "price-asc" | "price-desc";
  selectedMinPrice?: number;
  selectedMaxPrice?: number;
  isOnSaleOnly?: boolean;
  categoryChildren: StorefrontHeaderCategoryChild[];
}) {
  const router = useRouter();
  const pathname = usePathname();
  const searchParams = useSearchParams();
  const [products, setProducts] = useState<StorefrontProductCard[]>(archive.products);
  const [currentPage, setCurrentPage] = useState(archive.currentPage);
  const [isLoadingMore, setIsLoadingMore] = useState(false);
  const [isSortMenuOpen, setIsSortMenuOpen] = useState(false);
  const [isPendingNavigation, startTransition] = useTransition();
  const sortMenuRef = useRef<HTMLDivElement>(null);
  const sliderTrackRef = useRef<HTMLDivElement>(null);
  const [dragTarget, setDragTarget] = useState<"min" | "max" | null>(null);
  const fallbackPriceRange = useMemo(() => {
    const prices = products
      .map((product) => parsePriceNumber(product.price))
      .filter((price): price is number => price !== null);

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

    return {
      min: Math.floor(Math.min(...prices)),
      max: Math.ceil(Math.max(...prices)),
    };
  }, [products]);
  const priceRange = archive.priceRange ?? fallbackPriceRange;
  const minBound = priceRange ? Math.floor(priceRange.min) : 0;
  const maxBound = priceRange ? Math.ceil(priceRange.max) : 0;
  const initialMinPrice =
    typeof selectedMinPrice === "number" && Number.isFinite(selectedMinPrice)
      ? Math.max(minBound, Math.min(selectedMinPrice, maxBound))
      : minBound;
  const initialMaxPrice =
    typeof selectedMaxPrice === "number" && Number.isFinite(selectedMaxPrice)
      ? Math.min(maxBound, Math.max(selectedMaxPrice, minBound))
      : maxBound;
  const [draftMinPrice, setDraftMinPrice] = useState(initialMinPrice);
  const [draftMaxPrice, setDraftMaxPrice] = useState(initialMaxPrice);

  const hasMore = currentPage < archive.totalPages && !isPendingNavigation;

  useEffect(() => {
    function handlePointerDown(event: MouseEvent) {
      if (!sortMenuRef.current?.contains(event.target as Node)) {
        setIsSortMenuOpen(false);
      }
    }

    function handleKeyDown(event: KeyboardEvent) {
      if (event.key === "Escape") {
        setIsSortMenuOpen(false);
      }
    }

    document.addEventListener("mousedown", handlePointerDown);
    document.addEventListener("keydown", handleKeyDown);

    return () => {
      document.removeEventListener("mousedown", handlePointerDown);
      document.removeEventListener("keydown", handleKeyDown);
    };
  }, []);

  const brandOptions = useMemo(() => {
    const brandMap = new Map<string, { slug: string; name: string; count: number }>();

    for (const product of products) {
      if (!product.brand?.slug || !product.brand.name) {
        continue;
      }

      const existing = brandMap.get(product.brand.slug);

      brandMap.set(product.brand.slug, {
        slug: product.brand.slug,
        name: product.brand.name,
        count: (existing?.count ?? 0) + 1,
      });
    }

    return Array.from(brandMap.values()).sort((left, right) =>
      left.name.localeCompare(right.name, "hr"),
    );
  }, [products]);

  const sliderTrackWidth = 279;
  const hasInteractivePriceRange = maxBound > minBound;
  const minThumbOffset = hasInteractivePriceRange
    ? ((draftMinPrice - minBound) / (maxBound - minBound)) * sliderTrackWidth
    : 0;
  const maxThumbOffset = hasInteractivePriceRange
    ? ((draftMaxPrice - minBound) / (maxBound - minBound)) * sliderTrackWidth
    : sliderTrackWidth;

  const navigateWithPriceRange = useCallback((nextMinPrice: number, nextMaxPrice: number) => {
    const params = new URLSearchParams(searchParams.toString());

    params.delete("page");

    if (nextMinPrice > minBound) {
      params.set("min_price", String(nextMinPrice));
    } else {
      params.delete("min_price");
    }

    if (nextMaxPrice < maxBound) {
      params.set("max_price", String(nextMaxPrice));
    } else {
      params.delete("max_price");
    }

    startTransition(() => {
      router.push(`${pathname}${params.toString() ? `?${params.toString()}` : ""}`);
    });
  }, [maxBound, minBound, pathname, router, searchParams]);

  const commitPriceRange = useCallback(() => {
    if (!hasInteractivePriceRange) {
      return;
    }

    const nextMinPrice = Math.min(draftMinPrice, draftMaxPrice);
    const nextMaxPrice = Math.max(draftMinPrice, draftMaxPrice);

    if (nextMinPrice === initialMinPrice && nextMaxPrice === initialMaxPrice) {
      return;
    }

    navigateWithPriceRange(nextMinPrice, nextMaxPrice);
  }, [
    draftMaxPrice,
    draftMinPrice,
    hasInteractivePriceRange,
    initialMaxPrice,
    initialMinPrice,
    navigateWithPriceRange,
  ]);

  const getSliderValueFromClientX = useCallback((clientX: number) => {
    if (!sliderTrackRef.current || !hasInteractivePriceRange) {
      return minBound;
    }

    const rect = sliderTrackRef.current.getBoundingClientRect();
    const ratio = Math.min(1, Math.max(0, (clientX - rect.left) / rect.width));

    return Math.round(minBound + ratio * (maxBound - minBound));
  }, [hasInteractivePriceRange, maxBound, minBound]);

  function updateDraggedThumb(target: "min" | "max", clientX: number) {
    const nextValue = getSliderValueFromClientX(clientX);

    if (target === "min") {
      setDraftMinPrice(Math.min(nextValue, draftMaxPrice));
      return;
    }

    setDraftMaxPrice(Math.max(nextValue, draftMinPrice));
  }

  function startThumbDrag(target: "min" | "max", clientX: number) {
    if (!hasInteractivePriceRange) {
      return;
    }

    setDragTarget(target);
    updateDraggedThumb(target, clientX);
  }

  function handleTrackPointerDown(clientX: number) {
    if (!hasInteractivePriceRange) {
      return;
    }

    const clickedValue = getSliderValueFromClientX(clientX);
    const minDistance = Math.abs(clickedValue - draftMinPrice);
    const maxDistance = Math.abs(clickedValue - draftMaxPrice);
    const nextTarget = minDistance <= maxDistance ? "min" : "max";

    startThumbDrag(nextTarget, clientX);
  }

  useEffect(() => {
    if (!dragTarget) {
      return;
    }

    const activeDragTarget = dragTarget;

    function syncDraggedThumb(clientX: number) {
      const nextValue = getSliderValueFromClientX(clientX);

      if (activeDragTarget === "min") {
        setDraftMinPrice(Math.min(nextValue, draftMaxPrice));
        return;
      }

      setDraftMaxPrice(Math.max(nextValue, draftMinPrice));
    }

    function handlePointerMove(event: PointerEvent) {
      syncDraggedThumb(event.clientX);
    }

    function handlePointerUp(event: PointerEvent) {
      syncDraggedThumb(event.clientX);
      setDragTarget(null);
      commitPriceRange();
    }

    window.addEventListener("pointermove", handlePointerMove);
    window.addEventListener("pointerup", handlePointerUp);

    return () => {
      window.removeEventListener("pointermove", handlePointerMove);
      window.removeEventListener("pointerup", handlePointerUp);
    };
  }, [
    commitPriceRange,
    dragTarget,
    draftMaxPrice,
    draftMinPrice,
    getSliderValueFromClientX,
    hasInteractivePriceRange,
  ]);

  async function loadMore() {
    if (!hasMore || isLoadingMore) {
      return;
    }

    try {
      setIsLoadingMore(true);
      const params = new URLSearchParams({
        page: String(currentPage + 1),
        per_page: "15",
      });

      if (query?.trim()) {
        params.set("q", query.trim());
      }

      if (selectedCategory?.trim()) {
        params.set("category", selectedCategory.trim());
      }

      if (selectedBrands.length > 0) {
        params.set("brand", selectedBrands.join(","));
      }

      params.set("sort", selectedSort);

      if (typeof selectedMinPrice === "number") {
        params.set("min_price", String(selectedMinPrice));
      }

      if (typeof selectedMaxPrice === "number") {
        params.set("max_price", String(selectedMaxPrice));
      }

      if (isOnSaleOnly) {
        params.set("on_sale", "1");
      }

      const response = await fetch(`/api/archive?${params.toString()}`);
      const payload = (await response.json()) as StorefrontArchivePage;

      if (!response.ok) {
        throw new Error("Archive load more failed");
      }

      setProducts((currentProducts) => {
        const productMap = new Map(currentProducts.map((product) => [product.id, product]));

        for (const product of payload.products) {
          productMap.set(product.id, product);
        }

        return Array.from(productMap.values());
      });
      setCurrentPage(payload.currentPage);
    } finally {
      setIsLoadingMore(false);
    }
  }

  return (
    <div className="grid gap-5 lg:grid-cols-[295px_minmax(0,925px)]">
      <aside className="space-y-[42px]">
        {categoryChildren.length > 0 ? (
          <section className="space-y-4">
            <h2 className="text-[16px] font-bold leading-[1.5] tracking-[-0.02em] text-primary">
              Kategorije
            </h2>
            <div className="space-y-3">
              {categoryChildren.map((child) => (
                <FilterLinkRow
                  key={child.id}
                  label={child.name}
                  href={buildArchiveHref({
                    query,
                    category: selectedCategory === child.slug ? "" : child.slug,
                    brands: selectedBrands,
                    sort: selectedSort,
                    minPrice: selectedMinPrice,
                    maxPrice: selectedMaxPrice,
                    onSale: isOnSaleOnly,
                  })}
                  active={selectedCategory === child.slug}
                />
              ))}
            </div>
          </section>
        ) : null}

        <section className="space-y-4">
            <h2 className="text-[16px] font-bold leading-[1.5] tracking-[-0.02em] text-primary">
              Cijena
            </h2>
          <div className="space-y-4">
            <div className="flex items-center gap-4">
              <div className="flex h-12 min-w-[127px] items-center rounded-[6px] border border-black/10 bg-white px-6 text-[16px] font-medium tracking-[-0.02em] text-primary">
                {draftMinPrice}
              </div>
              <span className="text-[16px] font-medium text-primary/60">-</span>
              <div className="flex h-12 min-w-[127px] items-center rounded-[6px] border border-black/10 bg-white px-5 text-[16px] font-medium tracking-[-0.02em] text-primary">
                {draftMaxPrice}
              </div>
            </div>

            <div
              className="relative h-4 w-[295px]"
              onPointerDown={(event) => handleTrackPointerDown(event.clientX)}
            >
              <div
                ref={sliderTrackRef}
                className="absolute left-2 right-2 top-[5px] h-[6px] rounded-full bg-black/10"
              />
              <div
                className="absolute top-[5px] h-[6px] rounded-full bg-accent"
                style={{
                  left: `${Math.min(minThumbOffset, maxThumbOffset) + 8}px`,
                  width: `${Math.max(0, Math.abs(maxThumbOffset - minThumbOffset))}px`,
                }}
              />
              <div
                className="absolute top-[-5px] z-10 flex size-[26px] cursor-pointer items-center justify-center rounded-full bg-white shadow-[0_2px_8px_rgba(32,32,33,0.12)]"
                style={{ left: `${minThumbOffset}px` }}
                onPointerDown={(event) => {
                  event.stopPropagation();
                  startThumbDrag("min", event.clientX);
                }}
              >
                <div className="size-2 rounded-full bg-accent" />
              </div>
              <div
                className="absolute top-[-5px] z-10 flex size-[26px] cursor-pointer items-center justify-center rounded-full bg-white shadow-[0_2px_8px_rgba(32,32,33,0.12)]"
                style={{ left: `${maxThumbOffset}px` }}
                onPointerDown={(event) => {
                  event.stopPropagation();
                  startThumbDrag("max", event.clientX);
                }}
              >
                <div className="size-2 rounded-full bg-accent" />
              </div>
            </div>

            <div className="flex items-center justify-between text-[16px] font-medium leading-[1.5] tracking-[-0.02em] text-primary">
              <span>{formatPriceValue(draftMinPrice)}</span>
              <span>{formatPriceValue(draftMaxPrice)}</span>
            </div>
          </div>
        </section>

        {brandOptions.length > 0 ? (
          <section className="space-y-4">
            <h2 className="text-[16px] font-bold leading-[1.5] tracking-[-0.02em] text-primary">
              Brend
            </h2>
            <div className="space-y-3">
              {brandOptions.map((brand) => (
                <FilterLinkRow
                  key={brand.slug}
                  label={brand.name}
                  active={selectedBrands.includes(brand.slug)}
                  href={buildArchiveHref({
                    query,
                    category: selectedCategory,
                    brands: toggleValue(selectedBrands, brand.slug),
                    sort: selectedSort,
                    minPrice: selectedMinPrice,
                    maxPrice: selectedMaxPrice,
                    onSale: isOnSaleOnly,
                  })}
                  uppercase
                />
              ))}
            </div>
          </section>
        ) : null}
      </aside>

      <div className="space-y-7">
        <div className="flex h-[45px] items-start justify-between">
          <p className="pt-[11px] text-[14px] font-medium uppercase leading-[1.5] tracking-[-0.02em] text-primary/60">
            {archive.totalResults} Proizvoda
          </p>

          <div ref={sortMenuRef} className="relative">
            <button
              type="button"
              aria-haspopup="menu"
              aria-expanded={isSortMenuOpen}
              aria-label="Odaberi sortiranje proizvoda"
              onClick={() => setIsSortMenuOpen((currentValue) => !currentValue)}
              className="inline-flex h-[45px] w-[163px] cursor-pointer items-center justify-center gap-2 rounded-[6px] bg-[#EEEEEE] px-5 text-[14px] font-medium leading-[1.5] tracking-[-0.02em] text-primary"
            >
              <span className="whitespace-nowrap">{getSortLabel(selectedSort)}</span>
              <Image
                src={detonexFigmaAssets.icons.chevronDown}
                alt=""
                width={18}
                height={18}
                className={`size-[18px] brightness-0 transition-transform ${isSortMenuOpen ? "rotate-180" : ""}`}
              />
            </button>

            {isSortMenuOpen ? (
              <div
                role="menu"
                aria-label="Sortiranje proizvoda"
                className="absolute right-0 top-[53px] z-20 flex w-[163px] flex-col overflow-hidden rounded-[6px] border border-black/10 bg-white shadow-[0_8px_24px_rgba(32,32,33,0.08)]"
              >
                {([
                  { value: "price-asc", label: "Najniža cijena" },
                  { value: "price-desc", label: "Najviša cijena" },
                ] as const).map((option) => (
                  <a
                    key={option.value}
                    role="menuitem"
                    href={buildArchiveHref({
                      query,
                    category: selectedCategory,
                    brands: selectedBrands,
                    sort: option.value,
                    minPrice: selectedMinPrice,
                    maxPrice: selectedMaxPrice,
                    onSale: isOnSaleOnly,
                  })}
                  onClick={() => setIsSortMenuOpen(false)}
                    className={`px-5 py-3 text-[14px] font-medium leading-[1.5] tracking-[-0.02em] transition-colors ${
                      selectedSort === option.value
                        ? "bg-[#EEEEEE] text-primary"
                        : "bg-white text-primary/60 hover:bg-[#F7F7F7] hover:text-primary"
                    }`}
                  >
                    {option.label}
                  </a>
                ))}
              </div>
            ) : null}
          </div>
        </div>

        {products.length > 0 ? (
          <>
            <div className="grid gap-x-5 gap-y-5 xl:grid-cols-3">
              {products.map((product) => (
                <ProductCard key={product.id} product={product} />
              ))}
            </div>

            {hasMore ? (
              <div className="flex justify-center pt-5">
                <Button type="button" onClick={loadMore} disabled={isLoadingMore}>
                  {isLoadingMore ? "Učitavanje..." : "Učitaj još"}
                </Button>
              </div>
            ) : null}
          </>
        ) : (
          <EmptyStateCard
            title="Nema proizvoda za prikaz"
            description="Dodaj proizvode, glavne kategorije i podkategorije u WooCommerce pa će se archive ovdje automatski napuniti."
          />
        )}
      </div>
    </div>
  );
}
