import {
  ArrowLeft,
  Brush,
  Check,
  Eraser,
  ImagePlus,
  RotateCcw,
  Save,
  Sparkles,
} from 'lucide-react';
import {
  type CSSProperties,
  type PointerEvent as ReactPointerEvent,
  useCallback,
  useEffect,
  useMemo,
  useRef,
  useState,
} from 'react';

import type {
  BabyLoveDrawingPoint,
  BabyLoveDrawingRecord,
  BabyLoveDrawingStroke,
  BabyLoveDrawingTool,
} from '../../../packages/shared/src/contracts/edutainmentBabyDrawing';
import { BABY_LOVE_DRAWING_RAINBOW_COLORS } from '../../../packages/shared/src/contracts/edutainmentBabyDrawing';
import {
  createBabyLoveDrawingMagicImage,
  saveBabyLoveDrawing,
} from '../../services/edutainment-baby-drawing';
import type { MocapHandInput } from '../../services/useMocapInput';
import { useMocapInput } from '../../services/useMocapInput';
import { PlatformRuntimeStatusToast } from '../common/PlatformRuntimeStatusToast';
import {
  appendPointToStroke,
  BABY_LOVE_DRAWING_BRUSH_SIZE,
  BABY_LOVE_DRAWING_DEFAULT_COLOR,
  BABY_LOVE_DRAWING_ERASER_SIZE,
  type BabyLoveDrawingBounds,
  type BabyLoveDrawingHandPoint,
  type BabyLoveDrawingHoverTarget,
  type BabyLoveDrawingPhase,
  createBabyDrawingStroke,
  hasHoverCompleted,
  isPointInsideBounds,
  resolveHoverProgress,
  toCanvasPoint,
} from './babyLoveDrawingModel';

type BabyLoveDrawingRuntimeShellProps = {
  onBack?: () => void;
};

type ActionButtonId = 'finish' | 'magic' | 'save' | 'restart' | 'back';

type RectMap = {
  canvas: BabyLoveDrawingBounds | null;
  colors: Record<string, BabyLoveDrawingBounds>;
  tools: Record<BabyLoveDrawingTool, BabyLoveDrawingBounds | null>;
  buttons: Record<ActionButtonId, BabyLoveDrawingBounds | null>;
};

type ActiveStrokeState = {
  stroke: BabyLoveDrawingStroke;
  lastPoint: BabyLoveDrawingPoint;
};

const BABY_LOVE_DRAWING_HAND_LOSS_GRACE_MS = 320;
const BABY_LOVE_DRAWING_HAND_SMOOTHING = 0.38;
const BABY_LOVE_DRAWING_RIGHT_HAND_MAX_FRAME_JUMP = 0.28;

const EMPTY_RECT_MAP: RectMap = {
  canvas: null,
  colors: {},
  tools: {
    brush: null,
    eraser: null,
  },
  buttons: {
    finish: null,
    magic: null,
    save: null,
    restart: null,
    back: null,
  },
};

function pointFromPointer(
  event: ReactPointerEvent<HTMLElement>,
  element: HTMLElement,
): BabyLoveDrawingHandPoint {
  const rect = element.getBoundingClientRect();
  const width = rect.width || 1;
  const height = rect.height || 1;

  return {
    x: Math.max(0, Math.min(1, (event.clientX - rect.left) / width)),
    y: Math.max(0, Math.min(1, (event.clientY - rect.top) / height)),
    state: event.buttons ? 'grab' : 'open_palm',
  };
}

function handToPoint(hand: MocapHandInput | null | undefined) {
  if (!hand) {
    return null;
  }

  return {
    x: hand.x,
    y: hand.y,
    state: hand.state,
  } satisfies BabyLoveDrawingHandPoint;
}

function commandToPlayerLeftHand(command: {
  rightHand?: MocapHandInput | null;
}) {
  // 本地 mocap handedness 当前按摄像头视角输出：画面右侧手对应用户身体左手。
  return handToPoint(command.rightHand);
}

function commandToPlayerRightHand(command: {
  leftHand?: MocapHandInput | null;
}) {
  // 本地 mocap handedness 当前按摄像头视角输出：画面左侧手对应用户身体右手。
  return handToPoint(command.leftHand);
}

function smoothHandPoint(
  previous: BabyLoveDrawingHandPoint | null,
  next: BabyLoveDrawingHandPoint,
): BabyLoveDrawingHandPoint {
  if (!previous) {
    return next;
  }

  return {
    x:
      previous.x +
      (next.x - previous.x) * BABY_LOVE_DRAWING_HAND_SMOOTHING,
    y:
      previous.y +
      (next.y - previous.y) * BABY_LOVE_DRAWING_HAND_SMOOTHING,
    state: next.state,
  };
}

function getHandPointDistance(
  left: BabyLoveDrawingHandPoint,
  right: BabyLoveDrawingHandPoint,
) {
  return Math.hypot(left.x - right.x, left.y - right.y);
}

function canAcceptRightHandPoint(
  previous: BabyLoveDrawingHandPoint | null,
  next: BabyLoveDrawingHandPoint | null,
) {
  if (!next || !previous) {
    return Boolean(next);
  }

  return (
    getHandPointDistance(previous, next) <=
    BABY_LOVE_DRAWING_RIGHT_HAND_MAX_FRAME_JUMP
  );
}

function sameHoverTarget(
  left: BabyLoveDrawingHoverTarget,
  right: BabyLoveDrawingHoverTarget,
) {
  if (!left || !right) {
    return left === right;
  }

  return left.kind === right.kind && left.id === right.id;
}

function findTargetInBounds<T extends string>(
  point: BabyLoveDrawingHandPoint | null,
  bounds: Record<T, BabyLoveDrawingBounds | null>,
): T | null {
  if (!point) {
    return null;
  }

  for (const [id, rect] of Object.entries(bounds) as Array<
    [T, BabyLoveDrawingBounds | null]
  >) {
    if (rect && isPointInsideBounds(point, rect)) {
      return id;
    }
  }

  return null;
}

function drawStrokeSegment(
  context: CanvasRenderingContext2D,
  stroke: BabyLoveDrawingStroke,
  from: BabyLoveDrawingPoint,
  to: BabyLoveDrawingPoint,
  width: number,
  height: number,
) {
  context.save();
  context.lineCap = 'round';
  context.lineJoin = 'round';
  context.lineWidth =
    stroke.tool === 'brush'
      ? BABY_LOVE_DRAWING_BRUSH_SIZE
      : BABY_LOVE_DRAWING_ERASER_SIZE;
  if (stroke.tool === 'eraser') {
    context.globalCompositeOperation = 'destination-out';
    context.strokeStyle = 'rgba(0,0,0,1)';
  } else {
    context.globalCompositeOperation = 'source-over';
    context.strokeStyle = stroke.color;
  }
  context.beginPath();
  context.moveTo(from.x * width, from.y * height);
  context.lineTo(to.x * width, to.y * height);
  context.stroke();
  context.restore();
}

export function BabyLoveDrawingRuntimeShell({
  onBack,
}: BabyLoveDrawingRuntimeShellProps) {
  const shellRef = useRef<HTMLElement | null>(null);
  const canvasRef = useRef<HTMLCanvasElement | null>(null);
  const rectMapRef = useRef<RectMap>(EMPTY_RECT_MAP);
  const activeStrokeRef = useRef<ActiveStrokeState | null>(null);
  const hoverTargetRef = useRef<BabyLoveDrawingHoverTarget>(null);
  const hoverStartedAtRef = useRef<number | null>(null);
  const hoverCompletedKeyRef = useRef<string | null>(null);
  const previousToolGrabRef = useRef<string | null>(null);
  const visibleLeftHandRef = useRef<BabyLoveDrawingHandPoint | null>(null);
  const visibleRightHandRef = useRef<BabyLoveDrawingHandPoint | null>(null);
  const leftHandSeenAtRef = useRef<number | null>(null);
  const rightHandSeenAtRef = useRef<number | null>(null);
  const [phase, setPhase] = useState<BabyLoveDrawingPhase>('drawing');
  const [selectedColor, setSelectedColor] = useState<string>(
    BABY_LOVE_DRAWING_DEFAULT_COLOR,
  );
  const [selectedTool, setSelectedTool] =
    useState<BabyLoveDrawingTool>('brush');
  const [strokes, setStrokes] = useState<BabyLoveDrawingStroke[]>([]);
  const [rightHandPoint, setRightHandPoint] =
    useState<BabyLoveDrawingHandPoint | null>(null);
  const [leftHandPoint, setLeftHandPoint] =
    useState<BabyLoveDrawingHandPoint | null>(null);
  const [hoverTarget, setHoverTarget] =
    useState<BabyLoveDrawingHoverTarget>(null);
  const [hoverProgress, setHoverProgress] = useState(0);
  const [originalImageSrc, setOriginalImageSrc] = useState<string | null>(null);
  const [magicImageSrc, setMagicImageSrc] = useState<string | null>(null);
  const [savedRecord, setSavedRecord] = useState<BabyLoveDrawingRecord | null>(
    null,
  );
  const [error, setError] = useState<string | null>(null);
  const { latestCommand } = useMocapInput({ enabled: true });

  const canUseMagic = phase === 'finished' || phase === 'magicReady';
  const canSave = phase === 'finished' || phase === 'magicReady';

  const actionButtons = useMemo(
    () => [
      {
        id: 'finish' as const,
        label: '完成',
        icon: Check,
        visible: phase === 'drawing',
      },
      {
        id: 'magic' as const,
        label: phase === 'magicPending' ? '魔法中' : '使用绘画魔法',
        icon: Sparkles,
        visible: phase === 'finished' || phase === 'magicReady' || phase === 'magicPending',
      },
      {
        id: 'save' as const,
        label: '保存',
        icon: Save,
        visible: canSave,
      },
      {
        id: 'restart' as const,
        label: '再画一张',
        icon: RotateCcw,
        visible: phase === 'saved',
      },
      {
        id: 'back' as const,
        label: '返回',
        icon: ArrowLeft,
        visible: phase === 'saved',
      },
    ],
    [canSave, phase],
  );

  const updateRectMap = useCallback(() => {
    const shell = shellRef.current;
    if (!shell) {
      return;
    }

    const shellRect = shell.getBoundingClientRect();
    const toUnitBounds = (element: Element | null): BabyLoveDrawingBounds | null => {
      if (!(element instanceof HTMLElement)) {
        return null;
      }

      const rect = element.getBoundingClientRect();
      return {
        left: (rect.left - shellRect.left) / shellRect.width,
        top: (rect.top - shellRect.top) / shellRect.height,
        width: rect.width / shellRect.width,
        height: rect.height / shellRect.height,
      };
    };
    const colors: Record<string, BabyLoveDrawingBounds> = {};
    BABY_LOVE_DRAWING_RAINBOW_COLORS.forEach((color) => {
      const rect = toUnitBounds(
        shell.querySelector(`[data-baby-drawing-color="${color.id}"]`),
      );
      if (rect) {
        colors[color.id] = rect;
      }
    });

    rectMapRef.current = {
      canvas: toUnitBounds(shell.querySelector('[data-baby-drawing-canvas]')),
      colors,
      tools: {
        brush: toUnitBounds(shell.querySelector('[data-baby-drawing-tool="brush"]')),
        eraser: toUnitBounds(shell.querySelector('[data-baby-drawing-tool="eraser"]')),
      },
      buttons: {
        finish: toUnitBounds(shell.querySelector('[data-baby-drawing-button="finish"]')),
        magic: toUnitBounds(shell.querySelector('[data-baby-drawing-button="magic"]')),
        save: toUnitBounds(shell.querySelector('[data-baby-drawing-button="save"]')),
        restart: toUnitBounds(shell.querySelector('[data-baby-drawing-button="restart"]')),
        back: toUnitBounds(shell.querySelector('[data-baby-drawing-button="back"]')),
      },
    };
  }, []);

  useEffect(() => {
    updateRectMap();
    window.addEventListener('resize', updateRectMap);
    return () => window.removeEventListener('resize', updateRectMap);
  }, [updateRectMap]);

  useEffect(() => {
    updateRectMap();
  }, [actionButtons, phase, updateRectMap]);

  useEffect(() => {
    const canvas = canvasRef.current;
    if (!canvas) {
      return;
    }

    const resizeCanvas = () => {
      const rect = canvas.getBoundingClientRect();
      const dpr = Math.min(window.devicePixelRatio || 1, 2);
      const nextWidth = Math.max(1, Math.floor(rect.width * dpr));
      const nextHeight = Math.max(1, Math.floor(rect.height * dpr));
      if (canvas.width === nextWidth && canvas.height === nextHeight) {
        return;
      }

      const previousImage = canvas.toDataURL('image/png');
      canvas.width = nextWidth;
      canvas.height = nextHeight;
      const context = canvas.getContext('2d');
      if (!context) {
        return;
      }
      context.fillStyle = '#fffdf4';
      context.fillRect(0, 0, canvas.width, canvas.height);
      if (previousImage) {
        const image = new Image();
        image.onload = () => {
          context.drawImage(image, 0, 0, canvas.width, canvas.height);
        };
        image.src = previousImage;
      }
    };

    resizeCanvas();
    window.addEventListener('resize', resizeCanvas);
    return () => window.removeEventListener('resize', resizeCanvas);
  }, []);

  const clearCanvas = useCallback(() => {
    const canvas = canvasRef.current;
    const context = canvas?.getContext('2d');
    if (!canvas || !context) {
      return;
    }

    context.globalCompositeOperation = 'source-over';
    context.fillStyle = '#fffdf4';
    context.fillRect(0, 0, canvas.width, canvas.height);
  }, []);

  useEffect(() => {
    clearCanvas();
  }, [clearCanvas]);

  const captureOriginalImage = useCallback(() => {
    const canvas = canvasRef.current;
    if (!canvas) {
      return null;
    }

    return canvas.toDataURL('image/png');
  }, []);

  const finishDrawing = useCallback(() => {
    const imageSrc = captureOriginalImage();
    if (!imageSrc) {
      return;
    }

    activeStrokeRef.current = null;
    setOriginalImageSrc(imageSrc);
    setPhase('finished');
    setError(null);
  }, [captureOriginalImage]);

  const restartDrawing = useCallback(() => {
    activeStrokeRef.current = null;
    hoverTargetRef.current = null;
    hoverStartedAtRef.current = null;
    hoverCompletedKeyRef.current = null;
    setPhase('drawing');
    setSelectedColor(BABY_LOVE_DRAWING_DEFAULT_COLOR);
    setSelectedTool('brush');
    setStrokes([]);
    setOriginalImageSrc(null);
    setMagicImageSrc(null);
    setSavedRecord(null);
    setError(null);
    setHoverTarget(null);
    setHoverProgress(0);
    clearCanvas();
  }, [clearCanvas]);

  const saveCurrentDrawing = useCallback(() => {
    const imageSrc = originalImageSrc ?? captureOriginalImage();
    if (!imageSrc) {
      return;
    }

    const response = saveBabyLoveDrawing({
      originalImageSrc: imageSrc,
      magicImageSrc,
      strokeTrace: strokes,
    });
    setOriginalImageSrc(imageSrc);
    setSavedRecord(response.record);
    setPhase('saved');
    setError(null);
  }, [captureOriginalImage, magicImageSrc, originalImageSrc, strokes]);

  const generateMagicImage = useCallback(async () => {
    const imageSrc = originalImageSrc ?? captureOriginalImage();
    if (!imageSrc || phase === 'magicPending') {
      return;
    }

    setOriginalImageSrc(imageSrc);
    setPhase('magicPending');
    setError(null);
    try {
      const response = await createBabyLoveDrawingMagicImage({
        originalImageSrc: imageSrc,
        strokeTrace: strokes,
      });
      setMagicImageSrc(response.magicImageSrc);
      setPhase('magicReady');
    } catch (magicError) {
      setError(
        magicError instanceof Error
          ? magicError.message
          : '生成宝贝爱画魔法图片失败。',
      );
      setPhase('finished');
    }
  }, [captureOriginalImage, originalImageSrc, phase, strokes]);

  const triggerButton = useCallback(
    (buttonId: string) => {
      if (buttonId === 'finish' && phase === 'drawing') {
        finishDrawing();
        return;
      }

      if (buttonId === 'magic' && canUseMagic) {
        void generateMagicImage();
        return;
      }

      if (buttonId === 'save' && canSave) {
        saveCurrentDrawing();
        return;
      }

      if (buttonId === 'restart' && phase === 'saved') {
        restartDrawing();
        return;
      }

      if (buttonId === 'back' && phase === 'saved') {
        onBack?.();
      }
    },
    [
      canSave,
      canUseMagic,
      finishDrawing,
      generateMagicImage,
      onBack,
      phase,
      restartDrawing,
      saveCurrentDrawing,
    ],
  );

  const applyHoverTarget = useCallback(
    (nextTarget: BabyLoveDrawingHoverTarget) => {
      const currentTarget = hoverTargetRef.current;
      const now = Date.now();
      if (!sameHoverTarget(currentTarget, nextTarget)) {
        hoverTargetRef.current = nextTarget;
        hoverStartedAtRef.current = nextTarget ? now : null;
        hoverCompletedKeyRef.current = null;
        setHoverTarget(nextTarget);
        setHoverProgress(0);
        return;
      }

      const startedAt = hoverStartedAtRef.current;
      const progress = resolveHoverProgress(nextTarget, startedAt, now);
      setHoverProgress(progress);
      if (!hasHoverCompleted(nextTarget, startedAt, now) || !nextTarget) {
        return;
      }

      const completeKey = `${nextTarget.kind}:${nextTarget.id}`;
      if (hoverCompletedKeyRef.current === completeKey) {
        return;
      }

      hoverCompletedKeyRef.current = completeKey;
      if (nextTarget.kind === 'color') {
        const color = BABY_LOVE_DRAWING_RAINBOW_COLORS.find(
          (item) => item.id === nextTarget.id,
        );
        if (color) {
          setSelectedColor(color.value);
          setSelectedTool('brush');
        }
        return;
      }

      triggerButton(nextTarget.id);
    },
    [triggerButton],
  );

  const updateToolFromRightHand = useCallback((point: BabyLoveDrawingHandPoint | null) => {
    if (!point || point.state !== 'grab') {
      previousToolGrabRef.current = null;
      return;
    }

    const tool = findTargetInBounds(point, rectMapRef.current.tools);
    if (!tool) {
      previousToolGrabRef.current = null;
      return;
    }

    if (previousToolGrabRef.current === tool) {
      return;
    }

    previousToolGrabRef.current = tool;
    setSelectedTool(tool);
  }, []);

  const drawWithRightHand = useCallback(
    (point: BabyLoveDrawingHandPoint | null) => {
      const canvasBounds = rectMapRef.current.canvas;
      const canvas = canvasRef.current;
      const context = canvas?.getContext('2d');
      if (
        phase !== 'drawing' ||
        !point ||
        point.state !== 'grab' ||
        !canvasBounds ||
        !canvas ||
        !context ||
        !isPointInsideBounds(point, canvasBounds)
      ) {
        activeStrokeRef.current = null;
        return;
      }

      const nextPoint = toCanvasPoint(point, canvasBounds);
      const activeStroke = activeStrokeRef.current;
      if (!activeStroke) {
        const stroke = createBabyDrawingStroke(
          selectedTool,
          selectedColor,
          nextPoint,
        );
        activeStrokeRef.current = {
          stroke,
          lastPoint: nextPoint,
        };
        setStrokes((current) => [...current, stroke]);
        return;
      }

      const nextStroke = appendPointToStroke(activeStroke.stroke, nextPoint);
      drawStrokeSegment(
        context,
        nextStroke,
        activeStroke.lastPoint,
        nextPoint,
        canvas.width,
        canvas.height,
      );
      activeStrokeRef.current = {
        stroke: nextStroke,
        lastPoint: nextPoint,
      };
      setStrokes((current) =>
        current.map((stroke) =>
          stroke.strokeId === nextStroke.strokeId ? nextStroke : stroke,
        ),
      );
    },
    [phase, selectedColor, selectedTool],
  );

  const updateInteraction = useCallback(
    (
      nextLeftHand: BabyLoveDrawingHandPoint | null,
      nextRightHand: BabyLoveDrawingHandPoint | null,
    ) => {
      const now = Date.now();
      const previousLeftHand = visibleLeftHandRef.current;
      const previousRightHand = visibleRightHandRef.current;
      const acceptedRightHand = canAcceptRightHandPoint(
        previousRightHand,
        nextRightHand,
      )
        ? nextRightHand
        : null;
      const visibleLeftHand = nextLeftHand
        ? smoothHandPoint(previousLeftHand, nextLeftHand)
        : previousLeftHand &&
            leftHandSeenAtRef.current !== null &&
            now - leftHandSeenAtRef.current <=
              BABY_LOVE_DRAWING_HAND_LOSS_GRACE_MS
          ? previousLeftHand
          : null;
      const visibleRightHand = acceptedRightHand
        ? smoothHandPoint(previousRightHand, acceptedRightHand)
        : previousRightHand &&
            rightHandSeenAtRef.current !== null &&
            now - rightHandSeenAtRef.current <=
              BABY_LOVE_DRAWING_HAND_LOSS_GRACE_MS
          ? previousRightHand
          : null;
      const activeRightHand = acceptedRightHand ? visibleRightHand : null;

      if (nextLeftHand) {
        leftHandSeenAtRef.current = now;
      }
      if (acceptedRightHand) {
        rightHandSeenAtRef.current = now;
      }

      visibleLeftHandRef.current = visibleLeftHand;
      visibleRightHandRef.current = visibleRightHand;
      setLeftHandPoint(visibleLeftHand);
      setRightHandPoint(visibleRightHand);
      updateToolFromRightHand(activeRightHand);
      drawWithRightHand(activeRightHand);

      const colorId = findTargetInBounds(
        visibleLeftHand,
        rectMapRef.current.colors,
      );
      const buttonId =
        findTargetInBounds(visibleLeftHand, rectMapRef.current.buttons) ??
        findTargetInBounds(visibleRightHand, rectMapRef.current.buttons);
      const nextHoverTarget: BabyLoveDrawingHoverTarget = colorId
        ? { kind: 'color', id: colorId }
        : buttonId
          ? { kind: 'button', id: buttonId }
          : null;
      applyHoverTarget(nextHoverTarget);
    },
    [applyHoverTarget, drawWithRightHand, updateToolFromRightHand],
  );

  useEffect(() => {
    if (!latestCommand) {
      return;
    }

    updateInteraction(
      commandToPlayerLeftHand(latestCommand),
      commandToPlayerRightHand(latestCommand),
    );
  }, [latestCommand, updateInteraction]);

  const handlePointerDown = (event: ReactPointerEvent<HTMLElement>) => {
    const point = pointFromPointer(event, event.currentTarget);
    if (event.button === 2) {
      updateInteraction(leftHandPoint, { ...point, state: 'grab' as const });
      return;
    }

    updateInteraction({ ...point, state: 'open_palm' as const }, null);
  };

  const handlePointerMove = (event: ReactPointerEvent<HTMLElement>) => {
    const point = pointFromPointer(event, event.currentTarget);
    const nextState: BabyLoveDrawingHandPoint['state'] = event.buttons
      ? 'grab'
      : 'open_palm';
    const nextPoint: BabyLoveDrawingHandPoint = {
      ...point,
      state: nextState,
    };
    if (event.buttons === 2) {
      updateInteraction(leftHandPoint, nextPoint);
      return;
    }
    updateInteraction(nextPoint, null);
  };

  const handlePointerUp = (event: ReactPointerEvent<HTMLElement>) => {
    const point = pointFromPointer(event, event.currentTarget);
    if (event.button === 2) {
      updateInteraction(leftHandPoint, { ...point, state: 'open_palm' as const });
      return;
    }

    updateInteraction({ ...point, state: 'open_palm' as const }, null);
  };

  return (
    <main
      ref={shellRef}
      className="baby-love-drawing-runtime"
      data-testid="baby-love-drawing-runtime"
      onPointerDown={handlePointerDown}
      onPointerMove={handlePointerMove}
      onPointerUp={handlePointerUp}
      onPointerCancel={handlePointerUp}
      onContextMenu={(event) => event.preventDefault()}
    >
      <button
        type="button"
        className="baby-love-drawing-runtime__back"
        onClick={onBack}
        aria-label="返回"
        title="返回"
      >
        <ArrowLeft className="h-5 w-5" />
      </button>

      <div className="baby-love-drawing-runtime__colors" aria-label="颜色">
        {BABY_LOVE_DRAWING_RAINBOW_COLORS.map((color) => (
          <button
            key={color.id}
            type="button"
            data-baby-drawing-color={color.id}
            className={`baby-love-drawing-runtime__color${
              selectedColor === color.value
                ? ' baby-love-drawing-runtime__color--active'
                : ''
            }`}
            style={{ '--baby-drawing-color': color.value } as CSSProperties}
            aria-label={color.label}
            title={color.label}
          />
        ))}
      </div>

      <section className="baby-love-drawing-runtime__board">
        <canvas
          ref={canvasRef}
          data-baby-drawing-canvas
          className="baby-love-drawing-runtime__canvas"
          aria-label="画板"
        />
        {magicImageSrc && phase !== 'drawing' ? (
          <img
            src={magicImageSrc}
            alt="绘画魔法结果"
            className="baby-love-drawing-runtime__magic-image"
          />
        ) : null}
        {phase === 'magicPending' ? (
          <div className="baby-love-drawing-runtime__magic-pending">
            <Sparkles className="h-7 w-7" />
            绘画魔法
          </div>
        ) : null}
      </section>

      <div className="baby-love-drawing-runtime__tools" aria-label="工具">
        <button
          type="button"
          data-baby-drawing-tool="brush"
          className={`baby-love-drawing-runtime__tool${
            selectedTool === 'brush'
              ? ' baby-love-drawing-runtime__tool--active'
              : ''
          }`}
          aria-label="画笔"
          title="画笔"
        >
          <Brush className="h-7 w-7" />
        </button>
        <button
          type="button"
          data-baby-drawing-tool="eraser"
          className={`baby-love-drawing-runtime__tool${
            selectedTool === 'eraser'
              ? ' baby-love-drawing-runtime__tool--active'
              : ''
          }`}
          aria-label="橡皮"
          title="橡皮"
        >
          <Eraser className="h-7 w-7" />
        </button>
      </div>

      <div className="baby-love-drawing-runtime__actions">
        {actionButtons
          .filter((button) => button.visible)
          .map((button) => {
            const Icon = button.icon;
            const isHovering =
              hoverTarget?.kind === 'button' && hoverTarget.id === button.id;
            return (
              <button
                key={button.id}
                type="button"
                data-baby-drawing-button={button.id}
                className="baby-love-drawing-runtime__action"
                disabled={button.id === 'magic' && phase === 'magicPending'}
                onClick={() => triggerButton(button.id)}
              >
                <Icon className="h-4 w-4" />
                <span>{button.label}</span>
                {isHovering ? (
                  <span
                    className="baby-love-drawing-runtime__action-progress"
                    style={
                      {
                        '--baby-drawing-hover-progress': `${hoverProgress * 100}%`,
                      } as CSSProperties
                    }
                  />
                ) : null}
              </button>
            );
          })}
      </div>

      {error ? (
        <PlatformRuntimeStatusToast
          tone="error"
          className="baby-love-drawing-runtime__status baby-love-drawing-runtime__status--top"
        >
          {error}
        </PlatformRuntimeStatusToast>
      ) : null}

      {savedRecord ? (
        <PlatformRuntimeStatusToast
          tone="success"
          role="status"
          className="baby-love-drawing-runtime__status baby-love-drawing-runtime__status--saved"
        >
          <ImagePlus className="h-5 w-5" />
          已保存
        </PlatformRuntimeStatusToast>
      ) : null}

      {leftHandPoint ? (
        <div
          className="baby-love-drawing-runtime__left-hand-indicator"
          aria-hidden="true"
          style={
            {
              left: `${leftHandPoint.x * 100}%`,
              top: `${leftHandPoint.y * 100}%`,
            } as CSSProperties
          }
        >
          <span />
        </div>
      ) : null}

      <div
        className={`baby-love-drawing-runtime__cursor baby-love-drawing-runtime__cursor--${selectedTool}`}
        style={
          {
            left: `${(rightHandPoint?.x ?? 0.5) * 100}%`,
            top: `${(rightHandPoint?.y ?? 0.5) * 100}%`,
            '--baby-drawing-color': selectedColor,
          } as CSSProperties
        }
      >
        {selectedTool === 'brush' ? (
          <Brush className="h-5 w-5" />
        ) : (
          <Eraser className="h-5 w-5" />
        )}
      </div>
    </main>
  );
}

export default BabyLoveDrawingRuntimeShell;
