import { useCallback, useEffect, useRef, useState } from 'react';
import * as Icon from '@/Components/ClassRoom/Icons';

// ─── types ───────────────────────────────────────────────────────────────────

export type BoardEvent =
  | { t: 's'; c: string; w: number; p: number[][] }
  | { t: 'txt'; c: string; fc: string; s: number; x: number; y: number; text: string }
  | { t: 'rect'; c: string; fc: string; w: number; x: number; y: number; rw: number; rh: number }
  | { t: 'circle'; c: string; fc: string; w: number; cx: number; cy: number; rx: number; ry: number }
  | { t: 'line'; c: string; w: number; x1: number; y1: number; x2: number; y2: number }
  | { t: 'arrow'; c: string; w: number; x1: number; y1: number; x2: number; y2: number }
  | { t: 'diamond'; c: string; fc: string; w: number; cx: number; cy: number; rw: number; rh: number }
  | { t: 'img'; x: number; y: number; rw: number; rh: number; data: string }
  | { t: 'clear' };

type Tool = 'pen' | 'eraser' | 'line' | 'arrow' | 'rect' | 'circle' | 'diamond' | 'text';
type Bg = 'plain' | 'grid' | 'dots';

type Props = {
  isHost: boolean;
  base: string;
};

// ─── constants ───────────────────────────────────────────────────────────────

const STROKE_COLORS = [
  '#1a1a2e', '#dc2626', '#2563eb', '#16a34a', '#d97706',
  '#7c3aed', '#0891b2', '#db2777', '#ffffff', '#6b7280',
];
const WIDTHS = [1, 2, 3, 5, 8, 12];
const BOARD_W = 1400;
const BOARD_H = 800;

function xsrf(): string {
  const m = document.cookie.match(/XSRF-TOKEN=([^;]+)/);
  return m ? decodeURIComponent(m[1]) : '';
}

async function apiPost(path: string, body: Record<string, unknown>): Promise<void> {
  const response = await fetch(path, {
    method: 'POST',
    credentials: 'same-origin',
    headers: {
      'Content-Type': 'application/json',
      'X-Requested-With': 'XMLHttpRequest',
      'X-XSRF-TOKEN': xsrf(),
    },
    body: JSON.stringify(body),
  });
  if (!response.ok) throw new Error(`whiteboard request failed (${response.status})`);
}

// ─── draw helper ─────────────────────────────────────────────────────────────

function drawEventOnCtx(ctx: CanvasRenderingContext2D, ev: BoardEvent, W: number, H: number): void {
  const px = (v: number) => (v / 1000) * W;
  const py = (v: number) => (v / 1000) * H;

  if (ev.t === 'clear') {
    ctx.clearRect(0, 0, W, H);
    return;
  }
  ctx.save();
  if (ev.t === 's') {
    if (ev.p.length < 2) { ctx.restore(); return; }
    ctx.globalCompositeOperation = 'source-over';
    ctx.strokeStyle = ev.c;
    ctx.lineWidth = ev.w;
    ctx.lineCap = 'round';
    ctx.lineJoin = 'round';
    ctx.beginPath();
    ctx.moveTo(px(ev.p[0][0]), py(ev.p[0][1]));
    for (let i = 1; i < ev.p.length; i++) ctx.lineTo(px(ev.p[i][0]), py(ev.p[i][1]));
    ctx.stroke();
  } else if (ev.t === 'line') {
    ctx.strokeStyle = ev.c;
    ctx.lineWidth = ev.w;
    ctx.lineCap = 'round';
    ctx.beginPath();
    ctx.moveTo(px(ev.x1), py(ev.y1));
    ctx.lineTo(px(ev.x2), py(ev.y2));
    ctx.stroke();
  } else if (ev.t === 'arrow') {
    const x1 = px(ev.x1); const y1 = py(ev.y1);
    const x2 = px(ev.x2); const y2 = py(ev.y2);
    const angle = Math.atan2(y2 - y1, x2 - x1);
    const head = Math.max(12, ev.w * 4);
    ctx.strokeStyle = ev.c;
    ctx.fillStyle = ev.c;
    ctx.lineWidth = ev.w;
    ctx.lineCap = 'round';
    ctx.beginPath();
    ctx.moveTo(x1, y1);
    ctx.lineTo(x2, y2);
    ctx.stroke();
    ctx.beginPath();
    ctx.moveTo(x2, y2);
    ctx.lineTo(x2 - head * Math.cos(angle - Math.PI / 6), y2 - head * Math.sin(angle - Math.PI / 6));
    ctx.lineTo(x2 - head * Math.cos(angle + Math.PI / 6), y2 - head * Math.sin(angle + Math.PI / 6));
    ctx.closePath();
    ctx.fill();
  } else if (ev.t === 'rect') {
    const fc = ev.fc ?? 'transparent';
    ctx.strokeStyle = ev.c;
    ctx.lineWidth = ev.w;
    ctx.fillStyle = fc;
    if (fc !== 'transparent' && fc !== '') ctx.fillRect(px(ev.x), py(ev.y), (ev.rw / 1000) * W, (ev.rh / 1000) * H);
    ctx.strokeRect(px(ev.x), py(ev.y), (ev.rw / 1000) * W, (ev.rh / 1000) * H);
  } else if (ev.t === 'circle') {
    const fc = ev.fc ?? 'transparent';
    const rx = ev.rx ?? (ev as unknown as { r?: number }).r ?? 10;
    const ry = ev.ry ?? rx;
    ctx.strokeStyle = ev.c;
    ctx.lineWidth = ev.w;
    ctx.fillStyle = fc;
    ctx.beginPath();
    ctx.ellipse(px(ev.cx), py(ev.cy), (rx / 1000) * W, (ry / 1000) * H, 0, 0, Math.PI * 2);
    if (fc !== 'transparent' && fc !== '') ctx.fill();
    ctx.stroke();
  } else if (ev.t === 'diamond') {
    const fc = ev.fc ?? 'transparent';
    const cx = px(ev.cx); const cy = py(ev.cy);
    const rx2 = (ev.rw / 1000) * W / 2;
    const ry2 = (ev.rh / 1000) * H / 2;
    ctx.strokeStyle = ev.c;
    ctx.lineWidth = ev.w;
    ctx.fillStyle = fc;
    ctx.beginPath();
    ctx.moveTo(cx, cy - ry2);
    ctx.lineTo(cx + rx2, cy);
    ctx.lineTo(cx, cy + ry2);
    ctx.lineTo(cx - rx2, cy);
    ctx.closePath();
    if (fc !== 'transparent' && fc !== '') ctx.fill();
    ctx.stroke();
  } else if (ev.t === 'txt') {
    const sz = Math.round((ev.s / 1000) * H);
    ctx.fillStyle = ev.c;
    ctx.font = `${sz}px Estedad, Vazirmatn, sans-serif`;
    ctx.textBaseline = 'top';
    const lines = ev.text.split('\n');
    lines.forEach((line, i) => ctx.fillText(line, px(ev.x), py(ev.y) + i * sz * 1.3));
  } else if (ev.t === 'img') {
    const img = new Image();
    img.onload = () => {
      ctx.drawImage(img, px(ev.x), py(ev.y), (ev.rw / 1000) * W, (ev.rh / 1000) * H);
    };
    img.src = ev.data;
  }
  ctx.restore();
}

// ─── component ───────────────────────────────────────────────────────────────

export function Whiteboard({ isHost, base }: Props) {
  const canvasRef = useRef<HTMLCanvasElement>(null);

  // drawing state
  const [tool, setTool] = useState<Tool>('pen');
  const [strokeColor, setStrokeColor] = useState('#1a1a2e');
  const [fillColor, setFillColor] = useState('transparent');
  const [width, setWidth] = useState(3);
  const [fontSize, setFontSize] = useState(40);
  const [bg, setBg] = useState<Bg>('plain');
  const [zoom, setZoom] = useState(1);

  // history for undo/redo (ImageData snapshots)
  const historyStack = useRef<ImageData[]>([]);
  const redoStack = useRef<ImageData[]>([]);

  // all completed events for undo sync
  const allEvents = useRef<BoardEvent[]>([]);
  const eventRedo = useRef<BoardEvent[]>([]);
  const pendingEvents = useRef<BoardEvent[]>([]);

  // drawing tracking
  const drawing = useRef(false);
  const shapeStart = useRef<[number, number] | null>(null);
  const currentPts = useRef<number[][]>([]);
  const previewSnap = useRef<ImageData | null>(null);

  // text input
  const [textPos, setTextPos] = useState<{ bx: number; by: number; px: number; py: number } | null>(null);
  const [textDraft, setTextDraft] = useState('');
  const textareaRef = useRef<HTMLTextAreaElement>(null);

  // ── helpers ────────────────────────────────────────────────────────────────

  const ctx = useCallback((): CanvasRenderingContext2D | null => {
    return canvasRef.current?.getContext('2d') ?? null;
  }, []);

  const W = BOARD_W;
  const H = BOARD_H;

  const draw = useCallback((ev: BoardEvent) => {
    const c = ctx();
    if (!c) return;
    drawEventOnCtx(c, ev, W, H);
  }, [ctx, W, H]);

  const saveHistory = useCallback(() => {
    const c = ctx();
    if (!c) return;
    const snap = c.getImageData(0, 0, W, H);
    historyStack.current.push(snap);
    if (historyStack.current.length > 80) historyStack.current.shift();
    redoStack.current = [];
  }, [ctx, W, H]);

  const canvasPoint = (e: React.PointerEvent<HTMLCanvasElement> | React.MouseEvent<HTMLCanvasElement>): [number, number] => {
    const rect = canvasRef.current!.getBoundingClientRect();
    return [
      Math.round(((e.clientX - rect.left) / rect.width) * 1000),
      Math.round(((e.clientY - rect.top) / rect.height) * 1000),
    ];
  };

  // commit an event: draw locally + queue for server + add to allEvents history
  const commit = useCallback((ev: BoardEvent, paint = true) => {
    if (paint) draw(ev);
    pendingEvents.current.push(ev);
    allEvents.current.push(ev);
    eventRedo.current = [];
  }, [draw]);

  const eraseColor = () => {
    const c = ctx();
    if (!c) return 'white';
    // detect if dark background or check bg - return white for now
    return '#ffffff';
  };

  // ── pointer events ─────────────────────────────────────────────────────────

  const onPointerDown = (e: React.PointerEvent<HTMLCanvasElement>) => {
    if (!isHost) return;
    const [bx, by] = canvasPoint(e);
    e.currentTarget.setPointerCapture(e.pointerId);

    if (tool === 'text') {
      const rect = canvasRef.current!.getBoundingClientRect();
      const px = e.clientX - rect.left;
      const py = e.clientY - rect.top;
      setTextPos({ bx, by, px, py });
      setTextDraft('');
      setTimeout(() => textareaRef.current?.focus(), 30);
      return;
    }

    saveHistory();
    previewSnap.current = ctx()?.getImageData(0, 0, W, H) ?? null;
    drawing.current = true;
    shapeStart.current = [bx, by];
    currentPts.current = [[bx, by]];
  };

  const onPointerMove = (e: React.PointerEvent<HTMLCanvasElement>) => {
    if (!drawing.current || !isHost) return;
    const [bx, by] = canvasPoint(e);

    if (tool === 'pen' || tool === 'eraser') {
      currentPts.current.push([bx, by]);
      const n = currentPts.current.length;
      if (n >= 2) {
        draw({
          t: 's',
          c: tool === 'eraser' ? eraseColor() : strokeColor,
          w: tool === 'eraser' ? 28 : width,
          p: [currentPts.current[n - 2], currentPts.current[n - 1]],
        });
      }
      return;
    }

    // shape preview — restore snapshot then draw preview
    const c = ctx();
    if (!c || !previewSnap.current || !shapeStart.current) return;
    c.putImageData(previewSnap.current, 0, 0);

    const [sx, sy] = shapeStart.current;
    const fc = fillColor === 'transparent' ? '' : fillColor;

    if (tool === 'line') {
      drawEventOnCtx(c, { t: 'line', c: strokeColor, w: width, x1: sx, y1: sy, x2: bx, y2: by }, W, H);
    } else if (tool === 'arrow') {
      drawEventOnCtx(c, { t: 'arrow', c: strokeColor, w: width, x1: sx, y1: sy, x2: bx, y2: by }, W, H);
    } else if (tool === 'rect') {
      drawEventOnCtx(c, { t: 'rect', c: strokeColor, fc, w: width, x: sx, y: sy, rw: bx - sx, rh: by - sy }, W, H);
    } else if (tool === 'circle') {
      const rx = Math.abs(bx - sx) / 2;
      const ry = Math.abs(by - sy) / 2;
      const cx2 = (sx + bx) / 2;
      const cy2 = (sy + by) / 2;
      drawEventOnCtx(c, { t: 'circle', c: strokeColor, fc, w: width, cx: cx2, cy: cy2, rx, ry }, W, H);
    } else if (tool === 'diamond') {
      const cx2 = (sx + bx) / 2;
      const cy2 = (sy + by) / 2;
      drawEventOnCtx(c, { t: 'diamond', c: strokeColor, fc, w: width, cx: cx2, cy: cy2, rw: Math.abs(bx - sx), rh: Math.abs(by - sy) }, W, H);
    }
  };

  const onPointerUp = (e: React.PointerEvent<HTMLCanvasElement>) => {
    if (!drawing.current || !isHost) return;
    drawing.current = false;
    const [bx, by] = canvasPoint(e);
    const [sx, sy] = shapeStart.current ?? [bx, by];
    const fc = fillColor === 'transparent' ? '' : fillColor;

    if (tool === 'pen' || tool === 'eraser') {
      if (currentPts.current.length >= 2) {
        commit({
          t: 's',
          c: tool === 'eraser' ? eraseColor() : strokeColor,
          w: tool === 'eraser' ? 28 : width,
          p: currentPts.current,
        }, false);
      }
    } else {
      const c = ctx();
      if (c && previewSnap.current) c.putImageData(previewSnap.current, 0, 0);
      if (tool === 'line') {
      commit({ t: 'line', c: strokeColor, w: width, x1: sx, y1: sy, x2: bx, y2: by });
      } else if (tool === 'arrow') {
      commit({ t: 'arrow', c: strokeColor, w: width, x1: sx, y1: sy, x2: bx, y2: by });
      } else if (tool === 'rect') {
      commit({ t: 'rect', c: strokeColor, fc, w: width, x: sx, y: sy, rw: bx - sx, rh: by - sy });
      } else if (tool === 'circle') {
      const rx = Math.abs(bx - sx) / 2;
      const ry = Math.abs(by - sy) / 2;
      const cx2 = (sx + bx) / 2;
      const cy2 = (sy + by) / 2;
      commit({ t: 'circle', c: strokeColor, fc, w: width, cx: cx2, cy: cy2, rx, ry });
      } else if (tool === 'diamond') {
      const cx2 = (sx + bx) / 2;
      const cy2 = (sy + by) / 2;
      commit({ t: 'diamond', c: strokeColor, fc, w: width, cx: cx2, cy: cy2, rw: Math.abs(bx - sx), rh: Math.abs(by - sy) });
      }
    }

    currentPts.current = [];
    shapeStart.current = null;
    previewSnap.current = null;
  };

  // ── text commit ────────────────────────────────────────────────────────────

  const commitText = () => {
    if (!textPos || !textDraft.trim()) { setTextPos(null); return; }
    const ev: BoardEvent = {
      t: 'txt',
      c: strokeColor,
      fc: fillColor,
      s: fontSize,
      x: textPos.bx,
      y: textPos.by,
      text: textDraft,
    };
    commit(ev);
    setTextPos(null);
    setTextDraft('');
  };

  // ── undo / redo ────────────────────────────────────────────────────────────

  const undo = useCallback(() => {
    const c = ctx();
    if (!c) return;
    const snap = historyStack.current.pop();
    if (!snap) return;
    const current = c.getImageData(0, 0, W, H);
    redoStack.current.push(current);
    c.putImageData(snap, 0, 0);

    // Remove last committed event and sync clear+redraw to server
    const removed = allEvents.current.pop();
    if (removed) eventRedo.current.push(removed);
    pendingEvents.current = [
      { t: 'clear' },
      ...allEvents.current,
    ];
  }, [ctx, W, H]);

  const redo = useCallback(() => {
    const c = ctx();
    if (!c) return;
    const snap = redoStack.current.pop();
    if (!snap) return;
    const current = c.getImageData(0, 0, W, H);
    historyStack.current.push(current);
    c.putImageData(snap, 0, 0);
    const restored = eventRedo.current.pop();
    if (restored) allEvents.current.push(restored);
    pendingEvents.current = [{ t: 'clear' }, ...allEvents.current];
  }, [ctx, W, H]);

  // ── clear ──────────────────────────────────────────────────────────────────

  const clearBoard = () => {
    if (!confirm('کل تخته پاک شود؟ (برای همه کاربران)')) return;
    saveHistory();
    draw({ t: 'clear' });
    allEvents.current = [];
    eventRedo.current = [];
    pendingEvents.current = [];
    void apiPost(`${base}/board/clear`, {});
  };

  // ── image paste ────────────────────────────────────────────────────────────

  useEffect(() => {
    if (!isHost) return;
    const handler = async (e: ClipboardEvent) => {
      const item = Array.from(e.clipboardData?.items ?? []).find((i) => i.type.startsWith('image/'));
      if (!item) return;
      const file = item.getAsFile();
      if (!file) return;
      const reader = new FileReader();
      reader.onload = (ev2) => {
        const data = ev2.target?.result as string;
        if (!data) return;
        saveHistory();
        commit({ t: 'img', x: 50, y: 50, rw: 500, rh: 350, data });
      };
      reader.readAsDataURL(file);
    };
    window.addEventListener('paste', handler);
    return () => window.removeEventListener('paste', handler);
  }, [isHost, commit, saveHistory]);

  // image upload via input
  const uploadImage = (file: File) => {
    const reader = new FileReader();
    reader.onload = (ev) => {
      const data = ev.target?.result as string;
      if (!data) return;
      saveHistory();
      commit({ t: 'img', x: 50, y: 50, rw: 500, rh: 350, data });
    };
    reader.readAsDataURL(file);
  };

  // ── keyboard shortcuts ─────────────────────────────────────────────────────

  useEffect(() => {
    if (!isHost) return;
    const handler = (e: KeyboardEvent) => {
      if ((e.ctrlKey || e.metaKey) && e.key === 'z') { e.preventDefault(); undo(); }
      if ((e.ctrlKey || e.metaKey) && e.key === 'y') { e.preventDefault(); redo(); }
      if (e.key === 'Escape') setTextPos(null);
      if (!e.ctrlKey && !e.metaKey && !e.altKey) {
        if (e.key === 'p') setTool('pen');
        if (e.key === 'e') setTool('eraser');
        if (e.key === 'l') setTool('line');
        if (e.key === 'a') setTool('arrow');
        if (e.key === 'r') setTool('rect');
        if (e.key === 'c') setTool('circle');
        if (e.key === 'd') setTool('diamond');
        if (e.key === 't') setTool('text');
      }
    };
    window.addEventListener('keydown', handler);
    return () => window.removeEventListener('keydown', handler);
  }, [isHost, undo, redo]);

  // ── server sync ────────────────────────────────────────────────────────────

  useEffect(() => {
    if (!isHost) return;
    const id = setInterval(() => {
      if (pendingEvents.current.length === 0) return;
      // Keep this exactly aligned with ClassRoomController's max:50 rule.
      // Undo/redo may enqueue a full replay, which is delivered in chunks.
      const events = pendingEvents.current.splice(0, 50);
      apiPost(`${base}/board`, { events }).catch(() => {
        pendingEvents.current.unshift(...events);
      });
    }, 350);
    return () => clearInterval(id);
  }, [base, isHost]);

  // ── zoom scroll ────────────────────────────────────────────────────────────

  const onWheel = (e: React.WheelEvent) => {
    if (!e.ctrlKey) return;
    e.preventDefault();
    setZoom((z) => Math.min(3, Math.max(0.3, z + (e.deltaY > 0 ? -0.1 : 0.1))));
  };

  // ── save PNG ───────────────────────────────────────────────────────────────

  const savePng = () => {
    const src = canvasRef.current;
    if (!src) return;
    const tmp = document.createElement('canvas');
    tmp.width = W; tmp.height = H;
    const c = tmp.getContext('2d')!;
    // white background
    c.fillStyle = '#fff';
    c.fillRect(0, 0, W, H);
    // grid/dots
    if (bg === 'grid') {
      const step = W / 40;
      c.strokeStyle = '#d1d5db'; c.lineWidth = 0.5;
      for (let x = 0; x <= W; x += step) { c.beginPath(); c.moveTo(x, 0); c.lineTo(x, H); c.stroke(); }
      for (let y = 0; y <= H; y += step) { c.beginPath(); c.moveTo(0, y); c.lineTo(W, y); c.stroke(); }
    } else if (bg === 'dots') {
      c.fillStyle = '#9ca3af';
      const step = W / 40;
      for (let x = step; x < W; x += step) {
        for (let y = step; y < H; y += step) {
          c.beginPath(); c.arc(x, y, 1.5, 0, Math.PI * 2); c.fill();
        }
      }
    }
    c.drawImage(src, 0, 0);
    const a = document.createElement('a');
    a.href = tmp.toDataURL('image/png');
    a.download = `whiteboard-${Date.now()}.png`;
    a.click();
  };

  // ── public draw (called by Room to replay server board events) ────────────

  // exposed via window global — Room.tsx calls it for incoming board events
  useEffect(() => {
    const globals = window as unknown as Record<string, unknown>;
    const drawIncoming = (ev: BoardEvent) => {
      const context = ctx();
      if (!context) return;
      drawEventOnCtx(context, ev, W, H);
    };
    globals.__wbDraw = drawIncoming;
    const pending = (globals.__wbPending as BoardEvent[] | undefined) ?? [];
    pending.splice(0).forEach(drawIncoming);
    return () => {
      if (globals.__wbDraw === drawIncoming) delete globals.__wbDraw;
    };
  }, [ctx, W, H]);

  // ─── render ───────────────────────────────────────────────────────────────

  const tools: { id: Tool; icon: typeof Icon.Pencil; label: string; key: string }[] = [
    { id: 'pen', icon: Icon.Pencil, label: 'قلم', key: 'P' },
    { id: 'eraser', icon: Icon.Eraser, label: 'پاک‌کن', key: 'E' },
    { id: 'line', icon: Icon.Line, label: 'خط', key: 'L' },
    { id: 'arrow', icon: Icon.Arrow, label: 'فلش', key: 'A' },
    { id: 'rect', icon: Icon.Square, label: 'مستطیل', key: 'R' },
    { id: 'circle', icon: Icon.Circle, label: 'بیضی', key: 'C' },
    { id: 'diamond', icon: Icon.Diamond, label: 'لوزی', key: 'D' },
    { id: 'text', icon: Icon.Text, label: 'متن', key: 'T' },
  ];

  const bgStyles: Record<Bg, React.CSSProperties> = {
    plain: { background: 'var(--wb-bg, #f8fafc)' },
    grid: {
      backgroundImage: 'linear-gradient(rgba(0,0,0,.06) 1px,transparent 1px),linear-gradient(90deg,rgba(0,0,0,.06) 1px,transparent 1px)',
      backgroundSize: '35px 35px',
      backgroundColor: 'var(--wb-bg, #f8fafc)',
    },
    dots: {
      backgroundImage: 'radial-gradient(circle,rgba(0,0,0,.15) 1.5px,transparent 1.5px)',
      backgroundSize: '35px 35px',
      backgroundColor: 'var(--wb-bg, #f8fafc)',
    },
  };

  return (
    <div className="wb" style={{ display: 'flex', flexDirection: 'column', gap: 0, height: '100%' }}>

      {/* ── toolbar ── */}
      {isHost && (
        <div className="wb-toolbar" style={{
          display: 'flex',
          flexWrap: 'wrap',
          alignItems: 'center',
          gap: '0.35rem',
          padding: '0.5rem 0.75rem',
          background: 'var(--color-surface)',
          borderBottom: '1px solid var(--color-border)',
          fontSize: '0.8rem',
        }}>

          {/* tools */}
          <div style={{ display: 'flex', gap: '2px', background: 'var(--color-bg-subtle)', borderRadius: 8, padding: '3px' }}>
            {tools.map((t) => {
              const ToolIcon = t.icon;
              return (
                <button
                  key={t.id}
                  type="button"
                  title={`${t.label} (${t.key})`}
                  onClick={() => { setTool(t.id); setTextPos(null); }}
                  style={{
                    width: 34, height: 34, border: 'none', borderRadius: 6, cursor: 'pointer',
                    background: tool === t.id ? 'var(--color-primary)' : 'transparent',
                    color: tool === t.id ? '#fff' : 'var(--color-text)',
                    display: 'flex', alignItems: 'center', justifyContent: 'center',
                    fontSize: '1rem', transition: 'background .15s',
                  }}
                >
                  <ToolIcon size={17} />
                </button>
              );
            })}
          </div>

          <div style={{ width: 1, height: 28, background: 'var(--color-border)', margin: '0 2px' }} />

          {/* stroke color */}
          <div style={{ display: 'flex', flexWrap: 'wrap', gap: 3, alignItems: 'center' }}>
            {STROKE_COLORS.map((c) => (
              <button
                key={c}
                type="button"
                onClick={() => setStrokeColor(c)}
                title={c}
                style={{
                  width: 22, height: 22, borderRadius: '50%', border: strokeColor === c ? '2px solid var(--color-primary)' : '1.5px solid #ccc',
                  background: c, cursor: 'pointer', padding: 0,
                  boxShadow: strokeColor === c ? '0 0 0 2px var(--color-primary-soft)' : 'none',
                }}
              />
            ))}
            <label title="رنگ سفارشی" style={{ cursor: 'pointer', position: 'relative', width: 22, height: 22 }}>
              <span style={{ position: 'absolute', inset: 0, borderRadius: '50%', border: '1.5px dashed #888', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '0.7rem' }}>+</span>
              <input type="color" value={strokeColor} onChange={(e) => setStrokeColor(e.target.value)} style={{ opacity: 0, position: 'absolute', inset: 0, width: '100%', height: '100%', cursor: 'pointer' }} />
            </label>
          </div>

          <div style={{ width: 1, height: 28, background: 'var(--color-border)', margin: '0 2px' }} />

          {/* fill color */}
          <div style={{ display: 'flex', gap: 3, alignItems: 'center' }}>
            <span style={{ fontSize: '0.72rem', color: 'var(--color-text-muted)' }}>پر:</span>
            <button
              type="button"
              onClick={() => setFillColor('transparent')}
              style={{ width: 22, height: 22, borderRadius: '50%', border: fillColor === 'transparent' ? '2px solid var(--color-primary)' : '1.5px solid #ccc', background: 'transparent', cursor: 'pointer', backgroundImage: 'linear-gradient(45deg,#ccc 25%,transparent 25%,transparent 75%,#ccc 75%)', backgroundSize: '8px 8px' }}
              title="بدون پر"
            />
            {['#fff', '#fef08a', '#bbf7d0', '#bfdbfe', '#fecaca'].map((c) => (
              <button key={c} type="button" onClick={() => setFillColor(c)}
                style={{ width: 22, height: 22, borderRadius: '50%', border: fillColor === c ? '2px solid var(--color-primary)' : '1.5px solid #ccc', background: c, cursor: 'pointer' }} />
            ))}
            <label title="رنگ پر سفارشی" style={{ cursor: 'pointer', position: 'relative', width: 22, height: 22 }}>
              <span style={{ position: 'absolute', inset: 0, borderRadius: '50%', border: '1.5px dashed #888', display: 'flex', alignItems: 'center', justifyContent: 'center', fontSize: '0.7rem' }}>+</span>
              <input type="color" value={fillColor === 'transparent' ? '#ffffff' : fillColor} onChange={(e) => setFillColor(e.target.value)} style={{ opacity: 0, position: 'absolute', inset: 0, width: '100%', height: '100%', cursor: 'pointer' }} />
            </label>
          </div>

          <div style={{ width: 1, height: 28, background: 'var(--color-border)', margin: '0 2px' }} />

          {/* stroke width */}
          <div style={{ display: 'flex', gap: 3, alignItems: 'center' }}>
            {WIDTHS.map((w) => (
              <button
                key={w}
                type="button"
                onClick={() => setWidth(w)}
                title={`ضخامت ${w}`}
                style={{
                  width: 30, height: 30, border: width === w ? '2px solid var(--color-primary)' : '1px solid var(--color-border)',
                  borderRadius: 6, background: width === w ? 'var(--color-primary-soft)' : 'transparent',
                  cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center',
                }}
              >
                <div style={{ width: Math.max(4, w * 1.8), height: Math.max(4, w * 1.8), borderRadius: '50%', background: 'var(--color-text)' }} />
              </button>
            ))}
          </div>

          {tool === 'text' && (
            <>
              <div style={{ width: 1, height: 28, background: 'var(--color-border)', margin: '0 2px' }} />
              <div style={{ display: 'flex', gap: '0.3rem', alignItems: 'center' }}>
                <span style={{ fontSize: '0.72rem', color: 'var(--color-text-muted)' }}>اندازه:</span>
                {[20, 30, 40, 56, 80].map((s) => (
                  <button key={s} type="button" onClick={() => setFontSize(s)}
                    style={{ width: 28, height: 28, border: fontSize === s ? '2px solid var(--color-primary)' : '1px solid var(--color-border)', borderRadius: 6, background: fontSize === s ? 'var(--color-primary-soft)' : 'transparent', cursor: 'pointer', fontSize: '0.7rem', fontWeight: 700, color: fontSize === s ? 'var(--color-primary)' : 'var(--color-text)' }}>
                    {s}
                  </button>
                ))}
              </div>
            </>
          )}

          <div style={{ width: 1, height: 28, background: 'var(--color-border)', margin: '0 2px' }} />

          {/* background */}
          <div style={{ display: 'flex', gap: 2, alignItems: 'center' }}>
            <span style={{ fontSize: '0.72rem', color: 'var(--color-text-muted)' }}>پس‌زمینه:</span>
            {(['plain', 'grid', 'dots'] as Bg[]).map((b) => (
              <button key={b} type="button" onClick={() => setBg(b)}
                style={{ padding: '0.2rem 0.5rem', border: bg === b ? '1.5px solid var(--color-primary)' : '1px solid var(--color-border)', borderRadius: 6, background: bg === b ? 'var(--color-primary-soft)' : 'transparent', cursor: 'pointer', fontSize: '0.72rem', color: bg === b ? 'var(--color-primary)' : 'var(--color-text)' }}>
                {b === 'plain' ? 'ساده' : b === 'grid' ? 'شطرنجی' : 'نقطه‌ای'}
              </button>
            ))}
          </div>

          <div style={{ flex: 1 }} />

          {/* zoom */}
          <div style={{ display: 'flex', gap: 2, alignItems: 'center' }}>
            <button type="button" onClick={() => setZoom((z) => Math.max(0.3, z - 0.1))} style={iconBtn}><Icon.Minus size={16} /></button>
            <span style={{ fontSize: '0.75rem', minWidth: 36, textAlign: 'center', color: 'var(--color-text-muted)' }}>{Math.round(zoom * 100)}٪</span>
            <button type="button" onClick={() => setZoom((z) => Math.min(3, z + 0.1))} style={iconBtn}><Icon.Plus size={16} /></button>
            <button type="button" onClick={() => setZoom(1)} style={{ ...iconBtn, fontSize: '0.68rem' }}>ریست</button>
          </div>

          <div style={{ width: 1, height: 28, background: 'var(--color-border)', margin: '0 2px' }} />

          {/* actions */}
          <button type="button" onClick={undo} title="واگرد (Ctrl+Z)" style={iconBtn}><Icon.Undo size={16} /></button>
          <button type="button" onClick={redo} title="بازانجام (Ctrl+Y)" style={iconBtn}><Icon.Redo size={16} /></button>
          <button type="button" onClick={clearBoard} title="پاک کردن کامل" style={{ ...iconBtn, color: '#dc2626' }}><Icon.Trash size={16} /></button>
          <label title="درج تصویر" style={{ ...iconBtn, cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
            <Icon.Image size={16} />
            <input type="file" accept="image/*" hidden onChange={(e) => e.target.files?.[0] && uploadImage(e.target.files[0])} />
          </label>
          <button type="button" onClick={savePng} title="دانلود PNG" style={iconBtn}><Icon.Download size={16} /></button>
        </div>
      )}

      {/* ── canvas area ── */}
      <div
        className="wb-canvas-wrap"
        style={{ flex: 1, overflow: 'auto', position: 'relative', ...bgStyles[bg] }}
        onWheel={onWheel}
      >
        <div style={{
          transform: `scale(${zoom})`,
          transformOrigin: 'top center',
          display: 'inline-block',
          position: 'relative',
          margin: zoom < 1 ? `0 auto ${(1 - zoom) * -BOARD_H * 0.5}px` : undefined,
        }}>
          <canvas
            ref={canvasRef}
            width={BOARD_W}
            height={BOARD_H}
            style={{
              display: 'block',
              cursor: isHost ? (tool === 'pen' ? 'crosshair' : tool === 'eraser' ? 'cell' : tool === 'text' ? 'text' : 'crosshair') : 'default',
              maxWidth: '100%',
            }}
            onPointerDown={onPointerDown}
            onPointerMove={onPointerMove}
            onPointerUp={onPointerUp}
            onPointerLeave={onPointerUp}
          />

          {/* floating text input */}
          {textPos && isHost && (
            <div style={{ position: 'absolute', left: textPos.px, top: textPos.py, zIndex: 10 }}>
              <textarea
                ref={textareaRef}
                value={textDraft}
                onChange={(e) => setTextDraft(e.target.value)}
                onKeyDown={(e) => { if (e.key === 'Enter' && !e.shiftKey) { e.preventDefault(); commitText(); } if (e.key === 'Escape') setTextPos(null); }}
                onBlur={commitText}
                rows={3}
                placeholder="متن را وارد کنید... (Enter برای تأیید)"
                style={{
                  minWidth: 160,
                  fontSize: Math.round((fontSize / 1000) * BOARD_H * zoom) + 'px',
                  fontFamily: 'Estedad, Vazirmatn, sans-serif',
                  color: strokeColor,
                  background: 'rgba(255,255,255,0.92)',
                  border: '2px dashed var(--color-primary)',
                  borderRadius: 6,
                  padding: '0.3rem 0.5rem',
                  resize: 'both',
                  outline: 'none',
                  lineHeight: 1.4,
                  direction: 'rtl',
                  boxShadow: '0 4px 16px rgba(0,0,0,.2)',
                }}
              />
            </div>
          )}
        </div>
      </div>

      {/* keyboard hint bar */}
      {isHost && (
        <div className="wb-hints" style={{ padding: '0.25rem 0.75rem', fontSize: '0.7rem', color: 'var(--color-text-muted)', background: 'var(--color-surface)', borderTop: '1px solid var(--color-border)', display: 'flex', gap: '1rem', flexWrap: 'wrap' }}>
          <span>P=قلم · E=پاک‌کن · L=خط · A=فلش · R=مستطیل · C=بیضی · D=لوزی · T=متن</span>
          <span>Ctrl+Z=واگرد · Ctrl+Y=بازانجام · Ctrl+Scroll=زوم · Ctrl+V=درج تصویر</span>
        </div>
      )}
    </div>
  );
}

const iconBtn: React.CSSProperties = {
  width: 30, height: 30, border: '1px solid var(--color-border)', borderRadius: 6,
  background: 'transparent', cursor: 'pointer', display: 'flex', alignItems: 'center',
  justifyContent: 'center', fontSize: '0.95rem', color: 'var(--color-text)',
};

// exported helper for Room.tsx to replay server board events
export function drawBoardEvent(ev: BoardEvent): void {
  const globals = window as unknown as Record<string, unknown>;
  const fn = globals.__wbDraw as ((event: BoardEvent) => void) | undefined;
  if (fn) fn(ev);
  else {
    const pending = (globals.__wbPending as BoardEvent[] | undefined) ?? [];
    pending.push(ev);
    globals.__wbPending = pending.slice(-1200);
  }
}
