import { useCallback, useEffect, useMemo, useState } from 'react';
import { useLocation } from 'react-router-dom';
import { useOnboarding } from '@/features/onboarding/hooks/useOnboarding';
import { TOUR_DEFINITIONS } from '@/features/onboarding/constants/onboarding.constants';
import type { TourStep } from '@/features/onboarding/types/onboarding.types';
import {
  findNextValidStepIndex,
  findTourTarget,
  getTargetRect,
  scrollTargetIntoView,
} from '@/features/onboarding/utils/tourScroll';

export function useGuidedTour() {
  const location = useLocation();
  const { dismissedTours, dismissTour } = useOnboarding();
  const [activeTourId, setActiveTourId] = useState<string | null>(null);
  const [stepIndex, setStepIndex] = useState(0);
  const [targetRect, setTargetRect] = useState<DOMRect | null>(null);
  const [hasMissingTarget, setHasMissingTarget] = useState(false);

  const tour = useMemo(
    () => TOUR_DEFINITIONS.find(
      (definition) =>
        !dismissedTours.includes(definition.id) &&
        definition.routePattern.test(location.pathname),
    ),
    [dismissedTours, location.pathname],
  );

  const steps: TourStep[] = useMemo(
    () => (tour?.steps ?? []).map((step) => ({ ...step })),
    [tour],
  );

  const currentStep = steps[stepIndex] ?? null;
  const isActive = Boolean(tour && activeTourId === tour.id && currentStep);

  useEffect(() => {
    if (!tour || dismissedTours.includes(tour.id)) {
      setActiveTourId(null);
      return;
    }

    const timer = window.setTimeout(() => {
      setActiveTourId(tour.id);
      setStepIndex(0);
    }, 600);

    return () => window.clearTimeout(timer);
  }, [tour, dismissedTours, location.pathname]);

  useEffect(() => {
    if (!isActive || !currentStep) {
      setTargetRect(null);
      setHasMissingTarget(false);
      return;
    }

    let cancelled = false;

    const measureTarget = () => {
      const element = findTourTarget(currentStep.target);
      if (element) {
        setTargetRect(getTargetRect(element));
        setHasMissingTarget(false);
      } else {
        setTargetRect(null);
        setHasMissingTarget(true);
      }
    };

    const activateStep = async () => {
      const element = findTourTarget(currentStep.target);

      if (!element) {
        const nextValidIndex = findNextValidStepIndex(steps, stepIndex);
        if (nextValidIndex !== null) {
          setStepIndex(nextValidIndex);
          return;
        }

        if (!cancelled) {
          setTargetRect(null);
          setHasMissingTarget(true);
        }
        return;
      }

      await scrollTargetIntoView(element);
      if (!cancelled) {
        measureTarget();
      }
    };

    void activateStep();

    window.addEventListener('resize', measureTarget);
    window.addEventListener('scroll', measureTarget, true);

    return () => {
      cancelled = true;
      window.removeEventListener('resize', measureTarget);
      window.removeEventListener('scroll', measureTarget, true);
    };
  }, [isActive, currentStep, stepIndex, steps]);

  const next = useCallback(() => {
    if (stepIndex < steps.length - 1) {
      setStepIndex((index) => index + 1);
      return;
    }
    if (tour) {
      void dismissTour(tour.id);
    }
    setActiveTourId(null);
  }, [dismissTour, stepIndex, steps.length, tour]);

  const back = useCallback(() => {
    setStepIndex((index) => Math.max(0, index - 1));
  }, []);

  const dismissForever = useCallback(() => {
    if (tour) {
      void dismissTour(tour.id);
    }
    setActiveTourId(null);
  }, [dismissTour, tour]);

  const skip = useCallback(() => {
    setActiveTourId(null);
  }, []);

  return {
    isActive,
    tourId: tour?.id ?? null,
    currentStep,
    stepIndex,
    totalSteps: steps.length,
    targetRect,
    hasMissingTarget,
    next,
    back,
    dismissForever,
    skip,
  };
}
