import { useEffect, useRef } from "react";
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { ScrollArea } from "@/components/ui/scroll-area";
import { Skeleton } from "@/components/ui/skeleton";
import type {
  DashboardOrderBookLevel,
  DashboardRealtimeTradeTick,
  DashboardStockOrderBookResponse,
} from "@/features/dashboard/types/dashboard.types";
import { cn } from "@/lib/utils";

const FIXED_DEPTH_COUNT = 10;

interface OrderBookProps {
  symbol?: string;
  referencePrice?: number;
  currentPrice?: number;
  latestTick: DashboardRealtimeTradeTick | null;
  recentTicks: DashboardRealtimeTradeTick[];
  orderBook: DashboardStockOrderBookResponse | null;
  isLoading?: boolean;
}

/**
 * @description 호가창(일반호가/누적호가/호가주문) UI
 * @see features/dashboard/hooks/useOrderBook.ts 초기호가 + 실시간호가 결합
 */
export function OrderBook({
  symbol,
  referencePrice,
  currentPrice,
  latestTick,
  recentTicks,
  orderBook,
  isLoading,
}: OrderBookProps) {
  const prevAskSizeByPriceRef = useRef<Map<number, number>>(new Map());
  const prevBidSizeByPriceRef = useRef<Map<number, number>>(new Map());

  // TODO: 상위 컴포넌트에서 실제 연결 상태를 props로 전달하도록 개선
  const isTradeConnected = !!orderBook;

  // ========== DERIVED STATE ==========
  const rawAsks = orderBook ? [...orderBook.levels].reverse() : [];
  const rawBids = orderBook ? orderBook.levels : [];

  // ========== FIXED DEPTH ROWS ==========
  const asks = toFixedDepthRows(rawAsks, FIXED_DEPTH_COUNT, "start");
  const bids = toFixedDepthRows(rawBids, FIXED_DEPTH_COUNT, "end");

  const bestAsk = findFirstPositive(asks, "askPrice");
  const bestBid = findFirstPositive(bids, "bidPrice");
  const spread =
    bestAsk > 0 && bestBid > 0 ? Math.max(bestAsk - bestBid, 0) : 0;
  const imbalance = calcImbalance(
    orderBook ? orderBook.totalBidSize : 0,
    orderBook ? orderBook.totalAskSize : 0,
  );

  // 최근 체결가와 정확히 일치하는 호가만 강조
  const highlightedPrice = resolveHighlightedPrice(latestTick?.price, asks, bids);

  const percentBasePrice =
    referencePrice && referencePrice > 0
      ? referencePrice
      : currentPrice && currentPrice > 0
        ? currentPrice
        : 0;

  // eslint-disable-next-line react-hooks/refs
  const prevAskSizeByPrice = prevAskSizeByPriceRef.current;
  // eslint-disable-next-line react-hooks/refs
  const prevBidSizeByPrice = prevBidSizeByPriceRef.current;

  // ========== PREV SNAPSHOT ==========
  useEffect(() => {
    if (!orderBook) {
      prevAskSizeByPriceRef.current = new Map();
      prevBidSizeByPriceRef.current = new Map();
      return;
    }

    const snapshotAsks = toFixedDepthRows(
      [...orderBook.levels].reverse(),
      FIXED_DEPTH_COUNT,
      "start",
    );
    const snapshotBids = toFixedDepthRows(
      orderBook.levels,
      FIXED_DEPTH_COUNT,
      "end",
    );

    const nextAskMap = new Map<number, number>();
    const nextBidMap = new Map<number, number>();

    snapshotAsks.forEach((row) => {
      if (row.askPrice > 0) {
        nextAskMap.set(row.askPrice, row.askSize);
      }
    });

    snapshotBids.forEach((row) => {
      if (row.bidPrice > 0) {
        nextBidMap.set(row.bidPrice, row.bidSize);
      }
    });

    prevAskSizeByPriceRef.current = nextAskMap;
    prevBidSizeByPriceRef.current = nextBidMap;
  }, [orderBook]);

  // ========== EMPTY STATE ==========
  if (!symbol) {
    return (
      <div className="flex h-full items-center justify-center text-sm text-muted-foreground">
        종목을 선택해 주세요.
      </div>
    );
  }

  if (isLoading && !orderBook) {
    return <OrderBookSkeleton />;
  }

  if (!orderBook) {
    return (
      <div className="flex h-full items-center justify-center text-sm text-muted-foreground">
        호가 데이터가 없습니다.
      </div>
    );
  }

  return (
    <div className="flex h-full min-h-0 flex-col bg-background">
      {/* ========== TAB HEADER ========== */}
      <Tabs defaultValue="normal" className="h-full min-h-0">
        <div className="border-b px-2 pt-2">
          <TabsList variant="line" className="w-full justify-start">
            <TabsTrigger value="normal" className="px-3">
              일반호가
            </TabsTrigger>
            <TabsTrigger value="cumulative" className="px-3">
              누적호가
            </TabsTrigger>
            <TabsTrigger value="order" className="px-3">
              호가주문
            </TabsTrigger>
          </TabsList>
        </div>

        {/* ========== NORMAL TAB ========== */}
        <TabsContent value="normal" className="min-h-0 flex-1">
          <div className="grid h-full min-h-0 grid-rows-[1fr_190px] overflow-hidden border-t">
            {/* ========== ORDER BOOK BODY ========== */}
            <div className="grid min-h-0 grid-cols-[minmax(0,1fr)_150px] overflow-hidden">
              <div className="min-h-0 border-r">
                <BookHeader />
                <ScrollArea className="h-[calc(100%-32px)]">
                  <BookSideRows
                    rows={asks}
                    side="ask"
                    maxSize={orderBook.totalAskSize}
                    percentBasePrice={percentBasePrice}
                    highlightedPrice={highlightedPrice}
                    previousSizeByPrice={prevAskSizeByPrice}
                  />
                  <BookCenterRow
                    bestAsk={bestAsk}
                    bestBid={bestBid}
                    spread={spread}
                    imbalance={imbalance}
                    latestTick={latestTick}
                  />
                  <BookSideRows
                    rows={bids}
                    side="bid"
                    maxSize={orderBook.totalBidSize}
                    percentBasePrice={percentBasePrice}
                    highlightedPrice={highlightedPrice}
                    previousSizeByPrice={prevBidSizeByPrice}
                  />
                </ScrollArea>
              </div>

              <BookRealtimeSummary
                orderBook={orderBook}
                latestTick={latestTick}
                spread={spread}
                imbalance={imbalance}
                totalAskSize={orderBook.totalAskSize}
                totalBidSize={orderBook.totalBidSize}
                isTradeConnected={isTradeConnected}
              />
            </div>

            {/* ========== TRADE TAPE ========== */}
            <TradeTape ticks={recentTicks} />
          </div>
        </TabsContent>

        {/* ========== CUMULATIVE TAB ========== */}
        <TabsContent value="cumulative" className="min-h-0 flex-1">
          <ScrollArea className="h-full border-t">
            <div className="p-3">
              <div className="mb-2 grid grid-cols-3 text-[11px] font-medium text-muted-foreground">
                <span>매도 누적</span>
                <span className="text-center">호가</span>
                <span className="text-right">매수 누적</span>
              </div>
              <CumulativeRows asks={asks} bids={bids} />
            </div>
          </ScrollArea>
        </TabsContent>

        {/* ========== ORDER TAB ========== */}
        <TabsContent value="order" className="min-h-0 flex-1">
          <div className="flex h-full items-center justify-center border-t px-4 text-sm text-muted-foreground">
            호가주문 탭은 주문 폼과 연동하여 다음 단계에서 확장합니다.
          </div>
        </TabsContent>
      </Tabs>
    </div>
  );
}

/**
 * @description 호가 테이블 머리글
 * @see OrderBook 일반호가 탭 본문
 */
function BookHeader() {
  return (
    <div className="grid h-8 grid-cols-3 border-b bg-muted/20 text-[11px] font-medium text-muted-foreground">
      <div className="flex items-center justify-end px-2">매도잔량</div>
      <div className="flex items-center justify-center border-x">호가</div>
      <div className="flex items-center justify-start px-2">매수잔량</div>
    </div>
  );
}

/**
 * @description 매도/매수 호가 행 목록
 * @see OrderBook 일반호가 탭 본문
 */
function BookSideRows({
  rows,
  side,
  maxSize,
  percentBasePrice,
  highlightedPrice,
  previousSizeByPrice,
}: {
  rows: DashboardOrderBookLevel[];
  side: "ask" | "bid";
  maxSize: number;
  percentBasePrice: number;
  highlightedPrice: number;
  previousSizeByPrice: Map<number, number>;
}) {
  return (
    <div className={cn(side === "ask" ? "bg-red-50/20" : "bg-blue-50/20")}>
      {rows.map((row, index) => {
        const price = side === "ask" ? row.askPrice : row.bidPrice;
        const size = side === "ask" ? row.askSize : row.bidSize;
        const ratio = maxSize > 0 ? Math.min((size / maxSize) * 100, 100) : 0;
        const isHighlightedRow =
          highlightedPrice > 0 && price > 0 && price === highlightedPrice;

        const prevSize = price > 0 ? previousSizeByPrice.get(price) : undefined;
        const delta = prevSize === undefined ? null : size - prevSize;
        const deltaRate =
          delta === null
            ? null
            : prevSize !== undefined && prevSize > 0
              ? (delta / prevSize) * 100
              : delta > 0
                ? 100
                : 0;

        return (
          <div
            key={`${side}-${index}-${price}-${size}`}
            className={cn(
              "grid h-8 grid-cols-3 border-b border-border/40 text-xs",
              isHighlightedRow && "bg-amber-100/40 dark:bg-amber-900/20",
            )}
          >
            <div className="relative flex items-center justify-end px-2">
              {side === "ask" && (
                <>
                  <DepthBar ratio={ratio} side={side} />
                  <div className="relative z-10 flex w-full items-center justify-end gap-1">
                    <span className="tabular-nums">{formatNumber(size)}</span>
                    <SizeDeltaIndicator delta={delta} deltaRate={deltaRate} />
                  </div>
                </>
              )}
            </div>

            <div
              className={cn(
                "flex items-center justify-center border-x font-medium tabular-nums",
                isHighlightedRow &&
                  "relative border-x-amber-400 bg-amber-50/70 dark:bg-amber-900/25",
              )}
            >
              <div className="flex w-full items-center justify-center gap-1">
                <span
                  className={cn(
                    side === "ask" ? "text-red-600" : "text-blue-600",
                    price <= 0 && "text-muted-foreground",
                  )}
                >
                  {price > 0 ? formatNumber(price) : "-"}
                </span>
                <span
                  className={cn(
                    "text-[10px]",
                    price > 0 && percentBasePrice > 0
                      ? calcChangePercent(price, percentBasePrice) >= 0
                        ? "text-red-500"
                        : "text-blue-500"
                      : "text-muted-foreground",
                  )}
                >
                  {price > 0 && percentBasePrice > 0
                    ? formatSignedPercent(calcChangePercent(price, percentBasePrice))
                    : "-"}
                </span>
                {isHighlightedRow && (
                  <span className="text-[10px] font-semibold text-amber-600 dark:text-amber-400">
                    현재가
                  </span>
                )}
              </div>
            </div>

            <div className="relative flex items-center justify-start px-2">
              {side === "bid" && (
                <>
                  <DepthBar ratio={ratio} side={side} />
                  <div className="relative z-10 flex w-full items-center justify-start gap-1">
                    <span className="tabular-nums">{formatNumber(size)}</span>
                    <SizeDeltaIndicator delta={delta} deltaRate={deltaRate} />
                  </div>
                </>
              )}
            </div>
          </div>
        );
      })}
    </div>
  );
}

/**
 * @description 잔량 증감 표시(절대값 + 증감률)
 * @see BookSideRows 수량 변경 시 +/-% 표시
 */
function SizeDeltaIndicator({
  delta,
  deltaRate,
}: {
  delta: number | null;
  deltaRate: number | null;
}) {
  if (delta === null || delta === 0 || deltaRate === null) {
    return <span className="text-[10px] text-muted-foreground">-</span>;
  }

  return (
    <span
      className={cn(
        "text-[10px] tabular-nums",
        delta > 0 ? "text-red-500" : "text-blue-500",
      )}
    >
      {formatSignedNumber(delta)} ({formatSignedPercent(deltaRate)})
    </span>
  );
}

/**
 * @description 호가 중앙 요약(스프레드/체결강도/수급)
 * @see OrderBook 일반호가 탭 본문
 */
function BookCenterRow({
  bestAsk,
  bestBid,
  spread,
  imbalance,
  latestTick,
}: {
  bestAsk: number;
  bestBid: number;
  spread: number;
  imbalance: number;
  latestTick: DashboardRealtimeTradeTick | null;
}) {
  return (
    <div className="border-y bg-background">
      <div className="grid h-8 grid-cols-3 text-[11px]">
        <div className="flex items-center justify-end px-2 text-red-600 tabular-nums">
          {bestAsk > 0 ? `매도1 ${formatNumber(bestAsk)}` : "-"}
        </div>
        <div className="flex items-center justify-center border-x text-muted-foreground">
          스프레드 {formatNumber(spread)}
        </div>
        <div className="flex items-center justify-start px-2 text-blue-600 tabular-nums">
          {bestBid > 0 ? `매수1 ${formatNumber(bestBid)}` : "-"}
        </div>
      </div>
      <div className="grid h-7 grid-cols-3 border-t text-[10px]">
        <div className="flex items-center justify-end px-2 text-muted-foreground">
          체결강도 {latestTick ? `${latestTick.tradeStrength.toFixed(2)}%` : "-"}
        </div>
        <div className="flex items-center justify-center border-x text-muted-foreground">
          수급 불균형 {imbalance >= 0 ? "+" : ""}
          {imbalance.toFixed(2)}%
        </div>
        <div className="flex items-center justify-start px-2 text-muted-foreground">
          체결량 {latestTick ? formatNumber(latestTick.tradeVolume) : "-"}
        </div>
      </div>
    </div>
  );
}

function BookRealtimeSummary({
  orderBook,
  latestTick,
  spread,
  imbalance,
  totalAskSize,
  totalBidSize,
  isTradeConnected,
}: {
  orderBook: {
    accumulatedVolume?: number;
    anticipatedVolume?: number;
    anticipatedPrice?: number;
    anticipatedChangeRate?: number;
  };
  latestTick: DashboardRealtimeTradeTick | null;
  spread: number;
  imbalance: number;
  totalAskSize: number;
  totalBidSize: number;
  isTradeConnected: boolean;
}) {
  return (
    <div className="border-l bg-muted/15 p-2 text-[11px]">
      {/* ========== WS STATUS ========== */}
      <div className="mb-2 flex items-center justify-between rounded border bg-background px-2 py-1">
        <span className="text-muted-foreground">실시간</span>
        <span
          className={cn(
            "font-medium",
            isTradeConnected ? "text-green-600" : "text-muted-foreground",
          )}
        >
          {isTradeConnected ? "연결됨" : "연결 대기"}
        </span>
      </div>

      <SummaryItem
        label="거래량"
        value={formatNumber(latestTick?.tradeVolume ?? orderBook.anticipatedVolume ?? 0)}
      />
      <SummaryItem
        label="누적거래량"
        value={formatNumber(
          latestTick?.accumulatedVolume ?? orderBook.accumulatedVolume ?? 0,
        )}
      />
      <SummaryItem
        label="체결강도"
        value={
          latestTick
            ? `${latestTick.tradeStrength.toFixed(2)}%`
            : orderBook.anticipatedChangeRate !== undefined
              ? `${orderBook.anticipatedChangeRate.toFixed(2)}%`
              : "-"
        }
      />
      <SummaryItem
        label="예상체결가"
        value={formatNumber(orderBook.anticipatedPrice ?? 0)}
      />
      <SummaryItem
        label="매도1호가"
        value={latestTick ? formatNumber(latestTick.askPrice1) : "-"}
        tone="ask"
      />
      <SummaryItem
        label="매수1호가"
        value={latestTick ? formatNumber(latestTick.bidPrice1) : "-"}
        tone="bid"
      />
      <SummaryItem
        label="매수체결"
        value={latestTick ? formatNumber(latestTick.buyExecutionCount) : "-"}
      />
      <SummaryItem
        label="매도체결"
        value={latestTick ? formatNumber(latestTick.sellExecutionCount) : "-"}
      />
      <SummaryItem
        label="순매수체결"
        value={latestTick ? formatNumber(latestTick.netBuyExecutionCount) : "-"}
      />
      <SummaryItem label="총 매도잔량" value={formatNumber(totalAskSize)} tone="ask" />
      <SummaryItem label="총 매수잔량" value={formatNumber(totalBidSize)} tone="bid" />
      <SummaryItem label="스프레드" value={formatNumber(spread)} />
      <SummaryItem
        label="수급 불균형"
        value={`${imbalance >= 0 ? "+" : ""}${imbalance.toFixed(2)}%`}
        tone={imbalance >= 0 ? "bid" : "ask"}
      />
    </div>
  );
}

function SummaryItem({
  label,
  value,
  tone,
}: {
  label: string;
  value: string;
  tone?: "ask" | "bid";
}) {
  return (
    <div className="mb-1.5 flex items-center justify-between rounded border bg-background px-2 py-1">
      <span className="text-muted-foreground">{label}</span>
      <span
        className={cn(
          "font-medium tabular-nums",
          tone === "ask" && "text-red-600",
          tone === "bid" && "text-blue-600",
        )}
      >
        {value}
      </span>
    </div>
  );
}

/**
 * @description 체결 테이프
 * @see features/dashboard/hooks/useKisTradeWebSocket.ts recentTradeTicks
 */
function TradeTape({ ticks }: { ticks: DashboardRealtimeTradeTick[] }) {
  return (
    <div className="border-t bg-background">
      {/* ========== TRADE TAPE HEADER ========== */}
      <div className="grid h-7 grid-cols-4 border-b bg-muted/20 px-2 text-[11px] font-medium text-muted-foreground">
        <div className="flex items-center">체결시각</div>
        <div className="flex items-center justify-end">체결가</div>
        <div className="flex items-center justify-end">체결량</div>
        <div className="flex items-center justify-end">체결강도</div>
      </div>

      {/* ========== TRADE TAPE ROWS ========== */}
      <ScrollArea className="h-[162px]">
        <div>
          {ticks.length === 0 && (
            <div className="flex h-[160px] items-center justify-center text-xs text-muted-foreground">
              체결 데이터 수신 대기 중입니다.
            </div>
          )}
          {ticks.map((tick, index) => (
            <div
              key={`${tick.tickTime}-${tick.price}-${tick.tradeVolume}-${index}`}
              className="grid h-8 grid-cols-4 border-b border-border/40 px-2 text-xs"
            >
              <div className="flex items-center tabular-nums">
                {formatTickTime(tick.tickTime)}
              </div>
              <div className="flex items-center justify-end tabular-nums text-red-600">
                {formatNumber(tick.price)}
              </div>
              <div className="flex items-center justify-end tabular-nums text-blue-600">
                {formatNumber(tick.tradeVolume)}
              </div>
              <div className="flex items-center justify-end tabular-nums">
                {tick.tradeStrength.toFixed(2)}%
              </div>
            </div>
          ))}
        </div>
      </ScrollArea>
    </div>
  );
}

/**
 * @description 누적호가 행 계산
 * @see OrderBook 누적호가 탭
 */
function CumulativeRows({
  asks,
  bids,
}: {
  asks: DashboardOrderBookLevel[];
  bids: DashboardOrderBookLevel[];
}) {
  const maxLen = Math.max(asks.length, bids.length);
  const cumulativeRows: Array<{
    key: string;
    askAcc: number;
    bidAcc: number;
    price: number;
  }> = [];

  let askAcc = 0;
  let bidAcc = 0;

  for (let index = 0; index < maxLen; index += 1) {
    const askRow = asks[index];
    const bidRow = bids[index];

    askAcc += askRow?.askSize ?? 0;
    bidAcc += bidRow?.bidSize ?? 0;

    cumulativeRows.push({
      key: `acc-${index}`,
      askAcc,
      bidAcc,
      price: askRow?.askPrice || bidRow?.bidPrice || 0,
    });
  }

  return (
    <div className="space-y-1">
      {cumulativeRows.map((row) => (
        <div
          key={row.key}
          className="grid h-7 grid-cols-3 items-center rounded border bg-background px-2 text-xs"
        >
          <span className="tabular-nums text-red-600">{formatNumber(row.askAcc)}</span>
          <span className="text-center font-medium tabular-nums">
            {formatNumber(row.price)}
          </span>
          <span className="text-right tabular-nums text-blue-600">
            {formatNumber(row.bidAcc)}
          </span>
        </div>
      ))}
    </div>
  );
}

function DepthBar({ ratio, side }: { ratio: number; side: "ask" | "bid" }) {
  return (
    <div
      className={cn(
        "absolute inset-y-1 z-0 rounded-sm",
        side === "ask" ? "right-1 bg-red-200/50" : "left-1 bg-blue-200/50",
      )}
      style={{ width: `${ratio}%` }}
    />
  );
}

function OrderBookSkeleton() {
  return (
    <div className="flex h-full flex-col p-3">
      <div className="mb-3 grid grid-cols-3 gap-2">
        <Skeleton className="h-8 w-full" />
        <Skeleton className="h-8 w-full" />
        <Skeleton className="h-8 w-full" />
      </div>
      <div className="space-y-2">
        {Array.from({ length: 16 }).map((_, i) => (
          <Skeleton key={i} className="h-7 w-full" />
        ))}
      </div>
    </div>
  );
}

function findFirstPositive(
  rows: DashboardOrderBookLevel[],
  key: "askPrice" | "bidPrice",
) {
  const row = rows.find((item) => item[key] > 0);
  return row?.[key] ?? 0;
}

function calcImbalance(totalBid: number, totalAsk: number) {
  const sum = totalBid + totalAsk;
  if (sum === 0) return 0;
  return ((totalBid - totalAsk) / sum) * 100;
}

/**
 * @description 호가 행 개수를 고정(기본 10)하여 UI 흔들림을 방지합니다.
 * @see OrderBook 상단/하단 호가 행 고정 렌더링
 */
function toFixedDepthRows(
  rows: DashboardOrderBookLevel[],
  targetCount: number,
  padDirection: "start" | "end",
) {
  if (rows.length === targetCount) return rows;

  if (rows.length > targetCount) {
    return rows.slice(0, targetCount);
  }

  const emptyRows = Array.from({ length: targetCount - rows.length }, () => ({
    askPrice: 0,
    bidPrice: 0,
    askSize: 0,
    bidSize: 0,
  }));

  return padDirection === "start"
    ? [...emptyRows, ...rows]
    : [...rows, ...emptyRows];
}

function resolveHighlightedPrice(
  latestTickPrice: number | undefined,
  asks: DashboardOrderBookLevel[],
  bids: DashboardOrderBookLevel[],
) {
  const preferred = latestTickPrice && latestTickPrice > 0 ? latestTickPrice : 0;
  if (preferred <= 0) return 0;

  const candidates = [
    ...asks.map((row) => row.askPrice),
    ...bids.map((row) => row.bidPrice),
  ].filter((price) => price > 0);

  if (candidates.length === 0) return 0;
  return candidates.includes(preferred) ? preferred : 0;
}

function formatTickTime(hhmmss: string) {
  if (!hhmmss || hhmmss.length !== 6) return "--:--:--";
  return `${hhmmss.slice(0, 2)}:${hhmmss.slice(2, 4)}:${hhmmss.slice(4, 6)}`;
}

function formatNumber(value: number) {
  return Number.isFinite(value) ? value.toLocaleString("ko-KR") : "0";
}

function formatSignedNumber(value: number) {
  const sign = value > 0 ? "+" : "";
  return `${sign}${value.toLocaleString("ko-KR")}`;
}

function calcChangePercent(price: number, basePrice: number) {
  if (basePrice <= 0) return 0;
  return ((price - basePrice) / basePrice) * 100;
}

function formatSignedPercent(value: number) {
  const sign = value > 0 ? "+" : "";
  return `${sign}${value.toFixed(2)}%`;
}
