'use client';

import { useState, useCallback, useMemo, useEffect } from 'react';
import { createPortal } from 'react-dom';
import { TourContext, type TourContextValue } from './TourContext';
import { TourSpotlight } from '../components/TourSpotlight';
import { TourContent } from '../components/TourContent';
import { useStepTarget } from '../hooks/useStepTarget';
import { useKeyboardNavigation } from '../hooks/useKeyboardNavigation';
import { logger } from '../utils/logger';
import type { TourProviderProps, TourState, TourStep } from '../types';

/**
 * Tour context provider.
 * Manages tour state and renders overlay.
 */
export function TourProvider({
  tours,
  children,
  spotlight = true,
  spotlightOpacity = 0.5,
  spotlightBlur = 0,
  animated = true,
  pulseRing = false,
  showArrow = false,
  keyboardNavigation = true,
  closeOnOverlayClick = false,
  closeOnEscape = true,
  scrollToTarget = true,
  scrollBehavior = 'smooth',
  progressVariant = 'dots',
  showSkipButton = true,
  skipLabel,
  finishLabel,
  portalContainer,
  zIndex = 50,
  onStart,
  onComplete,
  onSkip,
  onStepChange,
  onBeforeStepChange,
}: TourProviderProps) {
  const [state, setState] = useState<TourState>({
    isActive: false,
    activeTourId: null,
    currentStepIndex: 0,
    isTransitioning: false,
    direction: 'forward',
  });

  const [mounted, setMounted] = useState(false);

  useEffect(() => {
    setMounted(true);
  }, []);

  // Get active tour and current step
  const activeTour = useMemo(
    () => tours.find((t) => t.id === state.activeTourId),
    [tours, state.activeTourId]
  );

  const steps = useMemo(() => {
    if (!activeTour) return [];
    // Apply tour defaults to each step
    return activeTour.steps.map((step) => ({
      ...activeTour.defaults,
      ...step,
    }));
  }, [activeTour]);

  const currentStep = steps[state.currentStepIndex] as TourStep | undefined;

  // Track target elements
  const { targets, isReady } = useStepTarget(currentStep?.target, currentStep?.id, {
    scrollToTarget,
    scrollBehavior,
    spotlightPadding: currentStep?.spotlightPadding ?? 8,
  });

  // Start tour
  const start = useCallback(
    (tourId: string, startIndex = 0) => {
      const tour = tours.find((t) => t.id === tourId);
      if (!tour) {
        logger.warn(`Tour "${tourId}" not found`);
        return;
      }
      if (tour.steps.length === 0) {
        logger.warn(`Tour "${tourId}" has no steps`);
        return;
      }

      logger.info(`Starting tour "${tourId}"`);
      setState({
        isActive: true,
        activeTourId: tourId,
        currentStepIndex: Math.min(startIndex, tour.steps.length - 1),
        isTransitioning: false,
        direction: 'forward',
      });
      onStart?.(tourId);
    },
    [tours, onStart]
  );

  // Close tour
  const close = useCallback(() => {
    if (state.activeTourId) {
      logger.info(`Tour "${state.activeTourId}" closed at step ${state.currentStepIndex}`);
      onSkip?.(state.activeTourId, state.currentStepIndex);
    }
    setState({
      isActive: false,
      activeTourId: null,
      currentStepIndex: 0,
      isTransitioning: false,
      direction: 'forward',
    });
  }, [state.activeTourId, state.currentStepIndex, onSkip]);

  // Next step
  const next = useCallback(async () => {
    if (!activeTour) return;

    const nextIndex = state.currentStepIndex + 1;

    // Complete tour if at last step
    if (nextIndex >= steps.length) {
      logger.info(`Tour "${state.activeTourId}" completed`);
      onComplete?.(state.activeTourId!);
      setState({
        isActive: false,
        activeTourId: null,
        currentStepIndex: 0,
        isTransitioning: false,
        direction: 'forward',
      });
      return;
    }

    // Check if can proceed
    const canProceed = (await onBeforeStepChange?.(state.currentStepIndex, nextIndex)) ?? true;
    if (!canProceed) return;

    // Call step onHide callback
    currentStep?.onHide?.();

    setState((prev) => ({
      ...prev,
      currentStepIndex: nextIndex,
      direction: 'forward',
    }));

    const nextStep = steps[nextIndex];
    onStepChange?.(nextIndex, nextStep, 'forward');
    nextStep?.onShow?.();
  }, [activeTour, state, steps, currentStep, onComplete, onBeforeStepChange, onStepChange]);

  // Previous step
  const previous = useCallback(async () => {
    if (state.currentStepIndex <= 0) return;

    const prevIndex = state.currentStepIndex - 1;

    const canProceed = (await onBeforeStepChange?.(state.currentStepIndex, prevIndex)) ?? true;
    if (!canProceed) return;

    currentStep?.onHide?.();

    setState((prev) => ({
      ...prev,
      currentStepIndex: prevIndex,
      direction: 'backward',
    }));

    const prevStep = steps[prevIndex];
    onStepChange?.(prevIndex, prevStep, 'backward');
    prevStep?.onShow?.();
  }, [state.currentStepIndex, steps, currentStep, onBeforeStepChange, onStepChange]);

  // Go to specific step
  const goTo = useCallback(
    async (index: number) => {
      if (!activeTour || index < 0 || index >= steps.length) return;
      if (index === state.currentStepIndex) return;

      const canProceed = (await onBeforeStepChange?.(state.currentStepIndex, index)) ?? true;
      if (!canProceed) return;

      currentStep?.onHide?.();

      const direction = index > state.currentStepIndex ? 'forward' : 'backward';
      setState((prev) => ({
        ...prev,
        currentStepIndex: index,
        direction,
      }));

      const targetStep = steps[index];
      onStepChange?.(index, targetStep, direction);
      targetStep?.onShow?.();
    },
    [activeTour, steps, state.currentStepIndex, currentStep, onBeforeStepChange, onStepChange]
  );

  // Keyboard navigation
  useKeyboardNavigation({
    enabled: keyboardNavigation && state.isActive,
    onNext: next,
    onPrevious: previous,
    onClose: closeOnEscape ? close : undefined,
  });

  // Lock body scroll when tour is active
  useEffect(() => {
    if (state.isActive) {
      const originalOverflow = document.body.style.overflow;
      document.body.style.overflow = 'hidden';
      return () => {
        document.body.style.overflow = originalOverflow;
      };
    }
  }, [state.isActive]);

  // Context value
  const contextValue: TourContextValue = useMemo(
    () => ({
      state,
      steps,
      currentStep: currentStep ?? null,
      start,
      next,
      previous,
      goTo,
      close,
      isActive: state.isActive,
      activeTourId: state.activeTourId,
      currentStepIndex: state.currentStepIndex,
      totalSteps: steps.length,
      isFirstStep: state.currentStepIndex === 0,
      isLastStep: state.currentStepIndex === steps.length - 1,
      progress: steps.length > 0 ? (state.currentStepIndex + 1) / steps.length : 0,
    }),
    [state, steps, currentStep, start, next, previous, goTo, close]
  );

  // Overlay content
  const overlay = state.isActive && currentStep && isReady && targets.length > 0 && (
    <div
      className="fixed inset-0"
      style={{ zIndex }}
      role="presentation"
      aria-hidden="true"
    >
      {spotlight && !currentStep.disableSpotlight && (
        <TourSpotlight
          targets={targets}
          opacity={spotlightOpacity}
          blur={spotlightBlur}
          animated={animated}
          pulseRing={pulseRing}
          onClick={closeOnOverlayClick ? close : undefined}
          allowInteraction={currentStep.allowTargetInteraction}
        />
      )}

      <TourContent
        step={currentStep}
        targetRect={targets[0].rect}
        currentIndex={state.currentStepIndex}
        totalSteps={steps.length}
        onNext={next}
        onPrevious={previous}
        onClose={close}
        progressVariant={progressVariant}
        showSkipButton={showSkipButton}
        showArrow={showArrow}
        skipLabel={skipLabel}
        finishLabel={finishLabel}
        onGoTo={goTo}
      />
    </div>
  );

  return (
    <TourContext.Provider value={contextValue}>
      {children}
      {mounted && createPortal(overlay, portalContainer ?? document.body)}
    </TourContext.Provider>
  );
}
