import {JSX, useEffect, useMemo, useRef, useState} from 'react';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faXmark} from '@fortawesome/free-solid-svg-icons';

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 getSpotlightBackground(step: GuideStep): string {
  if (step.x !== undefined && step.y !== undefined) {
    return 'rgba(0, 0, 0, 0.5)';
  }
  if (!step.targetSelector) {
    return 'rgba(0, 0, 0, 0.5)';
  }
  const element = document.querySelector(step.targetSelector) as HTMLElement | null;
  if (!element) {
    return 'rgba(0, 0, 0, 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, rgba(0, 0, 0, 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.
 */
function getPopoverPosition(step: GuideStep): React.CSSProperties {
  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 = document.querySelector(step.targetSelector) as HTMLElement | null;
  if (!element) {
    return {
      left: '50%',
      top: '50%',
      transform: 'translate(-50%, -50%)'
    };
  }
  
  const rect: DOMRect = element.getBoundingClientRect();
  const {left, top, width, height} = rect;
  const popoverWidth = 420;
  const popoverHeight = 300;
  const margin = 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 filteredSteps: GuideStep[] = useMemo((): GuideStep[] => {
    return steps.filter((step: GuideStep): boolean => step.id >= stepId);
  }, [steps, stepId]);
  
  const currentStepData: GuideStep = filteredSteps[currentStep];
  
  const timeoutRef = useRef<NodeJS.Timeout | null>(null);
  
  const showStep = (index: number) => {
    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 = document.querySelector(step.targetSelector) as HTMLElement;
        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);
      }
    };
  }, []);
  
  const handleNext: () => void = (): void => {
    if (currentStep < filteredSteps.length - 1) {
      showStep(currentStep + 1);
    } else {
      onComplete();
    }
  };
  
  const handlePrevious: () => void = (): void => {
    if (currentStep > 0) {
      showStep(currentStep - 1);
    }
  };
  if (!filteredSteps.length || !currentStepData) {
    return null;
  }
  return (
      <div className="fixed inset-0 z-50 font-['Lora']">
        <div
            className="absolute inset-0 transition-opacity duration-500"
            style={{
              background: rendered ? getSpotlightBackground(currentStepData) : 'rgba(0, 0, 0, 0.5)',
              opacity: isVisible ? 1 : 0
            }}
            onClick={onClose}
        />
        {rendered && (
            <GuidePopup
                step={currentStepData}
                isVisible={isVisible}
                currentStep={currentStep}
                totalSteps={filteredSteps.length}
                onPrevious={handlePrevious}
                onNext={handleNext}
                onClose={onClose}
            />
        )}
      </div>
  );
}

/**
 * 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 positionStyle = useMemo(() => {
    return getPopoverPosition(step);
  }, [step]);
  
  return (
      <div
          className={`absolute bg-tertiary border border-primary/30 rounded-xl shadow-2xl w-96 transition-all duration-300 ${
              isVisible ? 'opacity-100 scale-100' : 'opacity-0 scale-95'
          }`}
          style={positionStyle}
      >
        <div className="px-8 py-6 border-b border-secondary/40">
          <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((_, index) => (
                      <div
                          key={index}
                          className={`w-2.5 h-2.5 rounded-full transition-all duration-300 ${
                              index <= currentStep ? 'bg-primary scale-110' : 'bg-secondary/60'
                          }`}
                      />
                  ))}
                </div>
              </div>
            </div>
            <button
                onClick={onClose}
                className="text-muted hover:text-text-primary transition-colors p-2 hover:bg-secondary/30 rounded-lg"
                type="button"
            >
              <FontAwesomeIcon icon={faXmark} className="text-lg"/>
            </button>
          </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-secondary/20 border-t border-secondary/30 rounded-b-xl">
          <div className="flex items-center justify-between">
            {currentStep > 0 ? (
                <button
                    onClick={onPrevious}
                    className="text-muted hover:text-text-primary text-sm px-4 py-2 rounded-lg hover:bg-secondary/30 transition-all"
                    type="button"
                >
                  ← Précédent
                </button>
            ) : (
                <div></div>
            )}
            <button
                onClick={onNext}
                className="bg-primary hover:bg-primary-dark text-text-primary px-6 py-3 rounded-lg transition-all duration-200 text-sm font-medium shadow-lg hover:shadow-xl transform hover:scale-105"
                type="button"
            >
              {currentStep === totalSteps - 1 ? '🎉 Terminer' : 'Continuer →'}
            </button>
          </div>
        </div>
      </div>
  );
}