/**
 * 매매 시그널 알림 팝업 — 화면 크기 기반 페이지·좌우 슬라이드
 */
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
import { bindWindowDrag, clientXYFromReact } from '../utils/pointerDrag';
import { useTradeNotification, type TradeNotificationItem } from '../contexts/TradeNotificationContext';
import TradeSignalDetailBody from './TradeSignalDetailBody';
import { getSignalHeadline } from '../utils/tradeSignalDisplay';
import {
  type TradeAlertPopupLayout,
  type TradeAlertPopupPosition,
  normalizeTradeAlertGridCols,
  tradeAlertPopupClassNames,
} from '../utils/tradeAlertPopupLayout';
import { useToastPageCapacity } from '../hooks/useToastPageCapacity';

interface Props {
  onGoToChart: (market: string) => void;
  onOpenList: () => void;
  popupPosition?: TradeAlertPopupPosition;
  popupLayout?: TradeAlertPopupLayout;
  gridCols?: number;
}

const IcSlidePrev = () => (
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
    <polyline points="15 6 9 12 15 18" />
  </svg>
);

const IcSlideNext = () => (
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" aria-hidden>
    <polyline points="9 6 15 12 9 18" />
  </svg>
);

export const TradeSignalSnackbar: React.FC<Props> = ({
  onGoToChart,
  onOpenList,
  popupPosition = 'right',
  popupLayout = 'stack',
  gridCols = 2,
}) => {
  const {
    toastNotifications,
    dismissToast,
    dismissToasts,
    openDetail,
  } = useTradeNotification();

  const cols = normalizeTradeAlertGridCols(gridCols);
  const { pageSize, effectiveCols } = useToastPageCapacity(popupLayout, popupPosition, cols);
  const { stack, cards } = tradeAlertPopupClassNames(popupPosition, popupLayout);
  const layoutKey = `${popupPosition}-${popupLayout}-${cols}`;

  const [pageIndex, setPageIndex] = useState(0);
  const swipeRef = useRef<{ startX: number; startY: number } | null>(null);

  const positionsRef = useRef<Record<string, { x: number; y: number }>>({});
  const dragRef = useRef<{
    key: string;
    startX: number;
    startY: number;
    startLeft: number;
    startTop: number;
  } | null>(null);
  const [, forceDragRender] = React.useReducer((x: number) => x + 1, 0);
  const unbindDragRef = useRef<(() => void) | null>(null);

  const totalPages = Math.max(1, Math.ceil(toastNotifications.length / pageSize));
  const safePage = Math.min(pageIndex, totalPages - 1);

  const pageItems = useMemo(() => {
    const start = safePage * pageSize;
    return toastNotifications.slice(start, start + pageSize);
  }, [toastNotifications, safePage, pageSize]);

  useEffect(() => {
    positionsRef.current = {};
    setPageIndex(0);
  }, [layoutKey]);

  useEffect(() => {
    setPageIndex(p => Math.min(p, Math.max(0, totalPages - 1)));
  }, [totalPages, pageSize]);

  const goPrev = useCallback(() => {
    setPageIndex(p => Math.max(0, p - 1));
  }, []);

  const goNext = useCallback(() => {
    setPageIndex(p => Math.min(totalPages - 1, p + 1));
  }, [totalPages]);

  const dismissAll = useCallback(() => {
    if (toastNotifications.length === 0) return;
    dismissToasts(toastNotifications.map(n => n.id));
    setPageIndex(0);
    positionsRef.current = {};
  }, [dismissToasts, toastNotifications]);

  const dismissCurrentPage = useCallback(() => {
    const ids = pageItems.map(i => i.id);
    if (ids.length === 0) return;
    dismissToasts(ids);
    positionsRef.current = Object.fromEntries(
      Object.entries(positionsRef.current).filter(([id]) => !ids.includes(id)),
    );
    const remaining = toastNotifications.length - ids.length;
    const newTotalPages = Math.max(1, Math.ceil(remaining / pageSize));
    setPageIndex(p => Math.min(p, newTotalPages - 1));
  }, [dismissToasts, pageItems, toastNotifications.length, pageSize]);

  const handleDragStart = useCallback((e: React.PointerEvent, key: string) => {
    if (e.button !== 0) return;
    if ((e.target as HTMLElement).closest('button')) return;
    e.preventDefault();
    const el = (e.currentTarget as HTMLElement).closest('[data-tsn-card]') as HTMLElement;
    if (!el) return;
    try {
      (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
    } catch {
      /* ignore */
    }
    const rect = el.getBoundingClientRect();
    const pos = positionsRef.current[key];
    const { x, y } = clientXYFromReact(e);
    dragRef.current = {
      key,
      startX: x,
      startY: y,
      startLeft: pos?.x ?? rect.left,
      startTop: pos?.y ?? rect.top,
    };
    unbindDragRef.current?.();
    unbindDragRef.current = bindWindowDrag(
      ({ x: cx, y: cy }) => {
        const s = dragRef.current;
        if (!s) return;
        positionsRef.current = {
          ...positionsRef.current,
          [s.key]: {
            x: s.startLeft + (cx - s.startX),
            y: s.startTop + (cy - s.startY),
          },
        };
        forceDragRender();
      },
      () => {
        dragRef.current = null;
        unbindDragRef.current = null;
      },
    );
  }, []);

  const handleSwipeStart = useCallback((e: React.PointerEvent) => {
    if ((e.target as HTMLElement).closest('button')) return;
    swipeRef.current = { startX: e.clientX, startY: e.clientY };
  }, []);

  const handleSwipeEnd = useCallback((e: React.PointerEvent) => {
    const s = swipeRef.current;
    swipeRef.current = null;
    if (!s || totalPages <= 1) return;
    const dx = e.clientX - s.startX;
    const dy = e.clientY - s.startY;
    if (Math.abs(dx) < 48 || Math.abs(dx) < Math.abs(dy)) return;
    if (dx < 0) goNext();
    else goPrev();
  }, [goNext, goPrev, totalPages]);

  if (toastNotifications.length === 0) return null;

  const compactCard = popupLayout === 'grid' || popupLayout === 'strip';
  const gridColsEffective = popupLayout === 'grid' ? effectiveCols : cols;
  const containerStyle = popupLayout === 'grid'
    ? ({ ['--tsn-grid-cols' as string]: gridColsEffective } as React.CSSProperties)
    : undefined;
  const showSlideNav = totalPages > 1;

  const cardList = (
    <>
      {pageItems.map((item, index) => (
        <ToastCard
          key={item.id}
          item={item}
          stackIndex={index}
          compact={compactCard}
          position={positionsRef.current[item.id]}
          onDragStart={handleDragStart}
          onClose={() => {
            delete positionsRef.current[item.id];
            dismissToast(item.id);
          }}
          onDetail={() => openDetail(item)}
          onChart={() => {
            delete positionsRef.current[item.id];
            dismissToast(item.id);
            onGoToChart(item.market);
          }}
          onList={onOpenList}
        />
      ))}
    </>
  );

  const cardsProps = {
    className: `${cards} tsn-cards--paged`,
    style: containerStyle,
    onPointerDown: handleSwipeStart,
    onPointerUp: handleSwipeEnd,
    onPointerCancel: () => { swipeRef.current = null; },
  };

  return (
    <div
      key={layoutKey}
      className={`${stack}${showSlideNav ? ' tsn-stack--has-nav' : ''}`}
      style={containerStyle}
      aria-live="polite"
      aria-label="매매 시그널 알림"
    >
      <div className="tsn-stack-toolbar">
        <div className="tsn-toolbar-actions">
          <button
            type="button"
            className="tsn-dismiss-page"
            onClick={dismissCurrentPage}
            disabled={pageItems.length === 0}
            title={`현재 화면에 보이는 알림 ${pageItems.length}건 닫기`}
          >
            알림창 닫기 ({pageItems.length})
          </button>
          <button
            type="button"
            className="tsn-dismiss-all"
            onClick={dismissAll}
            disabled={toastNotifications.length === 0}
            title={`대기 중인 알림 ${toastNotifications.length}건 전체 닫기 (모든 페이지)`}
          >
            전체 닫기 ({toastNotifications.length})
          </button>
        </div>
        {showSlideNav && (
          <span className="tsn-pager-label">
            {safePage + 1} / {totalPages}
            <span className="tsn-pager-sub"> · 화면당 {pageSize}건</span>
          </span>
        )}
      </div>

      <div className={`tsn-slide-row${showSlideNav ? ' tsn-slide-row--nav' : ''}`}>
        {showSlideNav && (
          <button
            type="button"
            className="tsn-slide-nav tsn-slide-nav--prev"
            onClick={goPrev}
            disabled={safePage <= 0}
            aria-label="이전 알림 목록"
            title="이전 알림"
          >
            <IcSlidePrev />
          </button>
        )}

        {showSlideNav ? (
          <div className="tsn-slide-body">
            <div {...cardsProps}>{cardList}</div>
          </div>
        ) : (
          <div {...cardsProps}>{cardList}</div>
        )}

        {showSlideNav && (
          <button
            type="button"
            className="tsn-slide-nav tsn-slide-nav--next"
            onClick={goNext}
            disabled={safePage >= totalPages - 1}
            aria-label="다음 알림 목록"
            title="다음 알림"
          >
            <IcSlideNext />
          </button>
        )}
      </div>
    </div>
  );
};

const ToastCard: React.FC<{
  item: TradeNotificationItem;
  stackIndex: number;
  compact?: boolean;
  position?: { x: number; y: number };
  onDragStart: (e: React.PointerEvent, key: string) => void;
  onClose: () => void;
  onDetail: () => void;
  onChart: () => void;
  onList: () => void;
}> = ({ item, stackIndex, compact, position, onDragStart, onClose, onDetail, onChart, onList }) => {
  const isBuy = item.signalType === 'BUY';
  const isDragged = Boolean(position);
  const headline = getSignalHeadline(item);

  return (
    <div
      data-tsn-card
      className={`tsn-card ${isBuy ? 'tsn-card--buy' : 'tsn-card--sell'}${stackIndex === 0 ? ' tsn-card--latest' : ''}${compact ? ' tsn-card--compact' : ''}${isDragged ? ' tsn-card--dragging' : ''}`}
      style={
        isDragged
          ? {
              position: 'fixed',
              left: position!.x,
              top: position!.y,
              right: 'auto',
              width: compact ? 320 : 380,
              zIndex: 10050 + stackIndex,
            }
          : undefined
      }
    >
      <div
        className="gc-popup-header tsn-card-header"
        onPointerDown={e => onDragStart(e, item.id)}
        style={{ touchAction: 'none' }}
      >
        <span className={`tsn-badge ${isBuy ? 'tsn-badge--buy' : 'tsn-badge--sell'}`}>
          {item.signalType}
        </span>
        <span className="gc-popup-title tsn-card-title" title={headline}>
          {headline}
        </span>
        <button type="button" className="gc-popup-close tsn-icon-btn" onClick={onClose} title="닫기" aria-label="닫기">
          ✕
        </button>
      </div>

      <TradeSignalDetailBody item={item} variant="compact" />

      <div className={`tsn-card-actions${compact ? ' tsn-card-actions--compact' : ''}`}>
        <button type="button" className="tsn-action-btn tsn-action-btn--primary" onClick={onChart}>
          차트
        </button>
        <button type="button" className="tsn-action-btn" onClick={onDetail}>
          상세
        </button>
        <button type="button" className="tsn-action-btn" onClick={onList}>
          목록
        </button>
      </div>
    </div>
  );
};

export default TradeSignalSnackbar;
