import { ArrowLeft, ArrowRight, Loader2 } from 'lucide-react';
import { useMemo, useRef, useState } from 'react';

import type {
  DragPuzzlePieceRequest,
  PuzzleBoardSnapshot,
  PuzzleCellPosition,
  PuzzleRunSnapshot,
  SwapPuzzlePiecesRequest,
} from '../../../packages/shared/src/contracts/puzzleRuntimeSession';
import { useResolvedAssetReadUrl } from '../../hooks/useResolvedAssetReadUrl';
import { ResolvedAssetImage } from '../ResolvedAssetImage';

type PuzzleRuntimeShellProps = {
  run: PuzzleRunSnapshot | null;
  isBusy?: boolean;
  error?: string | null;
  onBack: () => void;
  onSwapPieces: (payload: SwapPuzzlePiecesRequest) => void;
  onDragPiece: (payload: DragPuzzlePieceRequest) => void;
  onAdvanceNextLevel: () => void;
};

type PuzzleBoardPieceViewModel = {
  pieceId: string;
  row: number;
  col: number;
  correctRow: number;
  correctCol: number;
  label: string;
};

function boardCellKey(position: PuzzleCellPosition) {
  return `${position.row}:${position.col}`;
}

function buildBoardCells(board: PuzzleBoardSnapshot) {
  return Array.from({ length: board.rows * board.cols }, (_, index) => ({
    row: Math.floor(index / board.cols),
    col: index % board.cols,
  }));
}

function buildPieceLabel(pieceId: string) {
  const fallback = pieceId.slice(-2).toUpperCase();
  return fallback || '块';
}

/**
 * 拼图运行时壳层。
 * 前端仅维护轻量选中态与拖拽目标，交换、合并、拆分与通关全部以后端快照为准。
 */
export function PuzzleRuntimeShell({
  run,
  isBusy = false,
  error = null,
  onBack,
  onSwapPieces,
  onDragPiece,
  onAdvanceNextLevel,
}: PuzzleRuntimeShellProps) {
  const [selectedPieceId, setSelectedPieceId] = useState<string | null>(null);
  const [dragState, setDragState] = useState<{
    pieceId: string;
    pointerId: number;
    dragging: boolean;
    startX: number;
    startY: number;
  } | null>(null);
  const boardRef = useRef<HTMLDivElement | null>(null);
  const currentLevel = run?.currentLevel ?? null;
  const board = currentLevel?.board ?? null;
  const { resolvedUrl: resolvedCoverImage } = useResolvedAssetReadUrl(
    currentLevel?.coverImageSrc ?? null,
  );

  const pieces = useMemo<PuzzleBoardPieceViewModel[]>(() => {
    if (!board) {
      return [];
    }
    return board.pieces.map((piece) => ({
      pieceId: piece.pieceId,
      row: piece.currentRow,
      col: piece.currentCol,
      correctRow: piece.correctRow,
      correctCol: piece.correctCol,
      label: buildPieceLabel(piece.pieceId),
    }));
  }, [board]);

  const mergedCellKeys = useMemo(() => {
    if (!board) {
      return new Set<string>();
    }
    return new Set(
      board.mergedGroups.flatMap((group) =>
        group.occupiedCells.map((cell) => boardCellKey(cell)),
      ),
    );
  }, [board]);

  const pieceByCell = useMemo(() => {
    const map = new Map<string, PuzzleBoardPieceViewModel>();
    for (const piece of pieces) {
      map.set(`${piece.row}:${piece.col}`, piece);
    }
    return map;
  }, [pieces]);

  if (!run || !currentLevel || !board) {
    return (
      <div className="fixed inset-0 z-[100] flex items-center justify-center bg-slate-950 text-white">
        <div className="flex items-center gap-2 rounded-full bg-white/10 px-5 py-3 text-sm">
          <Loader2 className="h-4 w-4 animate-spin" />
          正在进入拼图关卡
        </div>
      </div>
    );
  }

  const handlePieceClick = (pieceId: string) => {
    if (isBusy) {
      return;
    }

    if (!selectedPieceId) {
      setSelectedPieceId(pieceId);
      return;
    }

    if (selectedPieceId === pieceId) {
      setSelectedPieceId(null);
      return;
    }

    onSwapPieces({
      firstPieceId: selectedPieceId,
      secondPieceId: pieceId,
    });
    setSelectedPieceId(null);
  };

  const resolveBoardCellFromPointer = (clientX: number, clientY: number) => {
    const boardElement = boardRef.current;
    if (!boardElement) {
      return null;
    }

    const rect = boardElement.getBoundingClientRect();
    if (
      clientX < rect.left ||
      clientX > rect.right ||
      clientY < rect.top ||
      clientY > rect.bottom
    ) {
      return null;
    }

    const relativeX = clientX - rect.left;
    const relativeY = clientY - rect.top;
    const col = Math.min(
      board.cols - 1,
      Math.max(0, Math.floor((relativeX / rect.width) * board.cols)),
    );
    const row = Math.min(
      board.rows - 1,
      Math.max(0, Math.floor((relativeY / rect.height) * board.rows)),
    );

    return { row, col };
  };

  const handlePiecePointerUp = (
    pieceId: string,
    event: React.PointerEvent<HTMLDivElement>,
  ) => {
    const currentDragState = dragState;
    if (!currentDragState || currentDragState.pieceId !== pieceId) {
      return;
    }

    event.currentTarget.releasePointerCapture(event.pointerId);

    if (currentDragState.dragging) {
      const targetCell = resolveBoardCellFromPointer(
        event.clientX,
        event.clientY,
      );
      if (targetCell) {
        onDragPiece({
          pieceId,
          targetRow: targetCell.row,
          targetCol: targetCell.col,
        });
      }
      setSelectedPieceId(null);
      setDragState(null);
      return;
    }

    setDragState(null);
    handlePieceClick(pieceId);
  };

  const statusLabel =
    currentLevel.status === 'cleared' ? '已通关' : `${board.rows}x${board.cols}`;
  const nextAvailable =
    currentLevel.status === 'cleared' && Boolean(run.recommendedNextProfileId);

  return (
    <div className="fixed inset-0 z-[100] flex justify-center bg-slate-950 text-white">
      <div className="relative h-full w-full overflow-hidden bg-[radial-gradient(circle_at_50%_20%,rgba(251,191,36,0.18),transparent_28%),radial-gradient(circle_at_20%_80%,rgba(249,115,22,0.16),transparent_26%),linear-gradient(180deg,#2d160e,#020617)]">
        {currentLevel.coverImageSrc ? (
          <ResolvedAssetImage
            src={currentLevel.coverImageSrc}
            alt=""
            aria-hidden="true"
            className="absolute inset-0 h-full w-full object-cover opacity-[0.16] blur-2xl"
          />
        ) : null}
        <div className="absolute inset-0 bg-[linear-gradient(rgba(255,255,255,0.04)_1px,transparent_1px),linear-gradient(90deg,rgba(255,255,255,0.04)_1px,transparent_1px)] bg-[length:34px_34px] opacity-20" />

        <div className="absolute left-0 top-0 z-20 flex w-full items-start justify-between gap-3 px-4 py-4">
          <button
            type="button"
            onClick={onBack}
            className="inline-flex h-10 w-10 items-center justify-center rounded-full bg-black/30 backdrop-blur"
          >
            <ArrowLeft className="h-4 w-4" />
          </button>

          <div className="flex max-w-[70vw] flex-col items-end gap-1 rounded-[1.2rem] bg-black/26 px-4 py-3 text-right backdrop-blur">
            <div className="text-[0.68rem] font-semibold tracking-[0.2em] text-white/70">
              PUZZLE
            </div>
            <div className="line-clamp-1 text-sm font-bold text-white">
              {currentLevel.levelName}
            </div>
            <div className="text-xs text-white/74">
              {currentLevel.authorDisplayName} · 第 {currentLevel.levelIndex} 关 ·{' '}
              {statusLabel}
            </div>
          </div>
        </div>

        <div className="absolute inset-0 flex items-center justify-center p-4 pt-24 pb-28">
          <div
            ref={boardRef}
            className="grid aspect-square w-full max-w-[min(92vw,92vh)] rounded-[1.7rem] border border-white/12 bg-white/8 p-2 shadow-[0_30px_80px_rgba(0,0,0,0.35)] backdrop-blur-sm"
            style={{
              gridTemplateColumns: `repeat(${board.cols}, minmax(0, 1fr))`,
            }}
          >
            {buildBoardCells(board).map((cell) => {
              const piece = pieceByCell.get(`${cell.row}:${cell.col}`) ?? null;
              const occupied = Boolean(piece);
              const isMerged = mergedCellKeys.has(boardCellKey(cell));
              const isSelected = piece?.pieceId === selectedPieceId;

              return (
                <div
                  key={`${cell.row}:${cell.col}`}
                  className="relative p-1"
                >
                  <div
                    className={`flex h-full min-h-[4.5rem] items-center justify-center rounded-[1rem] border text-sm font-black transition ${
                      occupied
                        ? isSelected
                          ? 'border-amber-200 bg-amber-400/84 text-slate-950 shadow-[0_12px_30px_rgba(251,191,36,0.22)]'
                          : isMerged
                            ? 'border-emerald-200/55 bg-emerald-300/26 text-white'
                            : 'border-white/18 bg-white/12 text-white'
                        : 'border-white/8 bg-black/18 text-white/20'
                    }`}
                    onPointerDown={(event) => {
                      if (!piece || isBusy) {
                        return;
                      }
                      event.currentTarget.setPointerCapture(event.pointerId);
                      setDragState({
                        pieceId: piece.pieceId,
                        pointerId: event.pointerId,
                        dragging: false,
                        startX: event.clientX,
                        startY: event.clientY,
                      });
                    }}
                    onPointerMove={(event) => {
                      if (
                        !piece ||
                        !dragState ||
                        dragState.pieceId !== piece.pieceId ||
                        dragState.pointerId !== event.pointerId ||
                        dragState.dragging
                      ) {
                        return;
                      }

                      const deltaX = event.clientX - dragState.startX;
                      const deltaY = event.clientY - dragState.startY;
                      if (Math.hypot(deltaX, deltaY) >= 8) {
                        setDragState((current) =>
                          current && current.pieceId === piece.pieceId
                            ? {
                                ...current,
                                dragging: true,
                              }
                            : current,
                        );
                      }
                    }}
                    onPointerUp={(event) => {
                      if (piece) {
                        handlePiecePointerUp(piece.pieceId, event);
                      }
                    }}
                    onPointerCancel={() => {
                      setDragState(null);
                    }}
                  >
                    {piece ? (
                      <div className="relative h-full w-full overflow-hidden rounded-[0.92rem]">
                        {resolvedCoverImage ? (
                          <div
                            className="absolute inset-0"
                            style={{
                              backgroundImage: `url("${resolvedCoverImage}")`,
                              backgroundSize: `${board.cols * 100}% ${board.rows * 100}%`,
                              backgroundPosition: `${
                                board.cols > 1
                                  ? (piece.correctCol / (board.cols - 1)) * 100
                                  : 0
                              }% ${
                                board.rows > 1
                                  ? (piece.correctRow / (board.rows - 1)) * 100
                                  : 0
                              }%`,
                            }}
                          />
                        ) : (
                          <div className="absolute inset-0 bg-[linear-gradient(145deg,rgba(251,191,36,0.4),rgba(76,29,19,0.72))]" />
                        )}
                        <div className="absolute inset-0 bg-black/10" />
                        <div className="absolute bottom-1 right-1 rounded-full bg-black/38 px-1.5 py-0.5 text-[10px] font-black text-white/86">
                          {piece.label}
                        </div>
                      </div>
                    ) : (
                      ''
                    )}
                  </div>
                </div>
              );
            })}
          </div>
        </div>

        <div className="absolute bottom-0 left-0 z-20 flex w-full items-end justify-between gap-3 px-4 py-4">
          <div className="max-w-[18rem] rounded-[1.1rem] bg-black/28 px-4 py-3 text-xs leading-6 text-white/74 backdrop-blur">
            {selectedPieceId
              ? '已选择一块，再点另一块可交换；也可以直接拖到目标位置。'
              : '点击两块可交换，拖动单块或合并块到目标格继续推进。'}
          </div>

          <div className="flex flex-col items-end gap-2">
            {error ? (
              <div className="rounded-full bg-red-500/20 px-3 py-1 text-xs text-red-100">
                {error}
              </div>
            ) : null}
            {nextAvailable ? (
              <button
                type="button"
                disabled={isBusy}
                onClick={onAdvanceNextLevel}
                className="inline-flex items-center gap-2 rounded-full bg-amber-200 px-4 py-2 text-sm font-bold text-slate-950 disabled:opacity-45"
              >
                下一关
                <ArrowRight className="h-4 w-4" />
              </button>
            ) : (
              <div className="rounded-full bg-black/28 px-4 py-2 text-xs text-white/72 backdrop-blur">
                {isBusy
                  ? '同步中...'
                  : currentLevel.status === 'cleared'
                    ? '等待下一关候选'
                    : '完成整张图即可通关'}
              </div>
            )}
          </div>
        </div>
      </div>
    </div>
  );
}

export default PuzzleRuntimeShell;
