import videojs from "video.js";
import "../../../styles/settings-popup.css";
import {
  formatQualityLabel,
  sortQualityTracks,
} from "../../../helpers/renditionHelper";
import {
  playbackSpeedIcon,
  qualityIcon,
  subtitleIcon,
  chevronLeftIcon,
  checkIcon,
} from "../../../assets/icons/index.js";
import { Logger } from "../../../utils/logger.js";

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

const AUTO_QUALITY_ID = -1;

class SettingsPopup extends Component {
  constructor(player, options) {
    super(player, options);
    this.isPopupOpen = false;
    this.currentSpeed = 1;
    this.currentRendition = this.getDefaultQuality();
    this.renditions = [];
    this.currentSubtitle = null;
    this.subtitles = [];
    this.currentMenu = "main"; // 'main', 'playback-speed', 'quality', or 'subtitles'
    this.logger = new Logger("SettingsPopup", "ui");
  }

  getDefaultQuality() {
    return {
      id: AUTO_QUALITY_ID,
      label: this.localize("auto"),
    };
  }

  getQualityLabel(track, index = 0) {
    if (track?.id === AUTO_QUALITY_ID) {
      return this.localize("auto");
    }

    return (
      formatQualityLabel(track, (key, tokens) => this.localize(key, tokens)) ||
      this.localize("qualityFallback", [index + 1])
    );
  }

  getPlaybackSpeedLabel(speed) {
    return speed === 1 ? this.localize("normal") : `${speed}x`;
  }

  getSubtitleLabel(subtitle) {
    return (
      subtitle.label ||
      subtitle.language ||
      this.localize("unknown")
    );
  }

  createEl() {
    const el = super.createEl("div", {
      className: "videojs-drm-settings-popup",
    });
    el.style.display = "none";

    return el;
  }

  ready() {
    super.ready();

    // Create main menu content
    this.createMainMenu();

    this.attachEvents();
    this.setupRenditionListener();
    this.setupSubtitleListener();
  }

  // Evaluated on demand, NOT once at ready(): Video.js selects/loads the
  // tech asynchronously after src(), so a static check at component-init
  // time can capture the wrong answer (same bug pattern as the DRM routing
  // fix in index.js — the requested option is not the actual outcome).
  // When the check was wrong here, the Shaka "loadedqualitydata" listener
  // was never attached and the quality menu permanently showed only "Auto".
  isShakaTech() {
    return this.player()?.techName_ === "Shaka";
  }

  // Main popup container
  createMainMenu() {
    const mainMenuOptions = [
      {
        action: "playback-speed",
        icon: playbackSpeedIcon,
        title: this.localize("playbackSpeed"),
        value: this.getPlaybackSpeedLabel(this.currentSpeed),
      },
      {
        action: "quality",
        icon: qualityIcon,
        title: this.localize("quality"),
        value: this.currentRendition?.label,
      },
      {
        action: "subtitles",
        icon: subtitleIcon,
        title: this.localize("Subtitles"),
        value: this.currentSubtitle
          ? this.getSubtitleLabel(this.currentSubtitle)
          : this.localize("off"),
      },
    ];

    this.mainMenuContent = mainMenuOptions
      .map(
        (option) => `
        <div class="vjsd-main-menu settings-popup-item" data-action="${option.action}">
            ${option.icon}
            <span class="settings-text settings-title">${option.title}</span>
            <span class="settings-value">${option.value}</span>
        </div>
    `
      )
      .join("");
  }

  createPlaybackSpeedMenu() {
    const speedOptions = [
      { value: 0.5, label: "0.5x", selected: this.currentSpeed === 0.5 },
      { value: 0.75, label: "0.75x", selected: this.currentSpeed === 0.75 },
      { value: 1, label: this.localize( "normal"), selected: this.currentSpeed === 1 },
      { value: 1.25, label: "1.25x", selected: this.currentSpeed === 1.25 },
      { value: 1.5, label: "1.5x", selected: this.currentSpeed === 1.5 },
      { value: 2, label: "2x", selected: this.currentSpeed === 2 },
    ];

    this.playbackSpeedMenuContent = this.createSubmenu(
      this.localize("playbackSpeed"),
      "speed",
      speedOptions,
      ["speed-item"]
    );
  }

  createQualityMenu() {
    // Add Auto option
    const qualityOptions = [];
    if (!this.renditions.some((r) => r.id == AUTO_QUALITY_ID)) {
      qualityOptions.push({
        value: AUTO_QUALITY_ID,
        label: this.localize( "auto"),
        selected: this.currentRendition?.id == AUTO_QUALITY_ID,
      });
    }

    // Add available renditions
    this.renditions.forEach((rendition) => {
      qualityOptions.push({
        value: rendition.id,
        label: rendition.label,
        selected: this.currentRendition?.id == rendition.id,
      });
    });

    this.qualityMenuContent = this.createSubmenu(
      this.localize( "quality"),
      "quality",
      qualityOptions,
      ["quality-item"]
    );
  }

  createSubtitlesMenu() {
    // Add Off option
    const subtitleOptions = [{
        value: 'off',
        label: this.localize( "off"),
        selected: !this.currentSubtitle || this.currentSubtitle.id === 'off',
    }];

    // Add available subtitles
    this.subtitles.forEach((subtitle) => {
      subtitleOptions.push({
        value: subtitle.id,
        label: this.getSubtitleLabel(subtitle),
        selected: this.currentSubtitle?.id == subtitle.id,
      });
    });

    this.subtitlesMenuContent = this.createSubmenu(
      this.localize("Subtitles"),
      "subtitle",
      subtitleOptions,
      ["subtitle-item"]
    );
  }

  createSubmenu(title, action, options, classNames) {
    return `
      ${this.createBackButton(title)}
      ${options
        .map(
          (option) => `
        <div class="settings-popup-item ${classNames.join(" ")} ${
            option.selected ? "selected" : ""
          }" data-${action}="${option.value}">
            <span class="settings-text">${option.label}</span>
            ${option.selected ? checkIcon : ""}
        </div>
        `
        )
        .join("")}
    `;
  }

  createBackButton(title) {
    return `
      <div class="settings-popup-item back-button" data-action="back">
        ${chevronLeftIcon}
        <span class="settings-text settings-title">${title}</span>
      </div>
    `;
  }

  attachEvents() {
    // Use event delegation for dynamic content
    const handleInteraction = (e) => {
      e.stopPropagation();

      const item = e.target.closest(".settings-popup-item");
      if (!item) return;

      const action = item.dataset.action;
      const speed = item.dataset.speed;
      const quality = item.dataset.quality;
      const subtitle = item.dataset.subtitle;

      if (action === "playback-speed") {
        this.showPlaybackSpeedMenu();
      } else if (action === "quality") {
        this.showQualityMenu();
      } else if (action === "subtitles") {
        this.showSubtitlesMenu();
      } else if (action === "back") {
        this.showMainMenu();
      } else if (speed) {
        this.setPlaybackSpeed(parseFloat(speed));
      } else if (quality) {
        this.setQuality(quality);
      } else if (subtitle) {
        this.setSubtitle(subtitle);
      }
    };

    // Add both click and touch events for better mobile support
    this.el().addEventListener("click", handleInteraction);
    this.el().addEventListener("touchend", (e) => {
      // Prevent click event from firing after touchend
      e.preventDefault();
      handleInteraction(e);
    });
  }

  toggle() {
    this.isPopupOpen = !this.isPopupOpen;
    if (this.isPopupOpen) {
      this.show();
    } else {
      this.hide();
    }
  }

  show() {
    this.el().style.display = "block";
    this.isPopupOpen = true;
    this.currentMenu = "main";

    this.createMainMenu();
    this.showMainMenu();

    setTimeout(() => {
      this.outsideHandler = (evt) => {
        if (!this.el().contains(evt.target)) {
          this.hide();
        }
      };
      document.addEventListener("click", this.outsideHandler);
    }, 0);
  }

  hide() {
    this.el().style.display = "none";
    this.isPopupOpen = false;
    this.currentMenu = "main";

    if (this.outsideHandler) {
      document.removeEventListener("click", this.outsideHandler);
      this.outsideHandler = null;
    }
  }

  showMainMenu() {
    this.currentMenu = "main";
    this.el().innerHTML = this.mainMenuContent;
  }

  showPlaybackSpeedMenu() {
    this.currentMenu = "playback-speed";
    // Ensure content is up to date
    this.createPlaybackSpeedMenu();
    this.el().innerHTML = this.playbackSpeedMenuContent;
  }

  showQualityMenu() {
    this.currentMenu = "quality";
    // Pull-based fallback: if the Shaka quality list is still empty at the
    // moment the user opens the menu (e.g. the initial "loadedqualitydata"
    // fired before our listener was attached, or the variant list changed),
    // ask the tech to re-emit it now. refreshQualityTracks() triggers
    // "loadedqualitydata" synchronously, so this.renditions is up to date
    // by the time the menu is built below.
    if (this.isShakaTech() && !this.renditions.some((r) => r.id != AUTO_QUALITY_ID)) {
      this._attachQualityTechListener?.();
      this.player().tech(true)?.refreshQualityTracks?.();
    }
    // Ensure content is up to date
    this.createQualityMenu();
    this.el().innerHTML = this.qualityMenuContent;
  }

  showSubtitlesMenu() {
    this.currentMenu = "subtitles";
    // Ensure content is up to date
    this.createSubtitlesMenu();
    this.el().innerHTML = this.subtitlesMenuContent;
  }

  setPlaybackSpeed(speed) {
    this.currentSpeed = speed;
    if (this.player) {
      this.player().playbackRate(speed);
    }
    // Close popup after selection
    this.hide();
  }

  setQuality(qualityId) {
    try {
      const rendition = this.renditions.find((r) => r.id == qualityId) || null;
      if (!rendition) return;

      this.currentRendition = rendition;
      if (this.isShakaTech() && this.qualitySwitchCallback) {
        this.qualitySwitchCallback(rendition.id, "video");
        return;
      }

      this.setRendition(rendition);
    } catch (error) {
      this.logger.warn("Could not set quality:", error);
    } finally {
      this.hide();
    }
  }

  setSubtitle(subtitleId) {
    if (subtitleId === "off") {
      this.currentSubtitle = null;
      this.disableSubtitles();
    } else {
      const subtitle = this.subtitles.find((s) => s.id == subtitleId);
      if (subtitle) {
        this.currentSubtitle = subtitle;
        this.enableSubtitle(subtitle);
      }
    }
    // Close popup after selection
    this.hide();
  }

  setupRenditionListener() {
    if (!this.player()) return;

    this._onLoadedQualityData = (event, { qualityData, qualitySwitchCallback }) => {
      this.qualitySwitchCallback = qualitySwitchCallback;
      this.updateRenditionsFromShaka(qualityData);
    };

    // Attach the Shaka quality listener to the *current* tech, re-attaching
    // if the tech instance changes (Video.js can swap techs on src()).
    // The old code grabbed player.tech() exactly once at ready() and only
    // when a static Shaka check passed — if the tech wasn't Shaka *yet* at
    // that moment, the listener was never attached and the menu stayed
    // "Auto"-only forever.
    this._attachQualityTechListener = () => {
      const tech = this.player().tech(true);
      if (!tech || tech === this._qualityTech) return;

      if (this._qualityTech) {
        this._qualityTech.off("loadedqualitydata", this._onLoadedQualityData);
      }
      this._qualityTech = tech;
      tech.on("loadedqualitydata", this._onLoadedQualityData);
    };

    this._attachQualityTechListener();
    this.player().on("loadstart", this._attachQualityTechListener);

    this.player().on("qualitytrackchange", (event, options) => {
      this.updateCurrentRendition(options);
    });

    // Non-Shaka (qualityLevels/VHS) sources: rebuild the list from player
    // state. Checked per event instead of once at init, for the same
    // tech-timing reason as above.
    const updateNonShakaRenditions = () => {
      if (!this.isShakaTech()) {
        this.updateRenditions();
      }
    };
    this.player().on("loadedmetadata", updateNonShakaRenditions);
    this.player().on("loadstart", updateNonShakaRenditions);
    this.player().on("qualitychange", updateNonShakaRenditions);
  }

  setupSubtitleListener() {
    if (!this.player) return;
    
    // this.player().tech().on("texttracksloaded", () => {
    //   this.updateSubtitles();
    // });

    // Listen for loadedmetadata to get subtitles
    this.player().on("loadedmetadata", () => {
      // Set timeout to ensure text tracks are loaded (for Safari)
      setTimeout(() => {
        this.updateSubtitles();
      }, 1000);
    });

    // Listen for subtitle changes from CCButton
    this.player().on('subtitlechanged', () => {
      this.updateCurrentSubtitle();
    });

    // Listen for source changes
    // this.player().on("loadstart", () => {
    //   this.updateSubtitles();
    // });

    // Listen for text track changes
    // this.player().on("texttrackchange", () => {
    //   this.updateCurrentSubtitle();
    // });
  }

  updateRenditionsFromShaka(qualityData) {
    try {
      if (qualityData?.video?.length > 0) {
        this.renditions = qualityData.video.map((track, index) => ({
          id: track.id,
          bandwidth: track.bandwidth,
          width: track.width,
          height: track.height,
          label: this.getQualityLabel(track, index),
          index: index,
          originalTrack: track,
        }));

        // Preserve the user's manual selection across list refreshes. The
        // quality list is re-emitted whenever Shaka fires "trackschanged"
        // (e.g. DRM key arrival), which can happen right after the user
        // picked a quality; unconditionally resetting here made the menu
        // fall back to "Auto" even though playback stayed on the chosen
        // track. Only fall back to Auto when the previously selected id no
        // longer exists in the new list.
        const prevId = this.currentRendition?.id;
        this.currentRendition =
          this.renditions.find((r) => r.id == prevId) ||
          this.getDefaultQuality();
      }
    } catch (error) {
      this.logger.warn("Could not update renditions from quality data:", error);
    }
  }

  updateRenditions() {
    this.logger.info("Updating renditions");
    try {
      // Try using qualityLevels API first
      if (
        this.player().qualityLevels &&
        this.player().qualityLevels()?.length > 0
      ) {
        this.renditions = Array.from(this.player().qualityLevels())
          .map((level, index) => ({
            id: `quality-${index}`,
            label:
              formatQualityLabel(level, (key, tokens) =>
                this.localize(key, tokens)
              ) || this.localize("qualityFallback", [index + 1]),
            bandwidth: level.bandwidth,
            width: level.width,
            height: level.height,
            index: index,
            qualityLevel: level,
          }))
          .sort(sortQualityTracks);

        this.logger.info(
          "Available renditions from qualityLevels:",
          this.renditions
        );
      } else if (this.player().tech()?.vhs) {
        this.logger.info("Quality levels is not usable, trying VHS...");
        const vhs = this.player().tech().vhs;

        if (vhs.representations && vhs.representations()) {
          const rawReps = vhs.representations();

          this.renditions = rawReps
            .map((rep, index) => ({
              id: `rendition-${index}`,
              label:
                formatQualityLabel(rep, (key, tokens) =>
                  this.localize(key, tokens)
                ) || this.localize("qualityFallback", [index + 1]),
              bandwidth: rep.bandwidth,
              width: rep.width,
              height: rep.height,
              resolution: rep.resolution,
              index: index,
              representation: rep,
            }))
            .sort(sortQualityTracks);

          this.logger.info("Available renditions from VHS:", this.renditions);
        }
      }
    } catch (error) {
      this.logger.warn("Could not get renditions:", error);
    } finally {
      if (!this.renditions.some((r) => r.id == AUTO_QUALITY_ID)) {
        this.renditions.unshift(this.getDefaultQuality());
      }
    }
  }

  updateCurrentRendition(options) {
    try {
      if (options && options.id !== undefined) {
        this.currentRendition =
          this.renditions.find((r) => r.id == options.id) || null;
        this.logger.info("Current rendition updated to:", this.currentRendition);
      }
    } catch (error) {
      this.logger.warn("Could not update current rendition:", error);
    }
  }

  setRendition(rendition = null) {
    try {
      const toAutoRendition = rendition.id === AUTO_QUALITY_ID;
      // Try using qualityLevels API first
      if (
        this.player().qualityLevels &&
        this.player().qualityLevels()?.length > 0
      ) {
        // Disable all quality levels first
        Array.from(this.player().qualityLevels()).forEach((level, index) => {
          level.enabled = toAutoRendition;
        });

        // Enable only the selected quality level
        if (rendition.qualityLevel) {
          rendition.qualityLevel.enabled = true;
          this.logger.info(
            "Enabled quality level:",
            rendition.qualityLevel ?? rendition.label
          );
        }
      } else if (this.player().tech()?.vhs && rendition.representation) {
        const vhs = this.player().tech().vhs;
        const reps = vhs.representations();

        reps.forEach((rep) => {
          if (rep === rendition.representation) {
            rep.enabled = true;
          } else {
            rep.enabled = toAutoRendition;
          }
        });
        this.logger.info("Set rendition to:", rendition);

        // Force VHS to update
        if (vhs.trigger) {
          vhs.trigger("renditionchange");
        }
      }
    } catch (error) {
      this.logger.warn("Could not set rendition:", error);
    }
  }

  updateSubtitles() {
    try {
      let textTracks = this.player().textTracks();

      this.subtitles = Array.from(textTracks)
        .filter(
          (track) =>
            track.kind === "captions" ||
            track.kind === "subtitles" ||
            track.kind === "subtitle"
        )
        .map((track, index) => ({
          id: track.id || `subtitle-${index}`,
          language: track.language || "unknown",
          label:
            track.label ||
            track.language ||
            this.localize("subtitleFallback", [index + 1]),
          kind: track.kind,
          index: index,
          textTrack: track,
        }));

      this.logger.info("Available subtitles:", this.subtitles);

      // Update current subtitle based on active track
      this.updateCurrentSubtitle();
    } catch (error) {
      this.logger.warn("Could not get subtitles:", error);
    }
  }

  updateCurrentSubtitle() {
    try {
      const textTracks = this.player().textTracks();
      const activeTrack = Array.from(textTracks).find(
        (track) =>
          track.mode === "showing" &&
          (track.kind === "captions" ||
            track.kind === "subtitles" ||
            track.kind === "subtitle")
      );

      if (activeTrack) {
        this.currentSubtitle = this.subtitles.find(
          (subtitle) => subtitle.textTrack === activeTrack
        );
      } else {
        this.currentSubtitle = null;
      }

      this.logger.info("Current subtitle:", this.currentSubtitle);
    } catch (error) {
      this.logger.warn("Could not update current subtitle:", error);
    }
  }

  enableSubtitle(subtitle) {
    try {
      this.logger.info("Enabling subtitle:", subtitle);

      // Disable all text tracks first
      const textTracks = this.player().textTracks();
      Array.from(textTracks).forEach((track) => {
        track.mode = "disabled";
        if (track === subtitle.textTrack) {
          track.mode = "showing";
        }
      });

      this.updateCCButtonState();
    } catch (error) {
      this.logger.warn("Could not enable subtitle:", error);
    }
  }

  disableSubtitles() {
    try {
      const textTracks = this.player().textTracks();
      Array.from(textTracks).forEach((track) => {
        if (track.kind === "captions" || track.kind === "subtitles" || track.kind === "subtitle") {
          track.mode = "disabled";
        }
      });

      this.updateCCButtonState();
    } catch (error) {
      this.logger.warn("Could not disable subtitles:", error);
    }
  }
  
  updateCCButtonState() {
    // Use VideoJS events to communicate with CCButton
    this.player().trigger('subtitlechanged');
  }

  dispose() {
    if (this._qualityTech && this._onLoadedQualityData) {
      this._qualityTech.off("loadedqualitydata", this._onLoadedQualityData);
      this._qualityTech = null;
    }
    if (this._attachQualityTechListener && this.player()) {
      this.player().off("loadstart", this._attachQualityTechListener);
    }
    super.dispose();
    this.el().remove();
  }
}

videojs.registerComponent("SettingsPopup", SettingsPopup);

export default SettingsPopup;
