/**
 * ChartMagnifier
 *
 * 차트 위에 떠 있는 돋보기(루페) 오버레이.
 * - 헤더 드래그로 이동
 * - 8방향 핸들로 리사이즈
 * - 내부 canvas 에 차트 픽셀을 확대 복사 (rAF 루프)
 * - X/Y 동시 줌 배율 조절
 * - Y축 전용 줌(Y-Zoom): 수직 방향만 추가 확대 → 미세한 Y값 변화도 파악 가능
 * - LIVE 지시등: 캔버스 픽셀 변화 감지 시 점멸
 */
import React, { useRef, useEffect, useCallback, useState } from 'react';

interface ChartMagnifierProps {
  /** LWC canvas 들이 들어있는 chart-container div */
  containerRef: React.RefObject<HTMLDivElement>;
  /** position:relative 부모 (tv-chart-wrap) — 위치 기준점 */
  wrapperRef:   React.RefObject<HTMLDivElement>;
  enabled:      boolean;
  onClose:      () => void;
}

const HEADER_H  = 30;
const MIN_W     = 180;
const MIN_H     = 120 + HEADER_H;
const INIT_W    = 320;
const INIT_H    = 240 + HEADER_H;

const XY_STEPS: number[] = [1.5, 2, 2.5, 3, 4, 5, 6, 8];
const Y_STEPS:  number[] = [1, 1.5, 2, 3, 5, 8, 12];

type ResizeDir = 'nw'|'n'|'ne'|'e'|'se'|'s'|'sw'|'w';

const HANDLES: { id: ResizeDir; cursor: string; style: React.CSSProperties }[] = [
  { id:'nw', cursor:'nw-resize', style:{ top:-5,    left:-5                                              } },
  { id:'n',  cursor:'n-resize',  style:{ top:-5,    left:'50%', transform:'translateX(-50%)'             } },
  { id:'ne', cursor:'ne-resize', style:{ top:-5,    right:-5                                             } },
  { id:'e',  cursor:'e-resize',  style:{ top:'50%', right:-5,  transform:'translateY(-50%)'             } },
  { id:'se', cursor:'se-resize', style:{ bottom:-5, right:-5                                             } },
  { id:'s',  cursor:'s-resize',  style:{ bottom:-5, left:'50%',transform:'translateX(-50%)'             } },
  { id:'sw', cursor:'sw-resize', style:{ bottom:-5, left:-5                                              } },
  { id:'w',  cursor:'w-resize',  style:{ top:'50%', left:-5,   transform:'translateY(-50%)'             } },
];

const ChartMagnifier: React.FC<ChartMagnifierProps> = ({
  containerRef, wrapperRef, enabled, onClose,
}) => {
  const canvasRef   = useRef<HTMLCanvasElement>(null);
  const tempCvRef   = useRef<HTMLCanvasElement | null>(null); // Y-Zoom 용 임시 캔버스
  const rafRef      = useRef<number>(0);

  // ── position / size / xy-zoom / y-zoom ────────────────────────────────────
  // position: fixed 기준 — 뷰포트 좌표 (wrapper 밖으로도 이동 가능)
  const initPos = useCallback(() => {
    const wr = wrapperRef.current?.getBoundingClientRect();
    return wr ? { x: wr.left + 40, y: wr.top + 40 } : { x: 80, y: 80 };
  }, [wrapperRef]);

  const [pos,    setPos]    = useState(() => ({ x: 80, y: 80 }));
  const [size,   setSize]   = useState({ w: INIT_W, h: INIT_H });
  const [xyZoom, setXyZoom] = useState(2.5);
  const [yZoom,  setYZoom]  = useState(1);
  /** LIVE 지시등: true = 직전 프레임 대비 픽셀 변화 있음 */
  const [isLive, setIsLive] = useState(false);

  const posRef    = useRef({ x: 80, y: 80 });
  const sizeRef   = useRef({ w: INIT_W, h: INIT_H });
  const xyZoomRef = useRef(2.5);
  const yZoomRef  = useRef(1);

  // 픽셀 변화 감지용: 이전 프레임의 중앙 수직 라인 픽셀 데이터
  const prevRowRef  = useRef<Uint8ClampedArray | null>(null);
  const liveTimerRef = useRef<ReturnType<typeof setTimeout> | null>(null);

  // ── rAF: 차트 캔버스 픽셀을 확대하여 magnifier canvas 에 복사 ──────────────
  const renderFrame = useCallback(() => {
    const canvas    = canvasRef.current;
    const container = containerRef.current;
    const wrapper   = wrapperRef.current;
    if (!canvas || !container || !wrapper) return;
    const ctx = canvas.getContext('2d', { willReadFrequently: true });
    if (!ctx) return;

    const { x, y }  = posRef.current;
    const { w, h }  = sizeRef.current;
    const zm        = xyZoomRef.current;
    const yz        = yZoomRef.current;
    const canvasH   = h - HEADER_H;
    const dpr       = window.devicePixelRatio || 1;

    // canvas 해상도 유지
    const tW = Math.round(w       * dpr);
    const tH = Math.round(canvasH * dpr);
    if (canvas.width !== tW || canvas.height !== tH) {
      canvas.width  = tW;
      canvas.height = tH;
      canvas.style.width  = w       + 'px';
      canvas.style.height = canvasH + 'px';
    }

    ctx.clearRect(0, 0, canvas.width, canvas.height);

    // 샘플링 중심 — position:fixed 이므로 pos가 이미 뷰포트 절대 좌표
    const centerX = x + w / 2;
    const centerY = y + HEADER_H + canvasH / 2;

    // 원본 영역 (xy-zoom 적용)
    const srcW = w       / zm;
    const srcH = canvasH / zm;
    const srcL = centerX - srcW / 2;
    const srcT = centerY - srcH / 2;

    // position:fixed → pos 자체가 뷰포트 좌표이므로 wrapper 오프셋 불필요
    const ssrcL = srcL;
    const ssrcT = srcT;

    // LWC 캔버스 레이어 순서대로 복사
    const layers = Array.from(container.querySelectorAll<HTMLCanvasElement>('canvas'));
    for (const lc of layers) {
      const cRect = lc.getBoundingClientRect();
      const csX   = (ssrcL - cRect.left) * dpr;
      const csY   = (ssrcT - cRect.top ) * dpr;
      const csW   = srcW * dpr;
      const csH   = srcH * dpr;
      const x0 = Math.max(0,        csX);
      const y0 = Math.max(0,        csY);
      const x1 = Math.min(lc.width,  csX + csW);
      const y1 = Math.min(lc.height, csY + csH);
      if (x1 <= x0 || y1 <= y0) continue;
      const dstX = ((x0 - csX) / csW) * canvas.width;
      const dstY = ((y0 - csY) / csH) * canvas.height;
      const dstW = ((x1 - x0)  / csW) * canvas.width;
      const dstH = ((y1 - y0)  / csH) * canvas.height;
      try { ctx.drawImage(lc, x0, y0, x1-x0, y1-y0, dstX, dstY, dstW, dstH); }
      catch { /* cross-origin 무시 */ }
    }

    // ── Y-Zoom: 수직 방향 추가 확대 ───────────────────────────────────────────
    // Y축의 미세한 움직임을 파악할 수 있도록 중앙 기준으로 수직만 재확대
    if (yz > 1) {
      // 임시 캔버스 초기화 (한 번만 생성)
      let tmp = tempCvRef.current;
      if (!tmp || tmp.width !== canvas.width || tmp.height !== canvas.height) {
        tmp = document.createElement('canvas');
        tmp.width  = canvas.width;
        tmp.height = canvas.height;
        tempCvRef.current = tmp;
      }
      const tCtx = tmp.getContext('2d')!;
      tCtx.clearRect(0, 0, tmp.width, tmp.height);
      tCtx.drawImage(canvas, 0, 0);

      // 중앙 ±(1/yz) 영역만 잘라 전체 높이로 늘림
      const srcYH = Math.round(canvas.height / yz);
      const srcYT = Math.round((canvas.height - srcYH) / 2);
      ctx.clearRect(0, 0, canvas.width, canvas.height);
      ctx.drawImage(tmp, 0, srcYT, canvas.width, srcYH, 0, 0, canvas.width, canvas.height);

      // Y-Zoom 배율 워터마크
      ctx.fillStyle = 'rgba(255,200,50,0.6)';
      ctx.font      = `bold ${Math.round(10 * dpr)}px monospace`;
      ctx.fillText(`Y×${yz}`, 4 * dpr, (canvasH - 4) * dpr);
    }

    // ── 테두리 ─────────────────────────────────────────────────────────────────
    ctx.strokeStyle = 'rgba(255, 200, 50, 0.35)';
    ctx.lineWidth   = 1;
    ctx.strokeRect(0.5, 0.5, canvas.width - 1, canvas.height - 1);

    // ── LIVE 감지: 중앙 세로 라인 픽셀 샘플링으로 변화 감지 ─────────────────
    try {
      const midX   = Math.floor(canvas.width  / 2);
      const sample = ctx.getImageData(midX, 0, 1, canvas.height).data;
      const prev   = prevRowRef.current;
      if (prev && prev.length === sample.length) {
        let diff = 0;
        for (let i = 0; i < sample.length; i += 4) {
          diff += Math.abs(sample[i]   - prev[i]);
          diff += Math.abs(sample[i+1] - prev[i+1]);
          diff += Math.abs(sample[i+2] - prev[i+2]);
        }
        if (diff > 30) {        // 임계값: 미세 잡음 무시
          setIsLive(true);
          if (liveTimerRef.current) clearTimeout(liveTimerRef.current);
          liveTimerRef.current = setTimeout(() => setIsLive(false), 600);
        }
      }
      prevRowRef.current = new Uint8ClampedArray(sample);
    } catch { /* getImageData 실패 무시 */ }
  }, [containerRef, wrapperRef]);

  // enabled 시 wrapper 위치 기준으로 초기 위치 설정
  useEffect(() => {
    if (!enabled) return;
    const p = initPos();
    posRef.current = p;
    setPos(p);
  }, [enabled, initPos]);

  // rAF 루프
  useEffect(() => {
    if (!enabled) { cancelAnimationFrame(rafRef.current); return; }
    const loop = () => { renderFrame(); rafRef.current = requestAnimationFrame(loop); };
    rafRef.current = requestAnimationFrame(loop);
    return () => cancelAnimationFrame(rafRef.current);
  }, [enabled, renderFrame]);

  // ── 드래그 (헤더) ─────────────────────────────────────────────────────────
  const dragStart = useRef<{ mx: number; my: number; px: number; py: number } | null>(null);

  const onHeaderDown = useCallback((e: React.PointerEvent) => {
    e.preventDefault();
    dragStart.current = { mx: e.clientX, my: e.clientY, px: posRef.current.x, py: posRef.current.y };
    (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
  }, []);

  const onHeaderMove = useCallback((e: React.PointerEvent) => {
    if (!dragStart.current) return;
    const dx = e.clientX - dragStart.current.mx;
    const dy = e.clientY - dragStart.current.my;
    const vw = window.innerWidth;
    const vh = window.innerHeight;
    const { w, h } = sizeRef.current;
    // 헤더가 항상 화면 안에 있도록 최소 마진만 유지 (위아래 자유 이동)
    const np = {
      x: Math.max(-(w - 60), Math.min(vw - 60,    dragStart.current.px + dx)),
      y: Math.max(-(h - HEADER_H), Math.min(vh - HEADER_H, dragStart.current.py + dy)),
    };
    posRef.current = np;
    setPos(np);
  }, []);

  const onHeaderUp = useCallback(() => { dragStart.current = null; }, []);

  // ── 리사이즈 핸들 ──────────────────────────────────────────────────────────
  const resizeStart = useRef<{
    dir: ResizeDir; mx: number; my: number;
    px: number; py: number; pw: number; ph: number;
  } | null>(null);

  const onHandleDown = useCallback((e: React.PointerEvent, dir: ResizeDir) => {
    e.preventDefault(); e.stopPropagation();
    resizeStart.current = {
      dir, mx: e.clientX, my: e.clientY,
      px: posRef.current.x,  py: posRef.current.y,
      pw: sizeRef.current.w, ph: sizeRef.current.h,
    };
    (e.currentTarget as HTMLElement).setPointerCapture(e.pointerId);
  }, []);

  const onHandleMove = useCallback((e: React.PointerEvent) => {
    const rs = resizeStart.current; if (!rs) return;
    const { dir, mx, my, px, py, pw, ph } = rs;
    const dx = e.clientX - mx; const dy = e.clientY - my;
    let nx = px, ny = py, nw = pw, nh = ph;
    if (dir.includes('e')) { nw = Math.max(MIN_W, pw + dx); }
    if (dir.includes('w')) { nw = Math.max(MIN_W, pw - dx); nx = px + pw - nw; }
    if (dir.includes('s')) { nh = Math.max(MIN_H, ph + dy); }
    if (dir.includes('n')) { nh = Math.max(MIN_H, ph - dy); ny = py + ph - nh; }
    // 리사이즈 시 뷰포트 경계 안에서만 클램프 (헤더 마진 확보)
    nx = Math.max(-(nw - 60), Math.min(window.innerWidth  - 60, nx));
    ny = Math.max(-(nh - HEADER_H), Math.min(window.innerHeight - HEADER_H, ny));
    posRef.current  = { x: nx, y: ny };
    sizeRef.current = { w: nw, h: nh };
    setPos ({ x: nx, y: ny });
    setSize({ w: nw, h: nh });
  }, [wrapperRef]);

  const onHandleUp = useCallback(() => { resizeStart.current = null; }, []);

  // ── XY-Zoom 조절 ──────────────────────────────────────────────────────────
  const adjustXyZoom = useCallback((delta: number) => {
    const cur = xyZoomRef.current;
    const idx = XY_STEPS.findIndex(s => s >= cur - 0.01);
    const next = XY_STEPS[Math.max(0, Math.min(XY_STEPS.length - 1, idx + delta))];
    xyZoomRef.current = next; setXyZoom(next);
  }, []);

  // ── Y-Zoom 조절 ───────────────────────────────────────────────────────────
  const adjustYZoom = useCallback((delta: number) => {
    const cur = yZoomRef.current;
    const idx = Y_STEPS.findIndex(s => s >= cur - 0.01);
    const next = Y_STEPS[Math.max(0, Math.min(Y_STEPS.length - 1, idx + delta))];
    yZoomRef.current = next; setYZoom(next);
  }, []);

  if (!enabled) return null;

  return (
    <div
      className="chart-magnifier"
      style={{ left: pos.x, top: pos.y, width: size.w, height: size.h }}
      onPointerDown={onHeaderDown}
      onPointerMove={onHeaderMove}
      onPointerUp={onHeaderUp}
    >
      {/* ── 헤더 ──────────────────────────────────────────────── */}
      <div
        className="chart-magnifier-header"
      >
        {/* 아이콘 + 제목 */}
        <svg className="chart-magnifier-icon" width="13" height="13" viewBox="0 0 13 13"
          fill="none" stroke="currentColor" strokeWidth="1.8">
          <circle cx="5.5" cy="5.5" r="4"/>
          <line x1="8.8" y1="8.8" x2="12.5" y2="12.5"/>
          <line x1="5.5" y1="3.5" x2="5.5" y2="7.5"/>
          <line x1="3.5" y1="5.5" x2="7.5" y2="5.5"/>
        </svg>
        <span className="chart-magnifier-title">확대경</span>

        {/* LIVE 지시등 */}
        <span
          className={`chart-magnifier-live${isLive ? ' live-on' : ''}`}
          title="캔버스 실시간 갱신 감지"
        >LIVE</span>

        {/* XY Zoom 컨트롤 */}
        <div className="chart-magnifier-zoom" title="X·Y 동시 확대">
          <button className="chart-magnifier-zoom-btn" onPointerDown={e => e.stopPropagation()}
            onClick={() => adjustXyZoom(-1)}>−</button>
          <span className="chart-magnifier-zoom-val">{xyZoom}×</span>
          <button className="chart-magnifier-zoom-btn" onPointerDown={e => e.stopPropagation()}
            onClick={() => adjustXyZoom(1)}>+</button>
        </div>

        {/* Y-Zoom 컨트롤 */}
        <div className="chart-magnifier-yzoom" title="Y축 전용 확대 (미세 변화 감지용)">
          <button className="chart-magnifier-zoom-btn chart-magnifier-yzoom-btn"
            onPointerDown={e => e.stopPropagation()} onClick={() => adjustYZoom(-1)}>−</button>
          <span className="chart-magnifier-zoom-val">
            <span style={{ fontSize: 9, opacity: 0.7 }}>Y</span>{yZoom}×
          </span>
          <button className="chart-magnifier-zoom-btn chart-magnifier-yzoom-btn"
            onPointerDown={e => e.stopPropagation()} onClick={() => adjustYZoom(1)}>+</button>
        </div>

        {/* 닫기 */}
        <button className="chart-magnifier-close" onPointerDown={e => e.stopPropagation()}
          onClick={onClose} title="닫기">×</button>
      </div>

      {/* ── 확대 canvas ───────────────────────────────────────── */}
      <canvas ref={canvasRef} className="chart-magnifier-canvas" />

      {/* ── 리사이즈 핸들 (8방향) ─────────────────────────────── */}
      {HANDLES.map(h => (
        <div key={h.id} className="chart-magnifier-handle"
          style={{ ...h.style, cursor: h.cursor, position: 'absolute', width: 10, height: 10 }}
          onPointerDown={e => onHandleDown(e, h.id)}
          onPointerMove={onHandleMove}
          onPointerUp={onHandleUp}
        />
      ))}
    </div>
  );
};

export default ChartMagnifier;
