import { OnboardingStep, OnboardingOptions } from "./types";
import confetti from "canvas-confetti";

export class Onboarding {
  private steps: OnboardingStep[];
  private options: OnboardingOptions;
  private currentStep = -1;
  private overlay: HTMLElement | null = null;
  private highlightedElement: HTMLElement | null = null;

  constructor(steps: OnboardingStep[], options: OnboardingOptions = {}) {
    this.steps = steps;
    this.options = {
      overlayColor: "rgba(0, 0, 0, 0.7)",
      textColor: "#333",
      backgroundColor: "#fff",
      allowSkip: true,
      skipOnMissingElement: true,
      ...options,
    };
  }

  public start(): void {
    this.createOverlay();
    this.next();
    this.setupKeyboardNavigation();
  }

  private createOverlay(): void {
    this.overlay = document.createElement("div");
    this.overlay.style.position = "fixed";
    this.overlay.style.top = "0";
    this.overlay.style.left = "0";
    this.overlay.style.width = "100%";
    this.overlay.style.height = "100%";
    this.overlay.style.backgroundColor = this.options.overlayColor!;
    this.overlay.style.zIndex = "10000";
    document.body.appendChild(this.overlay);
  }

  public next(): void {
    // Clear current step first
    this.clearHighlight();

    // Move to next step
    this.currentStep++;

    // Check if we've completed all steps
    if (this.currentStep >= this.steps.length) {
      this.complete();
      return;
    }

    // Show the next step
    this.showStep();
  }

  private handleKeyDown = (e: KeyboardEvent): void => {
    if (e.key === "Escape" && this.options.allowSkip) {
      this.complete();
    } else if (e.key === "ArrowRight") {
      this.next();
    } else if (e.key === "ArrowLeft" && this.currentStep > 0) {
      this.currentStep -= 2;
      this.next();
    }
  };

  private setupKeyboardNavigation(): void {
    if (!this.options.allowKeyboardNavigation) return;

    document.addEventListener("keydown", this.handleKeyDown);
  }

  private showStep(): void {
    const step = this.steps[this.currentStep];
    const element = document.querySelector(step.element);

    if (!element && this.options.skipOnMissingElement) {
      this.next();
      return;
    }

    if (!element) {
      console.warn(`Element not found for step: ${step.title}`);
      return;
    }

    // Clear previous highlights/tooltips
    this.clearHighlight();
    this.highlightElement(element as HTMLElement, step);
    // Highlight target element
    (element as HTMLElement).style.position = "relative";
    (element as HTMLElement).style.zIndex = "10001";
    // (element as HTMLElement).style.boxShadow = "0 0 0 4px rgba(255, 0, 0, 0.7)";
    (element as HTMLElement).style.transition = "box-shadow 0.3s ease";

    // Create tooltip container
    const tooltip = document.createElement("div");
    tooltip.style.position = "absolute";
    tooltip.style.zIndex = "10002";
    tooltip.style.minWidth = "250px";
    tooltip.style.maxWidth = "350px";
    tooltip.style.pointerEvents = "auto"; // Ensure tooltip is interactive

    // Position tooltip based on step.position or default to 'bottom'
    const position = step.position || "bottom";
    this.positionTooltip(tooltip, element as HTMLElement, position);

    // Determine step positions
    const isFirstStep = this.currentStep === 0;
    const isLastStep = this.currentStep === this.steps.length - 1;

    // Get the tooltip container we created in positionTooltip
    const tooltipContainer = tooltip.querySelector("div") as HTMLElement;

    // Tooltip content - now includes skip button in the same position as previous button would be
    tooltipContainer.innerHTML = `
        <div style="position: relative;">
                              ${
                                step.media ? this.renderMedia(step.media) : ""
                              }   
        <h3 style="margin: 0 0 8px 0; font-size: 18px;">${step.title}</h3>
            <p style="margin: 0 0 16px 0; font-size: 14px;">${
              step.description
            }</p>
            <div style="display: flex; justify-content: space-between; gap: 8px;">
                ${
                  isFirstStep
                    ? `<button class="onboarding-skip-btn" style="flex: 1; padding: 4px 8px; background:rgb(12, 12, 12); color: white; border: none; border-radius: 4px; cursor: pointer;">Skip</button>`
                    : !isFirstStep
                    ? `<button class="onboarding-prev-btn" style="flex: 1; padding: 4px 8px; background:rgb(7, 7, 7); color: white; border: none; border-radius: 4px; cursor: pointer;">Prev</button>`
                    : ""
                }
                <button class="onboarding-next-btn" style="flex: 1; padding: 4px 8px; background:rgb(10, 10, 10); color: white; border: none; border-radius: 4px; cursor: pointer;">
                    ${isLastStep ? "Finish" : "Next"}
                </button>
            </div>
        </div>
    `;

    // Add event listeners
    tooltip
      .querySelector(".onboarding-next-btn")
      ?.addEventListener("click", () => {
        if (isLastStep) {
          this.complete();
        } else {
          this.next();
        }
      });

    if (isFirstStep) {
      tooltip
        .querySelector(".onboarding-skip-btn")
        ?.addEventListener("click", () => {
          this.complete();
        });
    } else {
      tooltip
        .querySelector(".onboarding-prev-btn")
        ?.addEventListener("click", () => {
          this.currentStep -= 2;
          this.next();
        });
    }

    document.body.appendChild(tooltip);
  }

  private renderMedia(media: {
    type: string;
    url: string;
    alt?: string;
  }): string {
    if (
      media.type === "video" &&
      (media.url.includes("youtube.com") || media.url.includes("youtu.be"))
    ) {
      let embedUrl = media.url;

      // Convert youtu.be URLs to embed format
      if (media.url.includes("youtu.be")) {
        const videoId = media.url.split("/").pop()?.split("?")[0]; // Extract video ID
        embedUrl = `https://www.youtube.com/embed/${videoId}`;
      }

      // Convert youtube.com URLs to embed format
      if (media.url.includes("youtube.com")) {
        embedUrl = media.url.replace("watch?v=", "embed/").split("&")[0]; // Remove query parameters
      }

      // Render YouTube video using an iframe
      return `
        <iframe 
          src="${embedUrl}" 
          style="width: 100%; height: 200px; border: none; border-radius: 4px;" 
          allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" 
          allowfullscreen>
        </iframe>
      `;
    }

    switch (media.type) {
      case "image":
        return `<img src="${media.url}" alt="${
          media.alt || "Media"
        }" style="max-width: 100%; height: 100px; margin: 16px 0; border-radius: 4px;" />`;
      case "gif":
        return `<img src="${media.url}" alt="${
          media.alt || "Media"
        }" style="max-width: 100%; margin: 16px 0; border-radius: 4px;" />`;
      case "video":
        return `<video src="${media.url}" controls style="max-width: 100%; margin: 16px 0; border-radius: 4px;"></video>`;
      default:
        return "";
    }
  }

  private highlightElement(element: HTMLElement, step: OnboardingStep): void {
    this.highlightedElement = element;

    const highlightStyle = step.highlightStyle || "solid";
    const borderRadius = parseInt("4", 10);

    element.style.position = "relative";
    element.style.zIndex = "10001";
    element.style.transition = `all 300ms ease`;

    switch (highlightStyle) {
      case "dotted":
        element.style.border = `2px dotted rgba(255, 255, 255, 0.9)`;
        break;
      case "dashed":
        element.style.border = `2px dashed rgba(255, 255, 255, 0.9)`;
        break;
      case "pulse":
        element.style.boxShadow = `0 0 0 4px rgba(255, 0, 0, 0.7)`;
        element.style.animation = `pulse ${300 * 2}ms infinite`;
        this.addPulseAnimation();
        break;
      case "glow":
        element.style.boxShadow = `0 0 8px 8px rgba(255, 200, 0, 0.5)`;
        break;
      default: // solid
        element.style.boxShadow = `0 0 0 4px rgba(255, 0, 0, 0.7)`;
    }

    if (borderRadius > 0) {
      element.style.borderRadius = `${borderRadius}px`;
    }
  }

  private addPulseAnimation(): void {
    const style = document.createElement("style");
    style.textContent = `
      @keyframes pulse {
        0% { box-shadow: 0 0 0 4px rgba(255, 0, 0, 0.7); }
        50% { box-shadow: 0 0 0 8px rgba(255, 0, 0, 0.4); }
        100% { box-shadow: 0 0 0 4px rgba(255, 0, 0, 0.7); }
      }
    `;
    document.head.appendChild(style);
  }

  private positionTooltip(
    tooltip: HTMLElement,
    element: HTMLElement,
    position: string
  ): void {
    const rect = element.getBoundingClientRect();
    const arrowSize = 20;
    const tooltipMargin = 10;

    // Create tooltip container
    const tooltipContainer = document.createElement("div");
    tooltipContainer.style.position = "relative";
    tooltipContainer.style.backgroundColor = this.options.backgroundColor!;
    tooltipContainer.style.color = this.options.textColor!;
    tooltipContainer.style.padding = "16px";
    tooltipContainer.style.borderRadius = "8px";
    tooltipContainer.style.boxShadow = "0 4px 12px rgba(0,0,0,0.15)";
    tooltipContainer.style.border = "1px solid rgba(0,0,0,0.1)";

    // Create a unique class name for this tooltip's arrow
    const arrowClass = `onboarding-tooltip-arrow-${Math.random()
      .toString(36)
      .substr(2, 9)}`;

    // Add base arrow styles
    const arrowStyles = document.createElement("style");
    arrowStyles.textContent = `
      .${arrowClass}::after {
        content: '';
        position: absolute;
        width: ${arrowSize}px;
        height: ${arrowSize}px;
        background: ${this.options.backgroundColor};
        border: inherit;
        z-index: -1;
      }
    `;
    document.head.appendChild(arrowStyles);
    tooltipContainer.classList.add(arrowClass);

    // Position the tooltip and configure arrow based on position
    switch (position) {
      case "top":
        tooltip.style.bottom = `${
          window.innerHeight - rect.top + tooltipMargin
        }px`;
        tooltip.style.left = `${rect.left + rect.width / 2}px`;
        tooltip.style.transform = "translateX(-50%)";

        const topArrowStyles = document.createElement("style");
        topArrowStyles.textContent = `
          .${arrowClass}::after {
            bottom: -${arrowSize / 2}px;
            left: 50%;
            transform: translateX(-50%) rotate(45deg);
            border-top: none;
            border-left: none;
          }
        `;
        document.head.appendChild(topArrowStyles);
        break;

      case "left":
        tooltip.style.top = `${rect.top + rect.height / 2}px`;
        tooltip.style.right = `${
          window.innerWidth - rect.left + tooltipMargin
        }px`;
        tooltip.style.transform = "translateY(-50%)";

        const leftArrowStyles = document.createElement("style");
        leftArrowStyles.textContent = `
          .${arrowClass}::after {
            right: -${arrowSize / 2}px;
            top: 50%;
            transform: translateY(-50%) rotate(45deg);
            border-top: none;
            border-right: none;
          }
        `;
        document.head.appendChild(leftArrowStyles);
        break;

      case "right":
        tooltip.style.top = `${rect.top + rect.height / 2}px`;
        tooltip.style.left = `${rect.right + tooltipMargin}px`;
        tooltip.style.transform = "translateY(-50%)";

        const rightArrowStyles = document.createElement("style");
        rightArrowStyles.textContent = `
          .${arrowClass}::after {
            left: -${arrowSize / 2}px;
            top: 50%;
            transform: translateY(-50%) rotate(45deg);
            border-bottom: none;
            border-left: none;
          }
        `;
        document.head.appendChild(rightArrowStyles);
        break;

      case "bottom":
      default:
        tooltip.style.top = `${rect.bottom + tooltipMargin}px`;
        tooltip.style.left = `${rect.left + rect.width / 2}px`;
        tooltip.style.transform = "translateX(-50%)";

        const bottomArrowStyles = document.createElement("style");
        bottomArrowStyles.textContent = `
          .${arrowClass}::after {
            top: -${arrowSize / 2}px;
            left: 50%;
            transform: translateX(-50%) rotate(45deg);
            border-right: none;
            border-bottom: none;
          }
        `;
        document.head.appendChild(bottomArrowStyles);
        break;
    }

    tooltip.appendChild(tooltipContainer);
  }
  private clearHighlight(): void {
    // Remove all tooltips
    document
      .querySelectorAll('[style*="z-index: 10002"]')
      .forEach((el) => el.remove());

    // Reset highlighted elements
    document.querySelectorAll('[style*="z-index: 10001"]').forEach((el) => {
      const element = el as HTMLElement;
      element.style.position = ""; // Reset position
      element.style.zIndex = ""; // Reset zIndex
      element.style.boxShadow = ""; // Reset boxShadow
      element.style.border = ""; // Reset border (for dotted/dashed styles)
      element.style.animation = ""; // Reset animation (for pulse style)
      element.style.borderRadius = ""; // Reset borderRadius
    });

    // Clear the reference to the highlighted element
    this.highlightedElement = null;
  }

  private complete(): void {
    this.clearHighlight();

    // Remove the keyboard navigation event listener
    document.removeEventListener("keydown", this.handleKeyDown);

    if (this.overlay) {
      console.log("Removing overlay:", this.overlay);
      document.body.removeChild(this.overlay);
      this.overlay = null;
    }

    // Forcefully remove any lingering overlays
    document.querySelectorAll('div[style*="position: fixed"]').forEach((el) => {
      console.warn("Forcefully removing lingering overlay:", el);
      el.remove();
    });

    setTimeout(() => this.launchConfetti(), 200); // 🎉 Then show confetti!
    this.options.onComplete?.();
  }

  private launchConfetti(): void {
    const duration = 2 * 1000;
    const end = Date.now() + duration;

    (function frame() {
      // @ts-ignore
      confetti({
        particleCount: 5,
        angle: 60,
        spread: 55,
        origin: { x: 0 },
      });
      // @ts-ignore
      confetti({
        particleCount: 5,
        angle: 120,
        spread: 55,
        origin: { x: 1 },
      });

      if (Date.now() < end) {
        requestAnimationFrame(frame);
      }
    })();
  }
}
