import videojs from "video.js";
import "../../../styles/progress-bar.css";
import { formatTime } from "../../../helpers/helper";
import { Logger } from "../../../utils/logger";

const Component = videojs.getComponent("Component");

class ProgressBar extends Component {
  constructor(player, options) {
    super(player, options);

    this.logger = new Logger("ProgressBar", "ui");

    this.isDragging = false;
    this.isTouchDragging = false;
    this.tooltipTimeout = null;
  }

  createEl() {
    const el = super.createEl("div", {
      className: "custom-progress-container",
    });

    // Create progress bar HTML structure
    el.innerHTML = `
        <div class="custom-progress-loaded"></div>
        <div class="custom-progress-played"></div>
        <div class="custom-progress-handle"></div>
        <div class="custom-progress-tooltip">00:00</div>
    `;

    return el;
  }

  ready() {
    super.ready();

    this.loadedBar = this.el().querySelector(".custom-progress-loaded");
    this.playedBar = this.el().querySelector(".custom-progress-played");
    this.handle = this.el().querySelector(".custom-progress-handle");
    this.tooltip = this.el().querySelector(".custom-progress-tooltip");

    this.setupProgressBar();
  }

  // Update progress display
  updateProgress() {
    const currentTime = this.player().currentTime() || 0;
    const duration = this.player().duration() || 0;
    const buffered = this.player().buffered();

    if (duration > 0) {
      const playedPercent = (currentTime / duration) * 100;
      this.playedBar.style.width = `${playedPercent}%`;
      this.handle.style.left = `${playedPercent}%`;

      // Update loaded progress
      if (buffered.length > 0) {
        const loadedPercent =
          (buffered.end(buffered.length - 1) / duration) * 100;
        this.loadedBar.style.width = `${loadedPercent}%`;
      }
    }
  }

  // Handle click/tap to seek
  seekToPosition(e) {
    const rect = this.el().getBoundingClientRect();
    const clientX =
      e.clientX || (e.touches && e.touches[0] ? e.touches[0].clientX : 0);
    const percent = Math.max(
      0,
      Math.min(1, (clientX - rect.left) / rect.width)
    );
    const duration = this.player().duration();
    const seekTime = percent * duration;

    if (duration && !isNaN(duration)) {
      this.player().currentTime(seekTime);
      // Before the first play, scrubbing alone does not add vjs-has-started,
      // so the poster and big-play-button keep covering the tech. Treat a
      // user seek as "started" so the frame at the seeked time is visible.
      this.revealContentAfterUserSeek();
    }
  }

  /**
   * Hide poster / big-play-button after the user scrubs, so the video
   * content area shows the seeked frame instead of the initial overlay.
   */
  revealContentAfterUserSeek() {
    const player = this.player();
    if (!player) {
      return;
    }

    if (typeof player.hasStarted === "function" && !player.hasStarted()) {
      player.hasStarted(true);
    }
  }

  // Update tooltip based on mouse/touch position
  updateTooltipPosition(e) {
    const rect = this.el().getBoundingClientRect();
    const clientX =
      e.clientX || (e.touches && e.touches[0] ? e.touches[0].clientX : 0);
    const percent = Math.max(
      0,
      Math.min(1, (clientX - rect.left) / rect.width)
    );
    const duration = this.player().duration();

    if (duration && !isNaN(duration)) {
      const seekTime = percent * duration;
      this.tooltip.textContent = formatTime(seekTime);

      // Position tooltip at mouse/touch position
      const leftPercent = percent * 100;
      this.tooltip.style.left =
        leftPercent < 1
          ? "3px"
          : leftPercent > 99
          ? "calc(100% - 3px)"
          : `${leftPercent}%`;
    }
  }

  // Show tooltip
  showTooltip(e) {
    clearTimeout(this.tooltipTimeout);
    this.tooltip.style.opacity = "1";
    this.tooltip.style.visibility = "visible";
    this.updateTooltipPosition(e);
  }

  // Hide tooltip with delay
  hideTooltip(delay = 0) {
    clearTimeout(this.tooltipTimeout);
    this.tooltipTimeout = setTimeout(() => {
      this.tooltip.style.opacity = "0";
      this.tooltip.style.visibility = "hidden";
    }, delay);
  }

  // Handle drag to seek
  handleDrag(e) {
    if (this.isDragging || this.isTouchDragging) {
      this.seekToPosition(e);
    }
    this.updateTooltipPosition(e);
  }

  handleMouseUp() {
    this.isDragging = false;
    if (!this.el().matches(":hover")) {
      this.hideTooltip();
    }
  }

  setupProgressBar() {
    // Bind methods to keep context
    this.boundHandleDrag = this.handleDrag.bind(this);
    this.boundHandleMouseUp = this.handleMouseUp.bind(this);

    // Mouse events
    this.el().addEventListener("click", (e) => this.seekToPosition(e));

    this.el().addEventListener("mousedown", (e) => {
      this.isDragging = true;
      this.seekToPosition(e);
    });

    // Bind handleDrag with this context
    document.addEventListener("mousemove", this.boundHandleDrag);
    document.addEventListener("mouseup", this.boundHandleMouseUp);

    // Touch events for mobile
    this.el().addEventListener("touchstart", (e) => {
      e.preventDefault();
      this.isTouchDragging = true;
      this.showTooltip(e);
      this.seekToPosition(e);
    });

    this.el().addEventListener("touchmove", (e) => {
      e.preventDefault();
      if (this.isTouchDragging) {
        this.handleDrag(e);
      }
    });

    this.el().addEventListener("touchend", (e) => {
      e.preventDefault();
      this.isTouchDragging = false;
      // Hide tooltip after a delay on touch end
      this.hideTooltip(1000);
    });

    // Handle touch cancel
    this.el().addEventListener("touchcancel", (e) => {
      e.preventDefault();
      this.isTouchDragging = false;
      this.hideTooltip();
    });

    // Show tooltip on hover with position tracking
    this.el().addEventListener("mouseenter", (e) => {
      this.showTooltip(e);
    });

    this.el().addEventListener("mouseleave", () => {
      if (!this.isDragging) {
        this.hideTooltip();
      }
    });

    // Listen to player events
    this.player().on("timeupdate", () => this.updateProgress());
    this.player().on("progress", () => this.updateProgress());
    this.player().on("loadedmetadata", () => this.updateProgress());
    this.player().on("durationchange", () => this.updateProgress());
  }

  dispose() {
    // Clear tooltip timeout
    if (this.tooltipTimeout) {
      clearTimeout(this.tooltipTimeout);
      this.tooltipTimeout = null;
    }

    // Remove event listeners of document
    if (this.boundHandleDrag) {
      document.removeEventListener("mousemove", this.boundHandleDrag);
    }
    if (this.boundHandleMouseUp) {
      document.removeEventListener("mouseup", this.boundHandleMouseUp);
    }

    // Events are removed as components are disposed

    super.dispose();
  }
}

// Register the component
videojs.registerComponent("ProgressBar", ProgressBar);

export default ProgressBar;
