"use client";

import {
  useEffect,
  useId,
  useLayoutEffect,
  useRef,
  useState,
  type RefObject,
} from "react";

type DomainTourTarget = {
  description: string;
  eyebrow: string;
  id: string;
  targetRef: RefObject<HTMLElement | null>;
  title: string;
};

type DomainConnectionTourProps = {
  onClose: (completed: boolean) => void;
  open: boolean;
  steps: DomainTourTarget[];
};

type TourGeometry = {
  bubbleHeight: number;
  bubbleLeft: number;
  bubbleTop: number;
  bubbleWidth: number;
  spotlightHeight: number;
  spotlightLeft: number;
  spotlightTop: number;
  spotlightWidth: number;
  viewportHeight: number;
  viewportWidth: number;
};

const EMPTY_GEOMETRY: TourGeometry = {
  bubbleHeight: 0,
  bubbleLeft: 16,
  bubbleTop: 16,
  bubbleWidth: 320,
  spotlightHeight: 0,
  spotlightLeft: 0,
  spotlightTop: 0,
  spotlightWidth: 0,
  viewportHeight: 0,
  viewportWidth: 0,
};

function clamp(value: number, minimum: number, maximum: number) {
  return Math.min(Math.max(value, minimum), Math.max(minimum, maximum));
}

function calculateGeometry(
  target: HTMLElement,
  bubble: HTMLElement | null,
): TourGeometry {
  const rect = target.getBoundingClientRect();
  const viewportWidth = window.innerWidth;
  const viewportHeight = window.innerHeight;
  const spotlightPadding = viewportWidth < 640 ? 7 : 10;
  const spotlightLeft = clamp(rect.left - spotlightPadding, 8, viewportWidth - 8);
  const spotlightTop = clamp(rect.top - spotlightPadding, 8, viewportHeight - 8);
  const spotlightWidth = Math.min(
    rect.width + spotlightPadding * 2,
    viewportWidth - spotlightLeft - 8,
  );
  const spotlightHeight = Math.min(
    rect.height + spotlightPadding * 2,
    viewportHeight - spotlightTop - 8,
  );
  const bubbleWidth = Math.min(viewportWidth - 24, viewportWidth < 640 ? 344 : 380);
  const bubbleHeight = bubble?.offsetHeight || 228;
  const gap = viewportWidth < 640 ? 22 : 34;
  const roomBelow = viewportHeight - (spotlightTop + spotlightHeight);
  const shouldPlaceBelow = roomBelow >= bubbleHeight + gap + 12 || spotlightTop < bubbleHeight + gap;
  const bubbleTop = shouldPlaceBelow
    ? clamp(spotlightTop + spotlightHeight + gap, 12, viewportHeight - bubbleHeight - 12)
    : clamp(spotlightTop - bubbleHeight - gap, 12, viewportHeight - bubbleHeight - 12);
  const bubbleLeft = clamp(
    spotlightLeft + spotlightWidth / 2 - bubbleWidth / 2,
    12,
    viewportWidth - bubbleWidth - 12,
  );

  return {
    bubbleHeight,
    bubbleLeft,
    bubbleTop,
    bubbleWidth,
    spotlightHeight,
    spotlightLeft,
    spotlightTop,
    spotlightWidth,
    viewportHeight,
    viewportWidth,
  };
}

function createLeaderPath(geometry: TourGeometry) {
  const bubbleIsBelow = geometry.bubbleTop > geometry.spotlightTop;
  const startX = geometry.spotlightLeft + geometry.spotlightWidth / 2;
  const startY = bubbleIsBelow
    ? geometry.spotlightTop + geometry.spotlightHeight
    : geometry.spotlightTop;
  const endX = clamp(
    startX,
    geometry.bubbleLeft + 34,
    geometry.bubbleLeft + geometry.bubbleWidth - 34,
  );
  const endY = bubbleIsBelow
    ? geometry.bubbleTop
    : geometry.bubbleTop + geometry.bubbleHeight;
  const controlOffset = Math.max(18, Math.abs(endY - startY) * 0.45);
  const firstControlY = bubbleIsBelow ? startY + controlOffset : startY - controlOffset;
  const secondControlY = bubbleIsBelow ? endY - controlOffset : endY + controlOffset;

  return `M ${startX} ${startY} C ${startX} ${firstControlY}, ${endX} ${secondControlY}, ${endX} ${endY}`;
}

export function DomainConnectionTour({
  onClose,
  open,
  steps,
}: DomainConnectionTourProps) {
  const maskId = useId().replace(/:/g, "");
  const bubbleRef = useRef<HTMLDivElement | null>(null);
  const [activeIndex, setActiveIndex] = useState(0);
  const [geometry, setGeometry] = useState<TourGeometry>(EMPTY_GEOMETRY);
  const activeStep = steps[Math.min(activeIndex, Math.max(steps.length - 1, 0))];

  useLayoutEffect(() => {
    if (!open || !activeStep?.targetRef.current) {
      return undefined;
    }

    const target = activeStep.targetRef.current;
    const update = () => {
      setGeometry(calculateGeometry(target, bubbleRef.current));
    };

    target.scrollIntoView({ behavior: "smooth", block: "center", inline: "nearest" });
    const settleTimer = window.setTimeout(update, 360);
    update();
    window.addEventListener("resize", update);
    window.addEventListener("scroll", update, true);
    const resizeObserver = new ResizeObserver(update);
    resizeObserver.observe(target);

    return () => {
      window.clearTimeout(settleTimer);
      window.removeEventListener("resize", update);
      window.removeEventListener("scroll", update, true);
      resizeObserver.disconnect();
    };
  }, [activeIndex, activeStep, open]);

  useEffect(() => {
    if (!open) {
      return undefined;
    }

    const handleKeyDown = (event: KeyboardEvent) => {
      if (event.key === "Escape") {
        setActiveIndex(0);
        onClose(false);
      }
    };

    window.addEventListener("keydown", handleKeyDown);
    return () => window.removeEventListener("keydown", handleKeyDown);
  }, [onClose, open]);

  if (!open || !activeStep || geometry.viewportWidth === 0) {
    return null;
  }

  const isLastStep = activeIndex === steps.length - 1;
  const leaderPath = createLeaderPath(geometry);
  const closeTour = (completed: boolean) => {
    setActiveIndex(0);
    onClose(completed);
  };

  return (
    <div className="fixed inset-0 z-[2147483000]" role="presentation">
      <svg
        aria-hidden="true"
        className="pointer-events-none fixed inset-0 h-full w-full"
        viewBox={`0 0 ${geometry.viewportWidth} ${geometry.viewportHeight}`}
        preserveAspectRatio="none"
      >
        <defs>
          <mask id={maskId}>
            <rect width="100%" height="100%" fill="white" />
            <rect
              x={geometry.spotlightLeft}
              y={geometry.spotlightTop}
              width={geometry.spotlightWidth}
              height={geometry.spotlightHeight}
              rx="16"
              fill="black"
            />
          </mask>
        </defs>
        <rect
          width="100%"
          height="100%"
          fill="rgba(10, 20, 38, 0.7)"
          mask={`url(#${maskId})`}
        />
        <path className="om-domain-tour-line" d={leaderPath} />
      </svg>

      <div
        aria-hidden="true"
        className="om-domain-tour-spotlight pointer-events-none fixed rounded-[16px]"
        style={{
          height: geometry.spotlightHeight,
          left: geometry.spotlightLeft,
          top: geometry.spotlightTop,
          width: geometry.spotlightWidth,
        }}
      />

      <div
        ref={bubbleRef}
        role="dialog"
        aria-label="도메인 연결 화면 안내"
        aria-modal="true"
        className="om-domain-tour-bubble fixed overflow-hidden rounded-[18px] border border-white/15 bg-[#111c30] text-white shadow-[0_28px_90px_rgba(4,12,28,0.42)]"
        style={{
          left: geometry.bubbleLeft,
          top: geometry.bubbleTop,
          width: geometry.bubbleWidth,
        }}
      >
        <div className="px-5 pb-4 pt-5 sm:px-6 sm:pt-6">
          <div className="flex items-center justify-between gap-4">
            <p className="text-[11px] font-semibold tracking-[0.14em] text-[#8eb6ff]">
              {activeStep.eyebrow}
            </p>
            <span className="text-[11px] font-semibold tabular-nums text-white/45">
              {activeIndex + 1} / {steps.length}
            </span>
          </div>
          <h2 className="mt-3 text-[19px] font-semibold leading-[1.35] tracking-[-0.04em] sm:text-[21px]">
            {activeStep.title}
          </h2>
          <p className="mt-2.5 text-[13px] leading-6 text-white/68">
            {activeStep.description}
          </p>
        </div>

        <div className="flex items-center justify-between border-t border-white/10 px-4 py-3 sm:px-5">
          <button
            type="button"
            onClick={() => closeTour(false)}
            className="h-9 cursor-pointer px-2 text-[13px] font-semibold text-white/52 transition-colors hover:text-white"
          >
            건너뛰기
          </button>
          <div className="flex items-center gap-2">
            {activeIndex > 0 ? (
              <button
                type="button"
                onClick={() => setActiveIndex((current) => Math.max(0, current - 1))}
                className="h-9 cursor-pointer rounded-[10px] px-3 text-[13px] font-semibold text-white/65 transition-colors hover:bg-white/8 hover:text-white"
              >
                이전
              </button>
            ) : null}
            <button
              type="button"
              onClick={() => {
                if (isLastStep) {
                  closeTour(true);
                  return;
                }

                setActiveIndex((current) => Math.min(steps.length - 1, current + 1));
              }}
              className="h-9 min-w-[72px] cursor-pointer rounded-[10px] bg-[#2f6df6] px-4 text-[13px] font-semibold text-white transition-colors hover:bg-[#245ed6]"
            >
              {isLastStep ? "완료" : "다음"}
            </button>
          </div>
        </div>
      </div>
    </div>
  );
}
