import React, {JSX, useEffect, useRef, useState} from 'react';
import {createPortal} from 'react-dom';
import {X} from 'lucide-react';
import Button from '@/components/ui/Button';
import IconButton from '@/components/ui/IconButton';

export type GuidePosition =
    'top'
    | 'bottom'
    | 'left'
    | 'right'
    | 'auto'
    | 'top-left'
    | 'top-right'
    | 'bottom-left'
    | 'bottom-right';

export interface GuideStep {
  id: number;
  x?: number;
  y?: number;
  title: string;
  content: React.ReactNode;
  targetSelector?: string;
  highlightRadius?: number;
  position?: GuidePosition;
}

interface GuideTourProps {
  stepId: number;
  steps: GuideStep[];
  onClose: () => void;
  onComplete: () => void;
}

/**
 * Generates the spotlight background style for a given guide step.
 *
 * @param {GuideStep} step - The guide step containing information about the target element,
 *                           position, and properties for spotlight rendering.
 * @return {string} The CSS background string representing the spotlight effect.
 */
function getOverlayColor(opacity: number): string {
    const style: CSSStyleDeclaration = getComputedStyle(document.documentElement);
    const darkest: string = style.getPropertyValue('--theme-darkest-background').trim() || '#1A1A1A';
    const hex: string = darkest.replace('#', '');
    const r: number = parseInt(hex.substring(0, 2), 16);
    const g: number = parseInt(hex.substring(2, 4), 16);
    const b: number = parseInt(hex.substring(4, 6), 16);
    return `rgba(${r}, ${g}, ${b}, ${opacity})`;
}

function getSpotlightBackground(step: GuideStep): string {
  if (step.x !== undefined && step.y !== undefined) {
      return getOverlayColor(0.5);
  }
  if (!step.targetSelector) {
      return getOverlayColor(0.5);
  }
  const element: HTMLElement | null = document.querySelector<HTMLElement>(step.targetSelector);
  if (!element) {
      return getOverlayColor(0.5);
  }
  const rect: DOMRect = element.getBoundingClientRect();
  const centerX: number = rect.left + rect.width / 2;
  const centerY: number = rect.top + rect.height / 2;
  const radius: number = Math.max(rect.width, rect.height) / 2 + (step.highlightRadius || 10);

    return `radial-gradient(circle at ${centerX}px ${centerY}px, transparent ${radius}px, ${getOverlayColor(0.65)} ${radius + 20}px)`;
}

/**
 * Determines the position of a popover element based on the provided guide step properties.
 *
 * @param {GuideStep} step - An object containing the configuration for positioning the popover, including its x and y coordinates, target selector, and preferred position.
 * @return {React.CSSProperties} An object representing the CSS properties to position the popover, including `left`, `top`, and optionally `transform` values.
 */
interface PopoverPosition {
    left: string;
    top: string;
    transform?: string;
}

function getPopoverPosition(step: GuideStep): PopoverPosition {
  if (step.x !== undefined && step.y !== undefined) {
    return {
      left: `${step.x}%`,
      top: `${step.y}%`,
      transform: 'translate(-50%, -50%)'
    };
  }
  
  if (!step.targetSelector) {
    return {
      left: '50%',
      top: '50%',
      transform: 'translate(-50%, -50%)'
    };
  }
  
  const element: HTMLElement | null = document.querySelector<HTMLElement>(step.targetSelector);
  if (!element) {
    return {
      left: '50%',
      top: '50%',
      transform: 'translate(-50%, -50%)'
    };
  }

  const rect: DOMRect = element.getBoundingClientRect();
  const {left, top, width, height} = rect;
  const popoverWidth: number = 420;
  const popoverHeight: number = 300;
  const margin: number = 20;
  const position: GuidePosition = step.position || 'auto';
  
  switch (position) {
    case 'top':
      return {
        left: `${Math.max(margin, Math.min(left + width / 2 - popoverWidth / 2, window.innerWidth - popoverWidth - margin))}px`,
        top: `${Math.max(margin, top - popoverHeight - margin)}px`,
      };
    
    case 'bottom':
      return {
        left: `${Math.max(margin, Math.min(left + width / 2 - popoverWidth / 2, window.innerWidth - popoverWidth - margin))}px`,
        top: `${Math.min(top + height + margin, window.innerHeight - popoverHeight - margin)}px`,
      };
    
    case 'left':
      return {
        left: `${Math.max(margin, left - popoverWidth - margin)}px`,
        top: `${Math.max(margin, Math.min(top + height / 2 - popoverHeight / 2, window.innerHeight - popoverHeight - margin))}px`,
      };
    
    case 'right':
      return {
        left: `${Math.min(left + width + margin, window.innerWidth - popoverWidth - margin)}px`,
        top: `${Math.max(margin, Math.min(top + height / 2 - popoverHeight / 2, window.innerHeight - popoverHeight - margin))}px`,
      };
    
    case 'top-left':
      return {
        left: `${Math.max(margin, left)}px`,
        top: `${Math.max(margin, top - popoverHeight - margin)}px`,
      };
    
    case 'top-right':
      return {
        left: `${Math.max(margin, Math.min(left + width - popoverWidth, window.innerWidth - popoverWidth - margin))}px`,
        top: `${Math.max(margin, top - popoverHeight - margin)}px`,
      };
    
    case 'bottom-left':
      return {
        left: `${Math.max(margin, left)}px`,
        top: `${Math.min(top + height + margin, window.innerHeight - popoverHeight - margin)}px`,
      };
    
    case 'bottom-right':
      return {
        left: `${Math.max(margin, Math.min(left + width - popoverWidth, window.innerWidth - popoverWidth - margin))}px`,
        top: `${Math.min(top + height + margin, window.innerHeight - popoverHeight - margin)}px`,
      };
    
    case 'auto':
    default:
      let x: number = left + width + margin;
      let y: number = top + height / 2 - popoverHeight / 2;
      
      if (x + popoverWidth > window.innerWidth - margin) {
        x = left - popoverWidth - margin;
      }
      
      if (x < margin) {
        x = left + width / 2 - popoverWidth / 2;
        y = top + height + margin;
      }
      
      x = Math.max(margin, Math.min(x, window.innerWidth - popoverWidth - margin));
      y = Math.max(margin, Math.min(y, window.innerHeight - popoverHeight - margin));
      
      return {
        left: `${x}px`,
        top: `${y}px`,
      };
  }
}

/**
 * A component that guides the user through a series of steps.
 * Displays a sequence of instructional overlay elements based on the provided steps.
 * Handles navigation between steps and supports custom actions upon completion or closure.
 *
 * @param {Object} props - The properties object.
 * @param {number} props.stepId - The initial step ID to start the guide.
 * @param {Array} props.steps - An array of objects representing each step of the guide.
 * Each step should include necessary details such as its ID and other metadata.
 * @param {Function} props.onClose - Callback function executed when the guide is closed manually.
 * @param {Function} props.onComplete - Callback function executed when the guide is completed after the last step.
 *
 * @return {JSX.Element|null} The guide tour component that renders the step-by-step instructions,
 * or null if no steps are available or the initial conditions aren't met.
 */
export default function GuideTour({stepId, steps, onClose, onComplete}: GuideTourProps): JSX.Element | null {
  const [currentStep, setCurrentStep] = useState<number>(0);
  const [isVisible, setIsVisible] = useState<boolean>(false);
  const [rendered, setRendered] = useState<boolean>(false);
  const overlayRef: React.RefObject<HTMLDivElement | null> = useRef<HTMLDivElement>(null);
  
  const filteredSteps: GuideStep[] = React.useMemo((): GuideStep[] => {
    return steps.filter((step: GuideStep): boolean => step.id >= stepId);
  }, [steps, stepId]);
  
  const currentStepData: GuideStep = filteredSteps[currentStep];
  
  const timeoutRef: React.RefObject<NodeJS.Timeout | null> = useRef<NodeJS.Timeout | null>(null);
  
  function showStep(index: number): void {
    setIsVisible(false);
    
    if (timeoutRef.current) {
      clearTimeout(timeoutRef.current);
    }
    
    timeoutRef.current = setTimeout((): void => {
      setCurrentStep(index);
      setRendered(false);
      
      const step: GuideStep = filteredSteps[index];
      if (step?.targetSelector) {
        const element: HTMLElement | null = document.querySelector<HTMLElement>(step.targetSelector);
        if (element) {
          element.scrollIntoView({behavior: 'smooth', block: 'center'});
        }
      }
      
      timeoutRef.current = setTimeout((): void => {
        setRendered(true);
        
        timeoutRef.current = setTimeout((): void => {
          setIsVisible(true);
        }, 50);
      }, 600);
    }, 200);
  }
  
  useEffect((): () => void => {
    showStep(0);

    return (): void => {
      if (timeoutRef.current) {
        clearTimeout(timeoutRef.current);
      }
    };
  }, []);
    
    useEffect((): void => {
        if (overlayRef.current) {
            overlayRef.current.style.background = rendered ? getSpotlightBackground(currentStepData) : getOverlayColor(0.5);
            overlayRef.current.style.opacity = isVisible ? '1' : '0';
        }
    }, [rendered, isVisible, currentStepData]);
  
  function handleNext(): void {
    if (currentStep < filteredSteps.length - 1) {
      showStep(currentStep + 1);
    } else {
      onComplete();
    }
  }
  
  function handlePrevious(): void {
    if (currentStep > 0) {
      showStep(currentStep - 1);
    }
  }
  if (!filteredSteps.length || !currentStepData) {
    return null;
  }
    return createPortal(
        <div className="fixed inset-0 z-[60] font-['Lora']">
        <div
            ref={overlayRef}
            className="absolute inset-0 transition-opacity duration-500"
            onClick={onClose}
        />
        {rendered && (
            <GuidePopup
                step={currentStepData}
                isVisible={isVisible}
                currentStep={currentStep}
                totalSteps={filteredSteps.length}
                onPrevious={handlePrevious}
                onNext={handleNext}
                onClose={onClose}
            />
        )}
        </div>,
        document.body
  );
}

/**
 * Functional component that displays a guide popup. This popup includes step-based navigation,
 * title, content, and control buttons for navigating between steps or closing the popup.
 *
 * @param {object} params - The parameters for the GuidePopup component.
 * @param {GuideStep} params.step - The current guide step data, containing title and content.
 * @param {boolean} params.isVisible - Determines whether the popup is visible.
 * @param {number} params.currentStep - The index of the current step in the guide.
 * @param {number} params.totalSteps - Total number of steps in the guide.
 * @param {function} params.onPrevious - Callback invoked when navigating to the previous step.
 * @param {function} params.onNext - Callback invoked when navigating to the next step.
 * @param {function} params.onClose - Callback invoked when closing the popup.
 * @return {JSX.Element} The rendered GuidePopup component.
 */
function GuidePopup(
    {
      step,
      isVisible,
      currentStep,
      totalSteps,
      onPrevious,
      onNext,
      onClose
    }: {
      step: GuideStep;
      isVisible: boolean;
      currentStep: number;
      totalSteps: number;
      onPrevious: () => void;
      onNext: () => void;
      onClose: () => void;
    }): JSX.Element {
  const popupRef: React.RefObject<HTMLDivElement | null> = useRef<HTMLDivElement>(null);
    
    useEffect((): void => {
        if (popupRef.current) {
            const pos: PopoverPosition = getPopoverPosition(step);
            popupRef.current.style.left = pos.left;
            popupRef.current.style.top = pos.top;
            popupRef.current.style.transform = pos.transform || '';
        }
  }, [step]);
    
    return (
      <div
          ref={popupRef}
          className={`absolute bg-tertiary border border-primary/30 rounded-xl w-96 transition-opacity duration-300 ${
              isVisible ? 'opacity-100' : 'opacity-0'
          }`}
      >
          <div className="px-8 py-6 border-b border-secondary">
          <div className="flex items-start justify-between">
            <div className="flex-1 mr-6">
                <h3 className="text-text-primary font-semibold text-xl mb-3">
                {step.title}
              </h3>
              <div className="flex items-center space-x-4">
              <span className="text-primary text-sm font-medium bg-primary/10 px-3 py-1 rounded-full">
                Étape {currentStep + 1} sur {totalSteps}
              </span>
                <div className="flex items-center space-x-2">
                  {Array.from({length: totalSteps}).map((_: unknown, index: number) => (
                      <div
                          key={index}
                          className={`w-2.5 h-2.5 rounded-full transition-all duration-300 ${
                              index <= currentStep ? 'bg-primary' : 'bg-secondary'
                          }`}
                      />
                  ))}
                </div>
              </div>
            </div>
            <IconButton icon={X} variant="ghost" shape="square" onClick={onClose}/>
          </div>
        </div>
        <div className="px-8 py-8">
            <div className="text-text-secondary text-base leading-relaxed space-y-4">
            {step.content}
          </div>
        </div>
          <div className="px-8 py-6 bg-tertiary border-t border-secondary rounded-b-xl">
          <div className="flex items-center justify-between">
            {currentStep > 0 ? (
                <Button variant="ghost" size="sm" onClick={onPrevious}>
                  ← Précédent
                </Button>
            ) : (
                <div></div>
            )}
              <Button variant="primary" onClick={onNext}>
              {currentStep === totalSteps - 1 ? '🎉 Terminer' : 'Continuer →'}
              </Button>
          </div>
        </div>
      </div>
  );
}