import { useEffect, useRef } from 'react';

const SVG_NS = 'http://www.w3.org/2000/svg';

interface Point {
  x: number;
  y: number;
}

interface Box {
  left: number;
  top: number;
  right: number;
  bottom: number;
  cx: number;
  cy: number;
  width: number;
  height: number;
}

function relBox(el: Element, parent: Element): Box {
  const r = el.getBoundingClientRect();
  const p = parent.getBoundingClientRect();
  return {
    left: r.left - p.left,
    top: r.top - p.top,
    right: r.right - p.left,
    bottom: r.bottom - p.top,
    cx: r.left - p.left + r.width / 2,
    cy: r.top - p.top + r.height / 2,
    width: r.width,
    height: r.height,
  };
}

function snap(n: number) {
  return Math.round(n * 100) / 100;
}

/** 참고 레이아웃: 카드 변 부메랑(둔각 다이아) 끝점 */
function appendBoomerangMarker(
  group: SVGGElement,
  at: Point,
  side: 'left' | 'right',
  fill: string,
) {
  const dir = side === 'right' ? 1 : -1;
  const tipLen = 7;
  const wing = 4.5;
  const pts = [
    [at.x + dir * tipLen, at.y],
    [at.x + dir * 1.5, at.y - wing],
    [at.x - dir * 2.5, at.y],
    [at.x + dir * 1.5, at.y + wing],
  ]
    .map(([x, y]) => `${snap(x)},${snap(y)}`)
    .join(' ');
  const poly = document.createElementNS(SVG_NS, 'polygon');
  poly.setAttribute('points', pts);
  poly.setAttribute('fill', fill);
  group.appendChild(poly);
}

/** 참고 레이아웃: 허브 박스 연결부 V형 부메랑 (박스 안쪽 방향) */
function appendHubBoomerangMarker(
  group: SVGGElement,
  at: Point,
  dir: 'up' | 'down',
  fill: string,
) {
  const sign = dir === 'down' ? 1 : -1;
  const tipLen = 5.5;
  const wing = 5;
  const base = 3;
  const pts = [
    [at.x, at.y + sign * tipLen],
    [at.x - wing, at.y - sign * base],
    [at.x + wing, at.y - sign * base],
  ]
    .map(([x, y]) => `${snap(x)},${snap(y)}`)
    .join(' ');
  const poly = document.createElementNS(SVG_NS, 'polygon');
  poly.setAttribute('points', pts);
  poly.setAttribute('fill', fill);
  group.appendChild(poly);
}

function pathLength(a: Point, b: Point) {
  return Math.hypot(b.x - a.x, b.y - a.y);
}

/** snap·배율 100%에서도 0px 세그먼트가 되지 않도록 보정 */
function normalizePathPoints(points: Point[], minSeg = 1.25): Point[] {
  if (points.length < 2) return points;
  const out: Point[] = [{ ...points[0] }];
  for (let i = 1; i < points.length; i++) {
    const prev = out[out.length - 1];
    const cur = { ...points[i] };
    const len = pathLength(prev, cur);
    if (len < minSeg) {
      const ref =
        i + 1 < points.length
          ? points[i + 1]
          : i > 0
            ? points[i - 1]
            : cur;
      let dx = cur.x - prev.x;
      let dy = cur.y - prev.y;
      if (len < 0.01) {
        dx = ref.x - prev.x;
        dy = ref.y - prev.y;
      }
      const d = Math.hypot(dx, dy) || 1;
      out.push({ x: prev.x + (dx / d) * minSeg, y: prev.y + (dy / d) * minSeg });
    } else {
      out.push(cur);
    }
  }
  return out;
}

/** reference 상·하: 허브 테두리 ↔ 다이아 꼭짓점 (wrap clamp·stub 보정 없음) */
function referenceVerticalLine(cx: number, yHub: number, yDiamond: number): Point[] {
  if (Math.abs(yDiamond - yHub) < 0.5) return [];
  return [
    { x: cx, y: yHub },
    { x: cx, y: yDiamond },
  ];
}

function pointsToPath(points: Point[]) {
  return points
    .map((p, i) => `${i === 0 ? 'M' : 'L'}${snap(p.x).toFixed(1)} ${snap(p.y).toFixed(1)}`)
    .join(' ');
}

function diamondVertex(cx: number, cy: number, size: number, vertex: 'top' | 'right' | 'bottom' | 'left'): Point {
  const r = size / Math.SQRT2;
  if (vertex === 'top') return { x: cx, y: cy - r };
  if (vertex === 'right') return { x: cx + r, y: cy };
  if (vertex === 'bottom') return { x: cx, y: cy + r };
  return { x: cx - r, y: cy };
}

function diamondEdgeMidpoint(cx: number, cy: number, size: number, edge: string): Point {
  const d = size / (2 * Math.SQRT2);
  if (edge === 'top-left') return { x: cx - d, y: cy - d };
  if (edge === 'top-right') return { x: cx + d, y: cy - d };
  if (edge === 'bottom-left') return { x: cx - d, y: cy + d };
  return { x: cx + d, y: cy + d };
}

/** 다이아몬드 변 중점에서 바깥(테두리)쪽으로 살짝 밀어 연결 */
function diamondBorderEdgeMidpoint(
  cx: number,
  cy: number,
  size: number,
  edge: string,
  outward = 2,
): Point {
  const mid = diamondEdgeMidpoint(cx, cy, size, edge);
  const dx = mid.x - cx;
  const dy = mid.y - cy;
  const len = Math.hypot(dx, dy) || 1;
  return { x: mid.x + (dx / len) * outward, y: mid.y + (dy / len) * outward };
}

function cardEdgeAnchor(cardEl: Element, layout: Element, side: 'left' | 'right', y: number): Point {
  const cardBox = relBox(cardEl, layout);
  const clampedY = Math.max(cardBox.top + 10, Math.min(cardBox.bottom - 10, y));
  return { x: side === 'right' ? cardBox.right : cardBox.left, y: clampedY };
}

/** 참고 레이아웃: 카드 안쪽 변 — 위 행은 중앙보다 살짝 아래, 아래 행은 살짝 위 */
function cardReferenceInnerEdgeAnchor(
  cardEl: Element,
  layout: Element,
  side: 'left' | 'right',
  vert: 'top' | 'bottom',
): Point {
  const cardBox = relBox(cardEl, layout);
  const offset = Math.min(36, Math.max(22, cardBox.height * 0.09));
  const yShift = vert === 'top' ? offset : -offset;
  const pad = 14;
  const y = Math.max(cardBox.top + pad, Math.min(cardBox.bottom - pad, cardBox.cy + yShift));
  return { x: side === 'right' ? cardBox.right : cardBox.left, y };
}

const FACE_LINKS = [
  { edge: 'top-left', card: '.dept-card--hrm', side: 'right' as const, vert: 'top' as const, knee: 'left' as const },
  { edge: 'top-right', card: '.dept-card--hrd', side: 'left' as const, vert: 'top' as const, knee: 'right' as const },
  { edge: 'bottom-left', card: '.dept-card--ex', side: 'right' as const, vert: 'bottom' as const, knee: 'left' as const },
  { edge: 'bottom-right', card: '.dept-card--ga', side: 'left' as const, vert: 'bottom' as const, knee: 'right' as const },
];

function buildBentPath(
  cardAnchor: Point,
  edgeMid: Point,
  side: 'left' | 'right',
  kneeX: number,
  approachX: number,
): Point[] {
  const minLeg = 18;
  let x1 = kneeX;
  if (side === 'right') {
    if (x1 < cardAnchor.x + minLeg) x1 = cardAnchor.x + minLeg;
    return [cardAnchor, { x: x1, y: cardAnchor.y }, { x: approachX, y: edgeMid.y }, edgeMid];
  }
  if (x1 > cardAnchor.x - minLeg) x1 = cardAnchor.x - minLeg;
  return [cardAnchor, { x: x1, y: cardAnchor.y }, { x: approachX, y: edgeMid.y }, edgeMid];
}

/** 참고 레이아웃: 카드 → 짧은 수평 → ~120° 둔각 꺾임 → 대각 → 다이아몬드 */
function buildReferenceElbowPath(
  cardAnchor: Point,
  edgeMid: Point,
  side: 'left' | 'right',
  vert: 'top' | 'bottom',
): Point[] {
  const towardCenter = side === 'right' ? 1 : -1;
  const turnSign = vert === 'top' ? -1 : 1;
  const turn = Math.PI / 3; // 60° 꺾임 → 내각 120°
  const uOutX = towardCenter * Math.cos(turn);
  const uOutY = turnSign * Math.sin(turn);

  const minStub = 18;
  const maxStub = 64;

  let knee: Point;
  if (Math.abs(uOutY) > 1e-4) {
    const t = (cardAnchor.y - edgeMid.y) / uOutY;
    knee = { x: edgeMid.x - t * uOutX, y: cardAnchor.y };
  } else {
    knee = { x: cardAnchor.x + towardCenter * minStub, y: cardAnchor.y };
  }

  const stub = (knee.x - cardAnchor.x) * towardCenter;
  if (!Number.isFinite(stub) || stub < minStub || stub > maxStub) {
    knee = { x: cardAnchor.x + towardCenter * Math.max(minStub, Math.min(maxStub, stub || minStub)), y: cardAnchor.y };
  }

  return [cardAnchor, knee, edgeMid];
}

function anchorYForSymmetricFace(faceY: number, runX: number, vert: 'top' | 'bottom') {
  return vert === 'top' ? faceY + runX : faceY - runX;
}

const REF_HUB_LINE_COLOR = '#c5d2de';
const REF_HUB_DOT_COLOR = '#b8c6d4';
/** 둥근 꼭짓점 — 선 끝은 테두리 밖(다이아 레이어에 가려짐) */
const REF_DIAMOND_LINE_OVERLAP = 0;

/** reference 상·하: 실제 렌더 박스 + border-radius 보정 (기하 topV는 둥근 꼭짓점보다 바깥) */
function referenceDiamondVerticalY(
  diamondEl: HTMLElement,
  diamondBox: Box,
  end: 'top' | 'bottom',
): number {
  const r = parseFloat(getComputedStyle(diamondEl).borderRadius) || 16;
  const tipOffset = Math.min(r * 0.7, diamondBox.height * 0.07);
  return end === 'top'
    ? diamondBox.top + tipOffset + REF_DIAMOND_LINE_OVERLAP
    : diamondBox.bottom - tipOffset - REF_DIAMOND_LINE_OVERLAP;
}

function fitDiamond(hubColumn: HTMLElement | null, diamond: HTMLElement | null) {
  if (!hubColumn || !diamond || window.innerWidth <= 1200) return;
  const wrap = diamond.parentElement;
  if (!wrap) return;
  const rowW = wrap.clientWidth;
  const rowH = wrap.clientHeight;
  const fitInRow = 1.04 / Math.SQRT2;
  let size = Math.min(rowW * fitInRow, rowH * fitInRow);
  let scale = 0.9;
  const v = getComputedStyle(hubColumn).getPropertyValue('--hub-diamond-scale').trim();
  if (v) scale = parseFloat(v) || scale;
  size = Math.max(Math.floor(size * scale), Math.floor(150 * scale));
  diamond.style.width = `${size}px`;
  diamond.style.height = `${size}px`;
}

export type ConnectorStyle = 'default' | 'reference';

export function useBoardConnectors(enabled = true, style: ConnectorStyle = 'default') {
  const lineGroupRef = useRef<SVGGElement>(null);
  const svgRef = useRef<SVGSVGElement>(null);
  const dotGroupRef = useRef<SVGGElement>(null);
  const dotSvgRef = useRef<SVGSVGElement>(null);

  useEffect(() => {
    if (!enabled) return;

    let resizeTimer: ReturnType<typeof setTimeout>;

    const drawConnectors = () => {
      const layout = document.querySelector('.board-layout');
      const diamond = document.getElementById('hub-diamond');
      const hubColumn = document.getElementById('hub-column');
      const lineGroup = lineGroupRef.current;
      const svg = svgRef.current;
      const dotGroup = dotGroupRef.current;
      const dotSvg = dotSvgRef.current;
      if (!layout || !diamond || !lineGroup || !svg) return;

      fitDiamond(hubColumn, diamond);

      requestAnimationFrame(() => {
        requestAnimationFrame(() => {
          if (!layout.isConnected || !diamond.isConnected) return;
          drawConnectorsNow(
            layout,
            diamond,
            hubColumn,
            lineGroup,
            svg,
            dotGroup,
            dotSvg,
          );
        });
      });
    };

    const drawConnectorsNow = (
      layout: Element,
      diamond: HTMLElement,
      hubColumn: HTMLElement | null,
      lineGroup: SVGGElement,
      svg: SVGSVGElement,
      dotGroup: SVGGElement | null,
      dotSvg: SVGSVGElement | null,
    ) => {
      if (window.innerWidth <= 1200) {
        lineGroup.innerHTML = '';
        dotGroup && (dotGroup.innerHTML = '');
        svg.removeAttribute('viewBox');
        dotSvg?.removeAttribute('viewBox');
        return;
      }

      const layoutBox = layout.getBoundingClientRect();
      const diamondBox = relBox(diamond, layout);
      const diamondSize = diamond.offsetWidth;
      const diamondCx = diamondBox.cx;
      const diamondCy = diamondBox.cy;

      svg.setAttribute('viewBox', `0 0 ${layoutBox.width} ${layoutBox.height}`);
      lineGroup.innerHTML = '';
      if (dotGroup) dotGroup.innerHTML = '';
      if (dotSvg) dotSvg.setAttribute('viewBox', `0 0 ${layoutBox.width} ${layoutBox.height}`);

      const topV = diamondVertex(diamondCx, diamondCy, diamondSize, 'top');
      const bottomV = diamondVertex(diamondCx, diamondCy, diamondSize, 'bottom');

      const hubColumnEl = layout.querySelector('.hub-column, #hub-column');
      const hubBoxEls = hubColumnEl
        ? Array.from(hubColumnEl.querySelectorAll(':scope > .hub-box'))
        : [];
      const topHubBox =
        hubBoxEls[0] ??
        (style === 'reference'
          ? layout.querySelector('.hub-box--message')
          : layout.querySelector('.hub-postit-sheet--front'));
      const bottomHubBox =
        (hubBoxEls.length > 1 ? hubBoxEls[hubBoxEls.length - 1] : hubBoxEls[0]) ??
        (style === 'reference'
          ? layout.querySelector('.hub-box--focus')
          : layout.querySelector('.hub-schedule-planner'));

      const hubBox = hubColumn ? relBox(hubColumn, layout) : null;
      const hrmBox = document.querySelector('.dept-card--hrm');
      const exBox = document.querySelector('.dept-card--ex');
      const hrdBox = document.querySelector('.dept-card--hrd');
      const gaBox = document.querySelector('.dept-card--ga');
      const kneeXs = { left: 0, right: 0 };

      if (hubBox && hrmBox && exBox) {
        const innerLeft = Math.max(relBox(hrmBox, layout).right, relBox(exBox, layout).right);
        kneeXs.left = (innerLeft + hubBox.left) / 2;
      }
      if (hubBox && hrdBox && gaBox) {
        const innerRight = Math.min(relBox(hrdBox, layout).left, relBox(gaBox, layout).left);
        kneeXs.right = (innerRight + hubBox.right) / 2;
      }

      const d = diamondSize / (2 * Math.SQRT2);
      const approachGap = 32;
      const leftApproachX = diamondCx - d - approachGap;
      const rightApproachX = diamondCx + d + approachGap;
      const leftRunX = Math.max(12, leftApproachX - kneeXs.left);
      const rightRunX = Math.max(12, kneeXs.right - rightApproachX);

      const REF_CARD_LINE_COLORS: Record<string, string> = {
        '.dept-card--hrm': '#4a90d9',
        '.dept-card--hrd': '#37a184',
        '.dept-card--ex': '#9168b8',
        '.dept-card--ga': '#2563ab',
      };

      const appendPath = (
        points: Point[],
        opts?: {
          cardSelector?: string;
          dotAt?: Point;
          vertical?: boolean;
          markerSide?: 'left' | 'right';
          markerDir?: 'up' | 'down';
        },
      ) => {
        if (points.length < 2) return;

        const cardSelector = opts?.cardSelector;
        const stroke =
          opts?.vertical
            ? REF_HUB_LINE_COLOR
            : style === 'reference' && cardSelector
              ? REF_CARD_LINE_COLORS[cardSelector] ?? REF_HUB_LINE_COLOR
              : style === 'reference'
                ? REF_HUB_LINE_COLOR
                : '#b0bcc8';

        if (opts?.vertical && points.length === 2) {
          const [a, b] = points;
          const line = document.createElementNS(SVG_NS, 'line');
          line.setAttribute('x1', String(snap(a.x)));
          line.setAttribute('y1', String(snap(a.y)));
          line.setAttribute('x2', String(snap(b.x)));
          line.setAttribute('y2', String(snap(b.y)));
          line.setAttribute('stroke', stroke);
          line.setAttribute('stroke-width', '2');
          line.setAttribute('fill', 'none');
          line.setAttribute('stroke-linecap', 'butt');
          lineGroup.appendChild(line);
        } else {
          const path = document.createElementNS(SVG_NS, 'path');
          const normalized = normalizePathPoints(points);
          if (normalized.length < 2) return;
          path.setAttribute('d', pointsToPath(normalized));
          path.setAttribute('stroke', stroke);
          path.setAttribute('stroke-width', style === 'reference' ? '2' : '2.5');
          path.setAttribute('opacity', style === 'reference' ? '1' : '0.85');
          path.setAttribute('fill', 'none');
          path.setAttribute('stroke-linecap', style === 'reference' ? 'butt' : 'round');
          path.setAttribute('stroke-linejoin', style === 'reference' ? 'miter' : 'round');
          lineGroup.appendChild(path);
        }

        if (style === 'reference' && opts?.dotAt && dotGroup) {
          const fill =
            cardSelector ? (REF_CARD_LINE_COLORS[cardSelector] ?? REF_HUB_DOT_COLOR) : REF_HUB_DOT_COLOR;
          if (cardSelector && opts.markerSide) {
            appendBoomerangMarker(dotGroup, opts.dotAt, opts.markerSide, fill);
          } else if (opts.markerDir) {
            appendHubBoomerangMarker(dotGroup, opts.dotAt, opts.markerDir, fill);
          } else {
            const dot = document.createElementNS(SVG_NS, 'circle');
            dot.setAttribute('cx', String(snap(opts.dotAt.x)));
            dot.setAttribute('cy', String(snap(opts.dotAt.y)));
            dot.setAttribute('r', '4');
            dot.setAttribute('fill', fill);
            dotGroup.appendChild(dot);
          }
        }
      };

      FACE_LINKS.forEach((link) => {
        const cardEl = document.querySelector(link.card);
        if (!cardEl) return;
        const edgeMid =
          style === 'reference'
            ? diamondBorderEdgeMidpoint(diamondCx, diamondCy, diamondSize, link.edge)
            : diamondEdgeMidpoint(diamondCx, diamondCy, diamondSize, link.edge);
        const runX = link.knee === 'left' ? leftRunX : rightRunX;
        const approachX = link.knee === 'left' ? leftApproachX : rightApproachX;
        const cardAnchor =
          style === 'reference'
            ? cardReferenceInnerEdgeAnchor(cardEl, layout, link.side, link.vert)
            : cardEdgeAnchor(
                cardEl,
                layout,
                link.side,
                anchorYForSymmetricFace(edgeMid.y, runX, link.vert),
              );
        const pathPoints =
          style === 'reference'
            ? buildReferenceElbowPath(cardAnchor, edgeMid, link.side, link.vert)
            : buildBentPath(cardAnchor, edgeMid, link.side, kneeXs[link.knee], approachX);

        if (style === 'reference') {
          appendPath(pathPoints, {
            cardSelector: link.card,
            dotAt: cardAnchor,
            markerSide: link.side,
          });
        } else {
          appendPath(pathPoints);
        }
      });

      if (style === 'reference' && topHubBox) {
        const topHub = relBox(topHubBox, layout);
        const hubAnchor = { x: diamondCx, y: topHub.bottom };
        const diamondTopY = referenceDiamondVerticalY(diamond, diamondBox, 'top');
        const topPoints = referenceVerticalLine(diamondCx, hubAnchor.y, diamondTopY);
        appendPath(topPoints, { vertical: true, dotAt: hubAnchor, markerDir: 'down' });
      } else if (topHubBox) {
        const topHub = relBox(topHubBox, layout);
        appendPath([topV, { x: diamondCx, y: topHub.bottom }]);
      }

      if (style === 'reference' && bottomHubBox) {
        const bottomHub = relBox(bottomHubBox, layout);
        const hubAnchor = { x: diamondCx, y: bottomHub.top };
        const diamondBottomY = referenceDiamondVerticalY(diamond, diamondBox, 'bottom');
        const bottomPoints = referenceVerticalLine(diamondCx, diamondBottomY, hubAnchor.y);
        appendPath(bottomPoints, { vertical: true, dotAt: hubAnchor, markerDir: 'up' });
      } else if (bottomHubBox) {
        const bottomHub = relBox(bottomHubBox, layout);
        appendPath([bottomV, { x: diamondCx, y: bottomHub.top }]);
      }
    };

    const scheduleDraw = () => {
      clearTimeout(resizeTimer);
      resizeTimer = setTimeout(drawConnectors, 50);
    };

    window.addEventListener('resize', scheduleDraw);
    drawConnectors();

    if (document.fonts?.ready) {
      document.fonts.ready.then(() => {
        requestAnimationFrame(() => requestAnimationFrame(drawConnectors));
      });
    }

    const layoutEl = document.querySelector('.board-layout');
    const hubColEl = document.getElementById('hub-column');
    let ro: ResizeObserver | undefined;
    if (typeof ResizeObserver !== 'undefined') {
      ro = new ResizeObserver(scheduleDraw);
      if (layoutEl) ro.observe(layoutEl);
      if (hubColEl) ro.observe(hubColEl);
    }

    return () => {
      window.removeEventListener('resize', scheduleDraw);
      clearTimeout(resizeTimer);
      ro?.disconnect();
    };
  }, [enabled, style]);

  return { svgRef, lineGroupRef, dotSvgRef, dotGroupRef };
}
