// @design-system: layout/ScrollRow
'use client';

import { useRef, useState, useEffect, useLayoutEffect, useCallback, type ReactNode } from 'react';
import { cn } from '@/lib/cn';
import { Icon } from '@/components/ui/icons/Icon';
import { useT } from '@/lib/i18n/react';

// Isomorphic layout effect — useLayoutEffect on client, useEffect on SSR
const useIsoLayoutEffect = typeof window !== 'undefined' ? useLayoutEffect : useEffect;

type SpacingKey = '0' | '1' | '2' | '3' | '4' | '5' | '6' | '8' | '10' | '12';

export interface ScrollRowProps {
  children: ReactNode;
  /** Gap between children using token spacing scale. */
  gap?: SpacingKey;
  /** Inline padding on the scroll container. */
  px?: SpacingKey;
  /** Block padding. */
  py?: SpacingKey;
  className?: string;
  /** aria-label for the scrollable region. */
  'aria-label'?: string;
  /** Show prev/next arrow buttons on lg+ desktop. Hidden on mobile (touch drag). Default: true. */
  showArrows?: boolean;
  /** Applies -mx-{n} to the outer wrapper so the row bleeds past parent padding. */
  bleed?: SpacingKey;
}

const gapMap: Record<SpacingKey, string> = {
  '0': 'gap-0',
  '1': 'gap-1',
  '2': 'gap-2',
  '3': 'gap-3',
  '4': 'gap-4',
  '5': 'gap-5',
  '6': 'gap-6',
  '8': 'gap-8',
  '10': 'gap-10',
  '12': 'gap-12',
};

const pxMap: Record<SpacingKey, string> = {
  '0': 'px-0',
  '1': 'px-1',
  '2': 'px-2',
  '3': 'px-3',
  '4': 'px-4',
  '5': 'px-5',
  '6': 'px-6',
  '8': 'px-8',
  '10': 'px-10',
  '12': 'px-12',
};

const pyMap: Record<SpacingKey, string> = {
  '0': 'py-0',
  '1': 'py-1',
  '2': 'py-2',
  '3': 'py-3',
  '4': 'py-4',
  '5': 'py-5',
  '6': 'py-6',
  '8': 'py-8',
  '10': 'py-10',
  '12': 'py-12',
};

const bleedMap: Record<SpacingKey, string> = {
  '0': '',
  '1': '-mx-1',
  '2': '-mx-2',
  '3': '-mx-3',
  '4': '-mx-4',
  '5': '-mx-5',
  '6': '-mx-6',
  '8': '-mx-8',
  '10': '-mx-10',
  '12': '-mx-12',
};

// scroll-padding-inline must match px so snap-x respects the container padding
// Without this, RTL snap-start overshoots by px amount (scrollLeft = -px instead of 0)
const scrollPxMap: Record<SpacingKey, string> = {
  '0': 'scroll-px-0',
  '1': 'scroll-px-1',
  '2': 'scroll-px-2',
  '3': 'scroll-px-3',
  '4': 'scroll-px-4',
  '5': 'scroll-px-5',
  '6': 'scroll-px-6',
  '8': 'scroll-px-8',
  '10': 'scroll-px-10',
  '12': 'scroll-px-12',
};

export function ScrollRow({
  children,
  gap = '4',
  px = '4',
  py = '2',
  className,
  'aria-label': ariaLabel,
  showArrows = true,
  bleed,
}: ScrollRowProps) {
  const scrollRef = useRef<HTMLDivElement>(null);
  const [canPrev, setCanPrev] = useState(false);
  const [canNext, setCanNext] = useState(false);
  const [mounted, setMounted] = useState(false);
  const t = useT('common');

  const updateArrows = useCallback(() => {
    const el = scrollRef.current;
    if (!el) return;
    const { scrollLeft, scrollWidth, clientWidth } = el;
    const isRtl = getComputedStyle(el).direction === 'rtl';
    if (isRtl) {
      // RTL: scrollLeft is 0 at start (right), goes negative as scrolled left
      setCanPrev(scrollLeft < -4);
      setCanNext(scrollLeft > -(scrollWidth - clientWidth - 4));
    } else {
      setCanPrev(scrollLeft > 4);
      setCanNext(scrollLeft < scrollWidth - clientWidth - 4);
    }
  }, []);

  useIsoLayoutEffect(() => {
    if (!showArrows) return;
    const el = scrollRef.current;
    if (!el) return;
    updateArrows();
    setMounted(true);
    const ro = new ResizeObserver(updateArrows);
    ro.observe(el);
    el.addEventListener('scroll', updateArrows, { passive: true });
    return () => {
      el.removeEventListener('scroll', updateArrows);
      ro.disconnect();
    };
  }, [updateArrows, showArrows]);

  // Card entrance/exit dim is IntersectionObserver-driven: CSS view(inline)
  // timelines are Chromium-only and misreport progress in RTL scrollers.
  useEffect(() => {
    const el = scrollRef.current;
    if (!el) return;
    if (window.matchMedia('(prefers-reduced-motion: reduce)').matches) return;
    const io = new IntersectionObserver(
      (entries) => {
        for (const entry of entries) {
          const card = entry.target as HTMLElement;
          if (entry.intersectionRatio >= 0.6) {
            card.dataset.inView = 'true';
          } else if (entry.intersectionRatio <= 0.35) {
            card.dataset.inView = 'false';
          }
        }
      },
      { root: el, threshold: [0.35, 0.6] },
    );
    for (const card of el.querySelectorAll(':scope > [data-deal-card-scroll="true"]')) {
      io.observe(card);
    }
    el.dataset.fxReady = 'true';
    return () => {
      io.disconnect();
      delete el.dataset.fxReady;
    };
  }, []);

  const scroll = useCallback((dir: 'prev' | 'next') => {
    const el = scrollRef.current;
    if (!el) return;
    const isRtl = getComputedStyle(el).direction === 'rtl';
    const amount = el.clientWidth * 0.75;
    const delta = dir === 'next' ? (isRtl ? -amount : amount) : isRtl ? amount : -amount;
    const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
    el.scrollBy({ left: delta, behavior: reducedMotion ? 'instant' : 'smooth' });
  }, []);

  const arrowClass = cn(
    'absolute top-[33%] z-10 -translate-y-1/2',
    'hidden lg:flex items-center justify-center',
    'h-9 w-9 rounded-full',
    'bg-surface-base shadow-md',
    'text-text-primary',
    'hover:opacity-90',
    'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-brand-primary-600',
    'transition-opacity duration-150',
  );

  return (
    <div className={cn('relative', bleed && bleedMap[bleed])}>
      <style>{`
        @media (prefers-reduced-motion: no-preference) {
          [data-scroll-row][data-fx-ready] > [data-deal-card-scroll="true"] {
            opacity: 0.18;
            transform: translateY(var(--space-3)) scale(0.98);
            transition:
              opacity var(--duration-slow) ease,
              transform var(--duration-slow) ease;
          }

          [data-scroll-row][data-fx-ready] > [data-deal-card-scroll="true"][data-in-view="true"] {
            opacity: 1;
            transform: none;
          }
        }
      `}</style>
      {mounted && showArrows && (
        <button
          type="button"
          aria-label={t('scroll_prev')}
          onClick={() => scroll('prev')}
          className={cn(
            arrowClass,
            'start-0',
            canPrev ? 'opacity-100' : 'pointer-events-none opacity-0',
          )}
        >
          <Icon name="ChevronLeft" size="sm" mirror />
        </button>
      )}
      <div
        ref={scrollRef}
        data-scroll-row=""
        {...(ariaLabel ? { role: 'region', 'aria-label': ariaLabel } : {})}
        className={cn(
          'flex overflow-x-auto',
          'touch-pan-x overscroll-x-contain',
          'snap-x snap-mandatory',
          '[scrollbar-width:none] [&::-webkit-scrollbar]:hidden',
          gapMap[gap],
          pxMap[px],
          scrollPxMap[px],
          pyMap[py],
          '[&>*]:shrink-0 [&>*]:snap-start',
          className,
        )}
      >
        {children}
      </div>
      {mounted && showArrows && (
        <button
          type="button"
          aria-label={t('scroll_next')}
          onClick={() => scroll('next')}
          className={cn(
            arrowClass,
            'end-0',
            canNext ? 'opacity-100' : 'pointer-events-none opacity-0',
          )}
        >
          <Icon name="ChevronRight" size="sm" mirror />
        </button>
      )}
    </div>
  );
}
