'use client';
import {
  lazy,
  Suspense,
  useEffect,
  useRef,
  useState,
  type PointerEvent as RPointerEvent,
} from 'react';
import Link from 'next/link';
import {
  ArrowRight,
  ArrowUpRight,
  RotateCcw,
  Play,
  Pause,
  Plus,
  Minus,
  ShoppingBag,
  Check,
  Search,
  Send,
  MoveHorizontal,
} from 'lucide-react';
import { StylePreview } from './style-preview';
import {
  products,
  filterProducts,
  validCart,
  cartTotal,
  money,
  type CartLine,
} from '@/lib/commerce';
const Scene = lazy(() => import('./scene'));
const Thumbnail = lazy(() => import('./model-thumbnail'));

export function useCart() {
  const [cart, setCart] = useState<CartLine[]>([]),
    [ready, setReady] = useState(false);
  const source = useRef(Symbol('cart'));
  useEffect(() => {
    const restore = () => {
      try {
        setCart(
          validCart(JSON.parse(localStorage.getItem('webcraft-cart') || '[]')),
        );
      } catch {}
    };
    restore();
    setReady(true);
    const sync = (e: Event) => {
      const value = (e as CustomEvent<{ source: symbol; cart: CartLine[] }>)
        .detail;
      if (value.source !== source.current) setCart(value.cart);
    };
    window.addEventListener('webcraft-cart-change', sync);
    window.addEventListener('storage', restore);
    return () => {
      window.removeEventListener('webcraft-cart-change', sync);
      window.removeEventListener('storage', restore);
    };
  }, []);
  useEffect(() => {
    if (!ready) return;
    const json = JSON.stringify(cart);
    if (cartSnapshot === json) return;
    cartSnapshot = json;
    try {
      localStorage.setItem('webcraft-cart', json);
    } catch {}
    window.dispatchEvent(
      new CustomEvent('webcraft-cart-change', {
        detail: { source: source.current, cart },
      }),
    );
  }, [cart, ready]);
  const add = (id: string) =>
    setCart((c) => validCart([...c, { id, quantity: 1 }]));
  const quantity = (id: string, q: number) =>
    setCart((c) =>
      validCart(c.map((l) => (l.id === id ? { ...l, quantity: q } : l))),
    );
  return {
    cart,
    add,
    quantity,
    total: cartTotal(cart),
    count: cart.reduce((s, l) => s + l.quantity, 0),
  };
}
let cartSnapshot = '';

function Spring() {
  const ref = useRef<HTMLButtonElement>(null),
    state = useRef({
      x: 0,
      y: 0,
      vx: 0,
      vy: 0,
      drag: false,
      ox: 0,
      oy: 0,
      frame: 0,
    }),
    [active, setActive] = useState(false);
  useEffect(() => () => cancelAnimationFrame(state.current.frame), []);
  const write = () => {
    if (ref.current)
      ref.current.style.transform = `translate(${state.current.x}px, ${state.current.y}px)`;
  };
  const settle = () => {
    const s = state.current;
    s.drag = false;
    setActive(false);
    if (matchMedia('(prefers-reduced-motion:reduce)').matches) {
      s.x = s.y = 0;
      write();
      return;
    }
    let prev = performance.now();
    const tick = (t: number) => {
      const dt = Math.min((t - prev) / 1000, 0.032);
      prev = t;
      s.vx += (-140 * s.x - 15 * s.vx) * dt;
      s.vy += (-140 * s.y - 15 * s.vy) * dt;
      s.x += s.vx * dt;
      s.y += s.vy * dt;
      write();
      if (Math.abs(s.x) + Math.abs(s.y) + Math.abs(s.vx) + Math.abs(s.vy) > 0.2)
        s.frame = requestAnimationFrame(tick);
      else {
        s.x = s.y = 0;
        write();
      }
    };
    s.frame = requestAnimationFrame(tick);
  };
  return (
    <div className="spring-demo">
      <div className="spring-target" />
      <button
        ref={ref}
        aria-label="Перетащить пружинный элемент. Стрелки двигают, Enter возвращает."
        className={`spring-object ${active ? 'dragging' : ''}`}
        onPointerDown={(e) => {
          e.currentTarget.setPointerCapture(e.pointerId);
          const s = state.current;
          cancelAnimationFrame(s.frame);
          s.drag = true;
          s.ox = e.clientX - s.x;
          s.oy = e.clientY - s.y;
          s.vx = s.vy = 0;
          setActive(true);
        }}
        onPointerMove={(e) => {
          const s = state.current;
          if (s.drag) {
            s.x = Math.max(-115, Math.min(115, e.clientX - s.ox));
            s.y = Math.max(-70, Math.min(70, e.clientY - s.oy));
            write();
          }
        }}
        onPointerUp={settle}
        onPointerCancel={settle}
        onKeyDown={(e) => {
          const s = state.current;
          if (e.key.startsWith('Arrow')) {
            e.preventDefault();
            cancelAnimationFrame(s.frame);
            s.x +=
              e.key === 'ArrowLeft' ? -15 : e.key === 'ArrowRight' ? 15 : 0;
            s.y += e.key === 'ArrowUp' ? -15 : e.key === 'ArrowDown' ? 15 : 0;
            s.x = Math.max(-115, Math.min(115, s.x));
            s.y = Math.max(-70, Math.min(70, s.y));
            write();
          }
          if (e.key === 'Enter' || e.key === ' ') settle();
        }}
      >
        <MoveHorizontal size={32} />
      </button>
      <span className="demo-hint">Потяни. Отпусти. Ещё раз.</span>
      <span className="demo-tech">k 140 / damping 15</span>
    </div>
  );
}
function Magnetic() {
  const inner = useRef<HTMLSpanElement>(null);
  const [done, setDone] = useState(false);
  return (
    <div className="magnetic-demo">
      <span className="demo-tech">POINTER-DRIVEN / 120ms</span>
      <button
        className="magnet-target"
        onPointerMove={(e) => {
          if (
            !inner.current ||
            !matchMedia('(hover:hover) and (pointer:fine)').matches ||
            matchMedia('(prefers-reduced-motion:reduce)').matches
          )
            return;
          const b = e.currentTarget.getBoundingClientRect();
          inner.current.style.transform = `translate(${((e.clientX - b.left) / b.width - 0.5) * 32}px,${((e.clientY - b.top) / b.height - 0.5) * 26}px)`;
        }}
        onPointerLeave={() => {
          if (inner.current) inner.current.style.transform = 'translate(0,0)';
        }}
        onClick={() => {
          setDone(true);
          setTimeout(() => setDone(false), 1800);
        }}
      >
        <span ref={inner}>
          {done ? 'Nice move.' : 'Get in touch'}
          {done ? <Check size={22} /> : <ArrowUpRight size={22} />}
        </span>
      </button>
      <span className="demo-hint">Подведи курсор ближе</span>
    </div>
  );
}
function Parallax({ stack = false }: { stack?: boolean }) {
  const ref = useRef<HTMLDivElement>(null),
    back = useRef<HTMLDivElement>(null),
    front = useRef<HTMLDivElement>(null);
  const scroll = () => {
    if (!ref.current || matchMedia('(prefers-reduced-motion:reduce)').matches)
      return;
    const y = ref.current.scrollTop;
    if (back.current)
      back.current.style.transform = `translateY(${y * 0.35}px)`;
    if (front.current)
      front.current.style.transform = `translateY(${-y * 0.14}px)`;
  };
  return (
    <div
      className={`parallax-demo ${stack ? 'stack-demo' : ''}`}
      ref={ref}
      onScroll={scroll}
      tabIndex={0}
      aria-label="Прокручиваемая демонстрация. Используйте скролл или стрелки."
    >
      <div className="parallax-story">
        <div className="parallax-sticky">
          <div ref={back} className="parallax-back">
            {stack ? 'FORM' : 'DEPTH'}
          </div>
          <div ref={front} className="parallax-front">
            <span />
            {stack ? (
              <p>
                Ideas take shape.
                <br />
                One layer at a time.
              </p>
            ) : (
              <p>
                A different
                <br />
                perspective.
              </p>
            )}
          </div>
          <span className="parallax-note">ПРОКРУТИТЕ ВНУТРИ СЦЕНЫ ↓</span>
        </div>
        {stack && (
          <div className="story-chapters">
            <p>Идея задаёт направление.</p>
            <p>Материал определяет характер.</p>
            <p>Детали собирают целое.</p>
          </div>
        )}
      </div>
    </div>
  );
}
function Reveal() {
  const [key, setKey] = useState(0);
  return (
    <div className="reveal-demo">
      <div
        key={key}
        className="reveal-lines"
        aria-label="Make every word matter."
      >
        {['Make every', 'word', 'matter.'].map((l, i) => (
          <div aria-hidden="true" key={l}>
            <span style={{ animationDelay: `${i * 100}ms` }}>{l}</span>
          </div>
        ))}
      </div>
      <button className="demo-replay" onClick={() => setKey((k) => k + 1)}>
        <RotateCcw size={15} /> Повторить
      </button>
    </div>
  );
}
function Marquee() {
  const [paused, setPaused] = useState(false);
  return (
    <div className="marquee-demo">
      <div className={`marquee-track ${paused ? 'paused' : ''}`}>
        <div>
          MAKE IT MOVE <span>✳</span> MAKE IT MATTER <span>✳</span>{' '}
        </div>
        <div aria-hidden="true">
          MAKE IT MOVE <span>✳</span> MAKE IT MATTER <span>✳</span>{' '}
        </div>
      </div>
      <button className="demo-replay" onClick={() => setPaused(!paused)}>
        {paused ? <Play size={15} /> : <Pause size={15} />}{' '}
        {paused ? 'Продолжить' : 'Пауза'}
      </button>
      <span className="demo-tech">CSS / LINEAR / INFINITE</span>
    </div>
  );
}
function Transition() {
  const [view, setView] = useState(0);
  const change = () => {
    const update = () => setView((v) => 1 - v);
    if (
      document.startViewTransition &&
      !matchMedia('(prefers-reduced-motion:reduce)').matches
    )
      document.startViewTransition(update);
    else update();
  };
  return (
    <div className={`transition-demo view-${view}`}>
      <span className="demo-tech">VIEW TRANSITION API</span>
      <div className="transition-content">
        <span className="transition-orb" />
        <h3>{view ? 'A new\nperspective.' : 'Change\nthe scene.'}</h3>
      </div>
      <button onClick={change} className="demo-replay">
        Сменить сцену <ArrowRight size={16} />
      </button>
    </div>
  );
}
function Accordion() {
  return (
    <div className="accordion-demo">
      <h3>Good questions.</h3>
      {[
        [
          'Что входит в компонент?',
          'Семантическая разметка, видимый фокус и нативное раскрытие. Для этой задачи достаточно HTML details и summary.',
        ],
        [
          'Нужен ли JavaScript?',
          'Нет. Нативный аккордеон работает с клавиатурой и без JavaScript. Атрибут name связывает элементы в группу.',
        ],
        [
          'Как адаптировать стиль?',
          'Измените цвета и отступы. Сохраните видимый индикатор, контраст и область нажатия.',
        ],
      ].map(([q, a]) => (
        <details key={q} name="demo-accordion">
          <summary>
            {q}
            <Plus size={17} />
          </summary>
          <p>{a}</p>
        </details>
      ))}
    </div>
  );
}
function Comparison() {
  const [split, setSplit] = useState(50);
  return (
    <div className="comparison-demo">
      <div className="compare-side before">
        <span>BEFORE</span>
        <h3>
          Ideas need
          <br />
          good form.
        </h3>
      </div>
      <div
        className="compare-side after"
        style={{ clipPath: `inset(0 ${100 - split}% 0 0)` }}
      >
        <span>AFTER</span>
        <h3>
          Ideas need
          <br />
          <em>good form.</em>
        </h3>
      </div>
      <div className="compare-handle" style={{ left: `${split}%` }}>
        <MoveHorizontal size={20} />
      </div>
      <input
        aria-label="Граница сравнения до и после"
        type="range"
        min="5"
        max="95"
        value={split}
        onChange={(e) => setSplit(+e.target.value)}
      />
    </div>
  );
}
function FormDemo() {
  const [email, setEmail] = useState(''),
    [status, setStatus] = useState('idle'),
    [error, setError] = useState('');
  const input = useRef<HTMLInputElement>(null),
    timer = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);
  useEffect(() => () => clearTimeout(timer.current), []);
  return (
    <form
      className="form-demo"
      noValidate
      onSubmit={(e) => {
        e.preventDefault();
        if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
          setError('Укажите email в формате name@example.com.');
          input.current?.focus();
          return;
        }
        setError('');
        setStatus('loading');
        timer.current = setTimeout(() => setStatus('success'), 900);
      }}
    >
      <Send size={28} />
      <h3>Stay in the loop.</h3>
      <p>Демо формы. Адрес никуда не отправляется.</p>
      <label htmlFor="demo-email">Email</label>
      <input
        id="demo-email"
        ref={input}
        type="email"
        value={email}
        onChange={(e) => {
          setEmail(e.target.value);
          setStatus('idle');
          setError('');
        }}
        placeholder="you@example.com"
        aria-invalid={!!error}
        aria-describedby="email-error"
      />
      <span id="email-error" className="field-error" aria-live="polite">
        {error}
      </span>
      <button type="submit" disabled={status === 'loading'}>
        {status === 'loading'
          ? 'Проверяем…'
          : status === 'success'
            ? 'Форма заполнена верно'
            : 'Проверить форму'}
        {status === 'success' ? <Check size={18} /> : <ArrowRight size={18} />}
      </button>
      <span className="sr-only" role="status">
        {status === 'success'
          ? 'Демонстрация завершена. Данные не отправлены.'
          : ''}
      </span>
    </form>
  );
}
export function ApiDemo() {
  const [body, setBody] = useState('{"items":[{"id":"cup","quantity":2}]}'),
    [output, setOutput] = useState(
      'Нажмите «Отправить», чтобы получить ответ сервера.',
    ),
    [status, setStatus] = useState(''),
    [pending, setPending] = useState(false);
  const controller = useRef<AbortController | null>(null);
  useEffect(() => () => controller.current?.abort(), []);
  return (
    <div className="api-demo">
      <div className="api-title">
        <span>POST</span>
        <code>/api/quote</code>
        <span>{status}</span>
      </div>
      <label htmlFor="api-body">JSON запроса</label>
      <textarea
        id="api-body"
        value={body}
        onChange={(e) => setBody(e.target.value)}
        spellCheck={false}
      />
      <button
        className="primary"
        disabled={pending}
        onClick={async () => {
          setPending(true);
          controller.current?.abort();
          controller.current = new AbortController();
          const timer = setTimeout(() => controller.current?.abort(), 10000);
          try {
            const r = await fetch('/api/quote', {
              method: 'POST',
              headers: { 'Content-Type': 'application/json' },
              body,
              signal: controller.current.signal,
            });
            setStatus(`${r.status} ${r.ok ? 'OK' : 'ERROR'}`);
            setOutput(JSON.stringify(await r.json(), null, 2));
          } catch {
            setOutput(
              'Сервер недоступен или истекло время ожидания. Повторите запрос.',
            );
            setStatus('NETWORK ERROR');
          } finally {
            clearTimeout(timer);
            setPending(false);
          }
        }}
      >
        {pending ? 'Запрос…' : 'Отправить'}
        <ArrowUpRight size={16} />
      </button>
      <pre aria-live="polite">{output}</pre>
      <p>
        Сервер считает сумму по своему каталогу. Заказ и платёж не создаются.
      </p>
    </div>
  );
}
export function CatalogDemo({
  full = false,
  onlyCart = false,
}: {
  full?: boolean;
  onlyCart?: boolean;
}) {
  const { cart, add, quantity, total, count } = useCart();
  const [q, setQ] = useState(''),
    [category, setCategory] = useState('all'),
    [sort, setSort] = useState('featured'),
    [opened, setOpened] = useState(onlyCart),
    [message, setMessage] = useState(''),
    [pending, setPending] = useState(false);
  useEffect(() => {
    if (full) {
      const p = new URLSearchParams(window.location.search);
      setQ(p.get('q') || '');
      setCategory(p.get('category') || 'all');
      setSort(p.get('sort') || 'featured');
    }
  }, [full]);
  const filter = (key: string, value: string) => {
    if (key === 'q') setQ(value);
    if (key === 'category') setCategory(value);
    if (key === 'sort') setSort(value);
    if (full) {
      const p = new URLSearchParams(window.location.search);
      if (value && value !== 'all' && value !== 'featured') p.set(key, value);
      else p.delete(key);
      window.history.replaceState(
        null,
        '',
        `${location.pathname}${p.size ? '?' + p : ''}`,
      );
    }
  };
  const items = filterProducts({ q, category, sort });
  return (
    <div className={`catalog-demo ${full ? 'full-catalog' : ''}`}>
      <div className="catalog-demo-head">
        <b>
          OBJECT<span> / STORE</span>
        </b>
        <button onClick={() => setOpened(!opened)} aria-expanded={opened}>
          <ShoppingBag size={16} />
          {count}
        </button>
      </div>
      {opened ? (
        <div className="cart-panel">
          <h3>Ваша корзина</h3>
          <p>Учебные товары. Сохранено на этом устройстве.</p>
          {!cart.length ? (
            <div className="empty">
              <ShoppingBag />
              <p>Пока ничего нет.</p>
              <button onClick={() => setOpened(false)}>
                Вернуться к каталогу
              </button>
            </div>
          ) : (
            <>
              {cart.map((l) => {
                const p = products.find((p) => p.id === l.id)!;
                return (
                  <div className="cart-line" key={l.id}>
                    <span>
                      <b>{p.name}</b>
                      <small>{money(p.price)}</small>
                    </span>
                    <div>
                      <button
                        aria-label={`Уменьшить ${p.name}`}
                        onClick={() => {
                          quantity(l.id, l.quantity - 1);
                          setMessage('');
                        }}
                      >
                        <Minus size={14} />
                      </button>
                      <span>{l.quantity}</span>
                      <button
                        aria-label={`Увеличить ${p.name}`}
                        disabled={l.quantity >= p.stock}
                        onClick={() => {
                          quantity(l.id, l.quantity + 1);
                          setMessage('');
                        }}
                      >
                        <Plus size={14} />
                      </button>
                    </div>
                    <b>{money(p.price * l.quantity)}</b>
                  </div>
                );
              })}
              <div className="cart-total">
                <span>Предварительный итог</span>
                <b>{money(total)}</b>
              </div>
              <button
                className="store-action"
                disabled={pending}
                onClick={async () => {
                  setPending(true);
                  try {
                    const r = await fetch('/api/quote', {
                      method: 'POST',
                      headers: { 'Content-Type': 'application/json' },
                      body: JSON.stringify({ items: cart }),
                      signal: AbortSignal.timeout(10000),
                    });
                    const data = (await r.json()) as {
                      total: number;
                      error: { message: string };
                    };
                    setMessage(
                      r.ok
                        ? `Сервер подтвердил: ${money(data.total)}. Это демо — заказ и платёж не создаются.`
                        : data.error.message,
                    );
                  } catch {
                    setMessage(
                      'Не удалось связаться с сервером. Повторите расчёт.',
                    );
                  } finally {
                    setPending(false);
                  }
                }}
              >
                {pending ? 'Проверяем…' : 'Проверить на сервере'}
                <ArrowRight size={16} />
              </button>
            </>
          )}
          <p role="status">{message}</p>
        </div>
      ) : (
        <>
          <div className="store-intro">
            <h3>
              Objects for
              <br />
              every day.
            </h3>
            <span>
              Продуманная форма.
              <br />
              Простые ритуалы.
            </span>
          </div>
          <div className="catalog-filters">
            <label>
              <Search size={14} />
              <input
                placeholder="Найти предмет…"
                aria-label="Поиск товаров"
                value={q}
                onChange={(e) => filter('q', e.target.value)}
              />
            </label>
            <select
              aria-label="Категория товара"
              value={category}
              onChange={(e) => filter('category', e.target.value)}
            >
              <option value="all">Все категории</option>
              {[...new Set(products.map((p) => p.category))].map((c) => (
                <option key={c}>{c}</option>
              ))}
            </select>
            {full && (
              <select
                aria-label="Сортировка товаров"
                value={sort}
                onChange={(e) => filter('sort', e.target.value)}
              >
                <option value="featured">По умолчанию</option>
                <option value="price-asc">Сначала дешевле</option>
                <option value="price-desc">Сначала дороже</option>
              </select>
            )}
          </div>
          <div className="product-grid">
            {items.slice(0, full ? 6 : 3).map((p) => (
              <div className="product-card" key={p.id}>
                <div className="product-art" style={{ background: p.color }}>
                  <span>{p.name.split(' ').slice(1).join(' ')}</span>
                  <Suspense fallback={<span />}>
                    <Thumbnail object={p.shape} color={p.color} />
                  </Suspense>
                  <small>{p.category}</small>
                </div>
                <div className="product-info">
                  <Link href={`/templates/store/product?id=${p.id}`}>
                    {p.name}
                  </Link>
                  <button
                    aria-label={`Добавить ${p.name}`}
                    onClick={() => {
                      add(p.id);
                      setMessage(`${p.name} в корзине`);
                    }}
                  >
                    <Plus size={16} />
                  </button>
                  <span>{money(p.price)}</span>
                </div>
              </div>
            ))}
          </div>
          {!items.length && (
            <div className="empty">
              <p>Нет подходящих предметов.</p>
              <button
                onClick={() => {
                  filter('q', '');
                  filter('category', 'all');
                }}
              >
                Сбросить фильтры
              </button>
            </div>
          )}
          <span className="sr-only" role="status">
            {message}
          </span>
        </>
      )}
    </div>
  );
}
export function Demo({
  id,
  compact = false,
}: {
  id: string;
  compact?: boolean;
}) {
  switch (id) {
    case '3d':
      return (
        <Suspense
          fallback={
            <div className="scene-loading">Подготавливаем 3D-сцену…</div>
          }
        >
          <Scene compact={compact} />
        </Suspense>
      );
    case 'spring':
      return <Spring />;
    case 'magnetic':
      return <Magnetic />;
    case 'parallax':
      return <Parallax />;
    case 'stack':
      return <Parallax stack />;
    case 'typography':
      return (
        <StylePreview editable={!compact} id="editorial" small={compact} />
      );
    case 'catalog':
      return <CatalogDemo full={!compact} />;
    case 'reveal':
      return <Reveal />;
    case 'marquee':
      return <Marquee />;
    case 'transition':
      return <Transition />;
    case 'accordion':
      return <Accordion />;
    case 'comparison':
      return <Comparison />;
    case 'form':
      return <FormDemo />;
    case 'api':
      return <ApiDemo />;
    default:
      return <div>Пример не найден.</div>;
  }
}
