import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { useSearchParams } from 'react-router-dom'
import {
  Facets,
  fetchFacets,
  fetchProducts,
  formatInt,
  ProductsResponse,
} from '../api'
import EmptyState from '../components/EmptyState'
import Filters, {
  countActiveFilters,
  FilterValues,
} from '../components/Filters'
import FilterSheet from '../components/FilterSheet'
import { IconFilter, IconSearch } from '../components/Icons'
import Pagination from '../components/Pagination'
import ProductGrid from '../components/ProductGrid'
import { SkeletonGrid } from '../components/Skeleton'

const PER_PAGE = 24

export default function Catalog() {
  const [searchParams, setSearchParams] = useSearchParams()
  const [facets, setFacets] = useState<Facets | null>(null)
  const [data, setData] = useState<ProductsResponse | null>(null)
  const [loading, setLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [sheetOpen, setSheetOpen] = useState(false)
  const [shrunk, setShrunk] = useState(false)

  const values: FilterValues = useMemo(
    () => ({
      q: searchParams.get('q') ?? '',
      category: searchParams.get('category') ?? '',
      region: searchParams.get('region') ?? '',
      origins: (searchParams.get('origin') ?? '')
        .split(',')
        .filter((o) => o.length > 0),
      priceMin: searchParams.get('price_min') ?? '',
      priceMax: searchParams.get('price_max') ?? '',
      sort: searchParams.get('sort') ?? 'recent',
    }),
    [searchParams]
  )
  const page = Math.max(1, parseInt(searchParams.get('page') ?? '1', 10) || 1)
  const activeCount = countActiveFilters(values)

  // Mobile sticky search — local state, debounced to the URL.
  const [search, setSearch] = useState(values.q)
  const debounceRef = useRef<number | undefined>(undefined)
  useEffect(() => {
    setSearch(values.q)
  }, [values.q])

  // Shrink the sticky search bar once the page is scrolled.
  useEffect(() => {
    const onScroll = () => setShrunk(window.scrollY > 24)
    onScroll()
    window.addEventListener('scroll', onScroll, { passive: true })
    return () => window.removeEventListener('scroll', onScroll)
  }, [])

  useEffect(() => {
    const controller = new AbortController()
    fetchFacets(controller.signal)
      .then(setFacets)
      .catch(() => {})
    return () => controller.abort()
  }, [])

  useEffect(() => {
    const controller = new AbortController()
    setLoading(true)
    setError(null)
    fetchProducts(
      {
        q: values.q || undefined,
        category: values.category || undefined,
        region: values.region || undefined,
        origin: values.origins.length > 0 ? values.origins.join(',') : undefined,
        price_min: values.priceMin || undefined,
        price_max: values.priceMax || undefined,
        sort: values.sort || 'recent',
        page,
        per_page: PER_PAGE,
      },
      controller.signal
    )
      .then((res) => {
        setData(res)
        setLoading(false)
      })
      .catch((err: unknown) => {
        if (err instanceof DOMException && err.name === 'AbortError') return
        setError('Impossible de charger les produits. Réessayez plus tard.')
        setLoading(false)
      })
    return () => controller.abort()
  }, [values, page])

  const applyPatch = useCallback(
    (patch: Partial<FilterValues>, resetPage = true) => {
      setSearchParams(
        (prev) => {
          const next = new URLSearchParams(prev)
          const merged = { ...values, ...patch }
          const setOrDelete = (key: string, v: string) => {
            if (v) next.set(key, v)
            else next.delete(key)
          }
          setOrDelete('q', merged.q)
          setOrDelete('category', merged.category)
          setOrDelete('region', merged.region)
          setOrDelete('origin', merged.origins.join(','))
          setOrDelete('price_min', merged.priceMin)
          setOrDelete('price_max', merged.priceMax)
          setOrDelete('sort', merged.sort !== 'recent' ? merged.sort : '')
          if (resetPage) next.delete('page')
          return next
        },
        { replace: true }
      )
    },
    [setSearchParams, values]
  )

  const resetFilters = useCallback(() => {
    setSearchParams(new URLSearchParams(), { replace: true })
  }, [setSearchParams])

  const goToPage = useCallback(
    (p: number) => {
      setSearchParams((prev) => {
        const next = new URLSearchParams(prev)
        if (p > 1) next.set('page', String(p))
        else next.delete('page')
        return next
      })
      window.scrollTo({ top: 0, behavior: 'smooth' })
    },
    [setSearchParams]
  )

  function onMobileSearch(value: string) {
    setSearch(value)
    window.clearTimeout(debounceRef.current)
    debounceRef.current = window.setTimeout(() => {
      applyPatch({ q: value })
    }, 300)
  }

  return (
    <div className="page catalog">
      {/* Mobile sticky search (shrinks on scroll) */}
      <div
        className={
          shrunk ? 'catalog-searchbar catalog-searchbar-shrunk' : 'catalog-searchbar'
        }
      >
        <div className="catalog-searchbar-field">
          <span className="searchbar-icon" aria-hidden="true">
            <IconSearch size={17} />
          </span>
          <input
            type="search"
            value={search}
            onChange={(e) => onMobileSearch(e.target.value)}
            placeholder="Rechercher un produit…"
            aria-label="Rechercher un produit"
            enterKeyHint="search"
          />
        </div>
      </div>

      <div className="catalog-layout">
        <Filters
          facets={facets}
          values={values}
          onChange={applyPatch}
          onReset={resetFilters}
        />
        <div className="catalog-main">
          <div className="catalog-header">
            <h1>Produits</h1>
            {data && !loading && (
              <p className="catalog-count">
                {formatInt(data.total)} produit{data.total === 1 ? '' : 's'}
              </p>
            )}
          </div>
          {loading ? (
            <SkeletonGrid count={PER_PAGE} />
          ) : error ? (
            <EmptyState variant="error" message={error}>
              <button
                type="button"
                className="btn btn-secondary"
                onClick={() => applyPatch({})}
              >
                Réessayer
              </button>
            </EmptyState>
          ) : (
            <>
              <ProductGrid
                products={data?.items ?? []}
                onReset={resetFilters}
              />
              {data && (
                <Pagination
                  page={data.page}
                  perPage={data.per_page}
                  total={data.total}
                  onPageChange={goToPage}
                />
              )}
            </>
          )}
        </div>
      </div>

      {/* Mobile filter pill (above the tab bar) */}
      <button
        type="button"
        className="filter-fab"
        onClick={() => setSheetOpen(true)}
        aria-haspopup="dialog"
      >
        <IconFilter size={18} />
        Filtres
        {activeCount > 0 && (
          <span className="filter-fab-badge">{activeCount}</span>
        )}
      </button>

      <FilterSheet
        open={sheetOpen}
        facets={facets}
        values={values}
        total={data ? data.total : null}
        onChange={applyPatch}
        onReset={resetFilters}
        onClose={() => setSheetOpen(false)}
      />
    </div>
  )
}
