import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { AnimatePresence, motion, type Variants } from "motion/react";
import { cn } from "@/shared/ui/cn";
import type {
  ScheduledConditionRecord
} from "../types";
import {
  createVisibleTickerCards,
  type TickerStackCard
} from "./agent-task-ticker-utils";

type AgentTaskTickerProps = {
  conditions: ScheduledConditionRecord[];
  className?: string;
};

type TickerTransitionDirection = -1 | 1;

const tickerEase = [0.22, 1, 0.36, 1] as const;
const tickerSwitchAnimationMs = 380;
const tickerCardVariants: Variants = {
  initial: (direction: TickerTransitionDirection) => ({
    opacity: 0.62,
    x: 0,
    y: direction > 0 ? 36 : -28,
    rotate: 0
  }),
  exit: (direction: TickerTransitionDirection) => ({
    opacity: [1, 1, 0],
    x: 0,
    y: direction > 0 ? 36 : -28,
    rotate: 0,
    transition: {
      duration: 0.3,
      times: [0, 0.65, 1],
      ease: [0.5, 0, 0.2, 1]
    }
  })
};

const tickerContentVariants: Variants = {
  initial: {
    clipPath: "inset(0 100% 0 0)"
  },
  animate: {
    clipPath: "inset(0 0% 0 0)",
    transition: {
      duration: 0.18,
      ease: tickerEase
    }
  },
  exit: {
    clipPath: "inset(0 0 0 100%)",
    transition: {
      duration: 0.14,
      ease: [0.5, 0, 0.2, 1]
    }
  }
};

const tickerIndicatorVariants: Variants = {
  initial: {
    opacity: 0,
    x: 4,
    scale: 0.96
  },
  animate: {
    opacity: 1,
    x: 0,
    scale: 1,
    transition: {
      duration: 0.16,
      ease: tickerEase
    }
  },
  exit: {
    opacity: 0,
    x: 4,
    scale: 0.96,
    transition: {
      duration: 0.12,
      ease: [0.5, 0, 0.2, 1]
    }
  }
};

export function AgentTaskTicker({
  conditions,
  className
}: AgentTaskTickerProps) {
  const sectionRef = useRef<HTMLElement | null>(null);
  const isTaskSwitchLockedRef = useRef(false);
  const taskSwitchUnlockTimeoutRef = useRef<number | null>(null);
  const [nowMs, setNowMs] = useState(() => Date.now());
  const [rotationIndex, setRotationIndex] = useState(0);
  const [transitionDirection, setTransitionDirection] = useState<TickerTransitionDirection>(1);
  const [taskSwitchAnimation, setTaskSwitchAnimation] = useState(() => ({
    id: 0,
    active: false
  }));
  const [isHovered, setIsHovered] = useState(false);
  const [isDocumentVisible, setIsDocumentVisible] = useState(() => (
    typeof document === "undefined" || document.visibilityState === "visible"
  ));
  const [visibilityRevision, setVisibilityRevision] = useState(0);
  const runningConditions = conditions;
  const visibleTickerCards = useMemo(
    () => createVisibleTickerCards(runningConditions, rotationIndex, nowMs),
    [runningConditions, rotationIndex, nowMs]
  );
  const hasMultipleRunningTasks = runningConditions.length > 1;
  const activeIndicatorIndex = runningConditions.length > 0 ? rotationIndex % runningConditions.length : 0;
  const tickerLabel = hasMultipleRunningTasks
    ? `工况任务步骤，${runningConditions.length} 个执行中，可滚动切换`
    : "工况任务步骤";

  const releaseTaskSwitchLock = useCallback(() => {
    if (taskSwitchUnlockTimeoutRef.current !== null) {
      window.clearTimeout(taskSwitchUnlockTimeoutRef.current);
      taskSwitchUnlockTimeoutRef.current = null;
    }

    isTaskSwitchLockedRef.current = false;
  }, []);

  const finishTaskSwitchAnimation = useCallback(() => {
    setTaskSwitchAnimation((current) => (
      current.active ? { ...current, active: false } : current
    ));
  }, []);

  const lockTaskSwitch = useCallback(() => {
    isTaskSwitchLockedRef.current = true;

    if (taskSwitchUnlockTimeoutRef.current !== null) {
      window.clearTimeout(taskSwitchUnlockTimeoutRef.current);
    }

    taskSwitchUnlockTimeoutRef.current = window.setTimeout(() => {
      isTaskSwitchLockedRef.current = false;
      taskSwitchUnlockTimeoutRef.current = null;
      finishTaskSwitchAnimation();
    }, tickerSwitchAnimationMs);
  }, [finishTaskSwitchAnimation]);

  const shiftTask = useCallback((direction: TickerTransitionDirection) => {
    if (runningConditions.length <= 1 || isTaskSwitchLockedRef.current) {
      return;
    }

    lockTaskSwitch();
    setTransitionDirection(direction);
    setTaskSwitchAnimation((current) => ({
      id: current.id + 1,
      active: true
    }));
    setRotationIndex((current) => (
      (current + direction + runningConditions.length) % runningConditions.length
    ));
  }, [lockTaskSwitch, runningConditions.length]);

  const showPreviousTask = useCallback(() => {
    shiftTask(-1);
  }, [shiftTask]);

  const showNextTask = useCallback(() => {
    shiftTask(1);
  }, [shiftTask]);

  const handleWheel = useCallback((event: WheelEvent) => {
    if (!hasMultipleRunningTasks || event.deltaY === 0) {
      return;
    }

    event.preventDefault();

    if (event.deltaY > 0) {
      showNextTask();
      return;
    }

    showPreviousTask();
  }, [hasMultipleRunningTasks, showNextTask, showPreviousTask]);

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

    const refreshNow = () => setNowMs(Date.now());

    refreshNow();
    const intervalId = window.setInterval(() => {
      refreshNow();
    }, 1_000);

    return () => window.clearInterval(intervalId);
  }, [isDocumentVisible]);

  useEffect(() => {
    setRotationIndex(0);
    releaseTaskSwitchLock();
    finishTaskSwitchAnimation();
  }, [finishTaskSwitchAnimation, releaseTaskSwitchLock, runningConditions.length]);

  useEffect(() => releaseTaskSwitchLock, [releaseTaskSwitchLock]);

  useEffect(() => {
    const handleVisibilityChange = () => {
      const isVisible = document.visibilityState === "visible";

      releaseTaskSwitchLock();
      finishTaskSwitchAnimation();
      setIsHovered(false);
      setIsDocumentVisible(isVisible);
      setVisibilityRevision((current) => current + 1);

      if (isVisible) {
        setNowMs(Date.now());
      }
    };

    document.addEventListener("visibilitychange", handleVisibilityChange);

    return () => document.removeEventListener("visibilitychange", handleVisibilityChange);
  }, [finishTaskSwitchAnimation, releaseTaskSwitchLock]);

  useEffect(() => {
    const section = sectionRef.current;
    if (!section) {
      return;
    }

    section.addEventListener("wheel", handleWheel, { passive: false });

    return () => section.removeEventListener("wheel", handleWheel);
  }, [handleWheel]);

  useEffect(() => {
    if (!hasMultipleRunningTasks || isHovered || !isDocumentVisible) {
      return;
    }

    const intervalId = window.setInterval(() => {
      if (document.visibilityState !== "visible") {
        return;
      }

      shiftTask(1);
    }, 6_000);

    return () => window.clearInterval(intervalId);
  }, [hasMultipleRunningTasks, isDocumentVisible, isHovered, runningConditions.length, shiftTask]);

  if (visibleTickerCards.length === 0) {
    return null;
  }

  return (
    <section
      ref={sectionRef}
      className={cn(
        "agent-task-ticker pointer-events-auto relative h-[78px] overflow-visible",
        className
      )}
      aria-label={tickerLabel}
      title={tickerLabel}
      onMouseEnter={() => setIsHovered(true)}
      onMouseLeave={() => setIsHovered(false)}
    >
      <div className="absolute inset-0">
        <AnimatePresence
          key={`ticker-presence-${visibilityRevision}`}
          custom={transitionDirection}
          mode="popLayout"
          initial={false}
        >
          {visibleTickerCards
            .slice()
            .reverse()
            .map((card) => (
              <TickerTaskCard
                key={`ticker-stack-slot-${card.stackIndex}-${card.stackIndex === 0 ? taskSwitchAnimation.id : 0}`}
                card={card}
                isActive={card.stackIndex === 0}
                animateContentChanges={card.stackIndex === 0 && !taskSwitchAnimation.active}
                transitionDirection={transitionDirection}
              />
            ))}
        </AnimatePresence>
        <div className="pointer-events-none absolute right-3 top-8 z-40 -translate-y-1/2" aria-hidden="true">
          <AnimatePresence initial={false}>
            {isHovered && hasMultipleRunningTasks ? (
              <motion.div
                key="task-indicator"
                className="agent-task-ticker-indicator surface-control flex flex-col gap-1 rounded-full border px-1 py-1"
                variants={tickerIndicatorVariants}
                initial="initial"
                animate="animate"
                exit="exit"
              >
                {runningConditions.map((runningCondition, index) => (
                  <span
                    key={runningCondition.id}
                    className={cn(
                      "block h-1.5 w-1.5 rounded-sm transition-colors duration-150",
                      index === activeIndicatorIndex ? "bg-blue-600" : "bg-slate-300"
                    )}
                  />
                ))}
              </motion.div>
            ) : null}
          </AnimatePresence>
        </div>
      </div>
    </section>
  );
}

function TickerTaskCard({
  card,
  isActive,
  animateContentChanges,
  transitionDirection
}: {
  card: TickerStackCard;
  isActive: boolean;
  animateContentChanges: boolean;
  transitionDirection: TickerTransitionDirection;
}) {
  const { condition, view, stackIndex } = card;
  const stackStyle = getTickerStackStyle(stackIndex);
  const contentKey = view.currentStep
    ? `${condition.id}-${view.currentStep.id}`
    : `${condition.id}-pending`;

  return (
    <motion.article
      layout="position"
      className={cn(
        "absolute inset-x-0 top-0 h-16 overflow-hidden rounded-[22px] px-3 py-2",
        isActive ? "glass-transient z-30" : "surface-control pointer-events-none z-10 border"
      )}
      style={{ transformOrigin: "center top" }}
      custom={transitionDirection}
      variants={tickerCardVariants}
      initial="initial"
      animate={{
        opacity: stackStyle.opacity,
        x: stackStyle.x,
        y: stackStyle.y,
        rotate: stackStyle.rotate,
        transition: {
          duration: isActive ? 0.34 : 0.3,
          ease: tickerEase
        }
      }}
      exit="exit"
      aria-hidden={!isActive}
      data-condition-id={condition.id}
    >
      <div
        className={cn(
          "h-full",
          isActive ? "opacity-100" : "opacity-0"
        )}
      >
        <AnimatePresence key={condition.id} initial={false} mode="wait">
          <motion.div
            key={contentKey}
            className="h-full overflow-hidden"
            variants={tickerContentVariants}
            initial={animateContentChanges ? "initial" : false}
            animate={animateContentChanges ? "animate" : undefined}
            exit={animateContentChanges ? "exit" : undefined}
          >
            <TickerTaskCardContent view={view} />
          </motion.div>
        </AnimatePresence>
      </div>
    </motion.article>
  );
}

function TickerTaskCardContent({ view }: { view: TickerStackCard["view"] }) {
  if (!view.currentStep) {
    return (
      <div className="grid h-full grid-cols-[14px_minmax(0,1fr)] items-center gap-3 pr-4">
        <TickerStatusDot progressValue={view.progressValue} />
        <div className="min-w-0">
          <span className="block truncate text-[13px] font-semibold leading-5 text-slate-500">
            {view.title}
          </span>
          <TickerProgressBar progressLabel={view.progressLabel} muted />
        </div>
      </div>
    );
  }

  return (
    <div className="grid h-full grid-cols-[14px_minmax(0,1fr)] items-center gap-3 pr-5">
      <TickerStatusDot progressValue={view.progressValue} />
      <div className="flex min-w-0 flex-col justify-center">
        <div className="min-w-0 pr-1">
          <span className="block truncate text-[13px] font-semibold leading-4 text-slate-500">
            {view.title}
          </span>
        </div>
        <div className="min-w-0" title={`${view.currentStep.title}：${view.currentStep.detail}`}>
          <div className="grid min-w-0 grid-cols-[auto_4px_minmax(0,1fr)] items-center gap-2">
            <span className="min-w-0 truncate text-[13px] font-bold leading-5 text-slate-950">
              {view.currentStep.title}
            </span>
            <span className="h-1 w-1 rounded-full bg-slate-300" aria-hidden="true" />
            <span className="min-w-0 truncate text-[13px] font-medium leading-5 text-slate-500">
              {view.currentStep.detail}
            </span>
          </div>
        </div>
        <TickerProgressBar progressLabel={view.progressLabel} />
      </div>
    </div>
  );
}

function TickerProgressBar({
  progressLabel,
  muted = false
}: {
  progressLabel: string;
  muted?: boolean;
}) {
  return (
    <div
      className={cn(
        "mt-0.5 h-0.5 overflow-hidden rounded-full",
        muted ? "bg-slate-200/60" : "bg-slate-200/80"
      )}
      aria-hidden="true"
    >
      <motion.span
        className={cn(
          "status-tone-info block h-full rounded-full",
          muted ? "bg-[var(--status-border)]" : "bg-[var(--status-mark)]"
        )}
        animate={{ width: progressLabel }}
        initial={false}
        transition={{ duration: 0.34, ease: tickerEase }}
      />
    </div>
  );
}

function TickerStatusDot({ progressValue }: { progressValue?: number }) {
  const progress = typeof progressValue === "number" ? Math.min(100, Math.max(0, progressValue)) : 0;
  const dashOffset = 44 - (44 * progress) / 100;
  return (
    <span className="status-tone-info grid h-3.5 w-3.5 place-items-center" aria-hidden="true">
      <svg className="h-3.5 w-3.5 -rotate-90" viewBox="0 0 16 16">
        <circle cx="8" cy="8" r="7" fill="none" strokeWidth="2" className="stroke-[var(--status-border)]" />
        <motion.circle
          cx="8"
          cy="8"
          r="7"
          fill="none"
          strokeWidth="2"
          strokeLinecap="round"
          className="stroke-[var(--status-foreground)]"
          strokeDasharray="44"
          animate={{ strokeDashoffset: dashOffset }}
          initial={false}
          transition={{ duration: 0.34, ease: tickerEase }}
        />
        <circle cx="8" cy="8" r="3.5" className="fill-[var(--status-mark)]" />
      </svg>
    </span>
  );
}

function getTickerStackStyle(stackIndex: number) {
  if (stackIndex === 1) {
    return {
      opacity: 1,
      x: 0,
      y: 7,
      rotate: 0
    };
  }

  if (stackIndex === 2) {
    return {
      opacity: 1,
      x: 0,
      y: 14,
      rotate: 0
    };
  }

  return {
    opacity: 1,
    x: 0,
    y: 0,
    rotate: 0
  };
}
