import videojs from "video.js";
import "video.js/dist/video-js.css";
import { setupDRM } from "./drm-config";
import CustomControlBar from "./components/CustomControlBar";
import ErrorOverlay from "./components/ErrorOverlay";
import Shaka from "./shaka/shaka.js";
import "./styles/styles.css";
import {
  clearPlayerError,
  normalizePlayerError,
  reportPlayerError,
} from "./helpers/errorMessages.js";
import {
  DISPLAY_FORMAT,
  DISPLAY_FORMAT_VALUES,
} from "./constants/displayFormat.js";
import { detectSourceType } from "./helpers/helper.js";
import { setLogLevel, Logger } from "./utils/logger.js";
import {
  applyPlayerLanguage,
  registerAllLanguages,
  resolveLanguage,
} from "./lang";

registerAllLanguages();

// Define default values for the plugin's `state` object here.
Shaka.defaultState = {};

// Register tech with Video.js
videojs.registerTech("shaka", Shaka);
videojs.registerComponent("CustomControlBar", CustomControlBar);
videojs.registerComponent("ErrorOverlay", ErrorOverlay);

class VideoJSDRMPlayer {
  constructor(options = {}) {
    // Validate required parameters
    if (!options.containerId) {
      throw new Error("containerId parameter is required");
    }

    if (!options.source || typeof options.source !== "string" || !options.source.trim()) {
      throw new Error("source parameter is required");
    }

    // Check if drmConfig is required
    if (
      options.isDRM &&
      (!options.drmConfig || !Object.keys(options.drmConfig).length)
    ) {
      throw new Error("drmConfig parameter is required if isDRM is true");
    }

    this.options = {
      // Default options
      totalWidth: "100%",
      totalHeight:
        options.audioOnlyMode && options.classicControls ? "" : "auto",
      isDRM: false,
      useShakaTech: false,
      displayFormat: DISPLAY_FORMAT.DEFAULT,
      audioOnlyMode: false,
      classicControls: false,
      controls: true,
      logLevel: 1,
      language: "ja",
      // Merge with user options
      ...options,
    };

    this.options.language = resolveLanguage(this.options.language);

    // Set global log level
    if (this.options.logLevel !== undefined) {
      setLogLevel(this.options.logLevel);
    }

    // Create logger instance for this player
    this.logger = new Logger(`VideoJS-${options.containerId}`);

    this.containerId = options.containerId;
    this.source = options.source;
    this.isDRM = options.isDRM ?? false;
    this.player = null;

    // Create video element
    this.createVideoElement();

    // Initialize Video.js player
    this.initializePlayer();
  }

  createVideoElement() {
    const containerElement = document.getElementById(this.containerId);
    if (!containerElement) {
      throw new Error(
        `Container element with id "${this.containerId}" not found`
      );
    }

    // Create video element inside container
    const videoElement = document.createElement("video");
    videoElement.id = `${this.containerId}-video`;
    videoElement.className = "video-js vjs-default-skin";
    if (!this.options.classicControls) {
      videoElement.classList.add("hide-videojs-controls");
    }
    videoElement.setAttribute("playsinline", true);
    videoElement.setAttribute("webkit-playsinline", true);
    videoElement.setAttribute("x-webkit-airplay", "allow");

    if (this.options.totalWidth) {
      videoElement.style.width = this.options.totalWidth;
    }
    if (this.options.totalHeight) {
      videoElement.style.height = this.options.totalHeight;
    }

    // Clear container and add video element
    containerElement.innerHTML = "";
    containerElement.appendChild(videoElement);

    this.dispose();

    this.videoElement = videoElement;

    if (this.options.audioOnlyMode && !this.options.classicControls) {
      this.videoElement.classList.add("audio-only");

      if (DISPLAY_FORMAT_VALUES.includes(this.options.displayFormat)) {
        this.videoElement.classList.add(
          `display-format-${this.options.displayFormat}`
        );

        if (this.options.displayFormat === DISPLAY_FORMAT.HIDDEN) {
          this.videoElement.style.display = "none";
        }
      } else {
        this.logger.warn(`Unknown displayFormat: ${this.options.displayFormat}`);
      }
    }
  }

  initializePlayer() {
    const playerOptions = {
      ...this.getVideoJSOptions(),
      techOrder: this.options.useShakaTech ? ["shaka", "html5"] : ["html5"],
    };

    try {
      this.player = videojs(this.videoElement, playerOptions);
      applyPlayerLanguage(this.player, this.options.language);

      if (!this.player.getChild("ErrorOverlay")) {
        this.player.addChild("ErrorOverlay");
      }
    } catch (error) {
      this.logger.error("Failed to initialize Video.js player:", error);
      return;
    }

    if (!this.player) {
      throw new Error("Failed to initialize Video.js player");
    }

    if (this.options.fluid) {
      this.videoElement.classList.add("vjs-fluid");
      this.player.fluid(true);
    }

    window.addEventListener("beforeunload", function () {
      this.dispose();
    });

    this.player.on("error", () => {
      const payload = normalizePlayerError(this.player.error());
      this.logger.error("Player error:", payload);
      this.emit("error", payload);
    });

    // Add ready event
    this.player.ready(() => {
      this.logger.info("Video.js Player is ready");

      if (this.options.useShakaTech && !Shaka.isSupported()) {
        this._handlePlayerError(
          new Error("Shaka Player is not supported in this browser")
        );
        this.emit("ready");
        return;
      }

      // Set container ID to Shaka tech for instance exposure
      if (this.options.useShakaTech && this.player.tech()?.setContainerId) {
        this.player.tech().setContainerId(this.containerId);
      }

      // Setup DRM if needed
      try {
        if (!this.options.useShakaTech && this.isDRM) {
          setupDRM(
            this.player,
            {
              url: this.source,
              type: detectSourceType(this.source),
            },
            this.options.drmConfig,
            this.logger,
            this.containerId
          );
        } else {
          this.player.src({
            src: this.source,
            type: detectSourceType(this.source),
          });
        }
      } catch (error) {
        this.logger.error("Error setting up source:", error);
        this._handlePlayerError(error);
      }

      this.emit("ready");

      if (!this.options.classicControls && this.options.controls) {
        this.player.addChild("CustomControlBar");
      }

      // Emit custom ended event when playback finishes
      this.player.on("ended", () => {
        // this.logger.info("Playback ended");
        this.emit("ended");
      });

      // Detect audio-only after metadata is available
      this.player.on("loadedmetadata", () => this._applyAudioOnlyPlayButtonPoster());
    });
  }

  /**
   * Apply poster to play button for audio-only + PLAY_PAUSE_ONLY mode.
   * Used on initial load and after loading a new source (when control bar is recreated).
   */
  _applyAudioOnlyPlayButtonPoster() {
    // Wait for 500ms to ensure metadata is loaded (for Safari)
    setTimeout(() => {
      try {
        if (
          !this.options.classicControls &&
          this.options.audioOnlyMode &&
          this.options.controls &&
          this.options.displayFormat === DISPLAY_FORMAT.PLAY_PAUSE_ONLY &&
          this.options.poster
        ) {
          const playButton = this.player
            .el()
            .querySelector(".videojs-drm-play-button");
          if (playButton) {
            playButton.style.background = null;
            playButton.style.backgroundRepeat = "no-repeat";
            playButton.style.backgroundPosition = "center";
            playButton.style.backgroundSize = "cover";
            playButton.style.backgroundImage = `url(${this.options.poster})`;
          }
        }
      } catch (e) {
        this.logger.error("Error applying audio-only mode:", e);
      }
    }, 500);
  }

  getVideoJSOptions() {
    const videojsOptions = { ...this.options };

    // Remove our custom options
    delete videojsOptions.containerId;
    delete videojsOptions.source;
    delete videojsOptions.isDRM;
    delete videojsOptions.totalWidth;
    delete videojsOptions.totalHeight;
    delete videojsOptions.displayFormat;
    delete videojsOptions.classicControls;
    delete videojsOptions.logLevel;
    if (this.options.useShakaTech && this.options.isDRM) {
      videojsOptions.shaka = {
        drmConfig: this.options.drmConfig,
      };
    } else {
      delete videojsOptions.drmConfig;
    }

    return videojsOptions;
  }

  _handlePlayerError(error) {
    if (!this.player) {
      return;
    }
    reportPlayerError(this.player, error);
  }

  _hidePlayerError() {
    clearPlayerError(this.player);
  }

  // Custom event system
  emit(eventName, data = null) {
    const event = new CustomEvent(
      `${this.containerId}-videojs-drm-player:${eventName}`,
      {
        detail: data,
      }
    );
    document.dispatchEvent(event);
  }

  on(eventName, callback) {
    document.addEventListener(
      `${this.containerId}-videojs-drm-player:${eventName}`,
      callback
    );
  }

  // Public API methods
  play() {
    if (this.player) {
      return this.player.play();
    }
  }

  pause() {
    if (this.player) {
      this.player.pause();
    }
  }

  stop() {
    if (this.player) {
      this.player.pause();
      this.player.currentTime(0);
    }
  }

  rewind(seconds = 10) {
    if (this.player) {
      const currentTime = this.player.currentTime();
      this.player.currentTime(Math.max(0, currentTime - seconds));
    }
  }

  forward(seconds = 10) {
    if (this.player) {
      const currentTime = this.player.currentTime();
      const duration = this.player.duration();
      this.player.currentTime(Math.min(duration, currentTime + seconds));
    }
  }

  setVolume(volume) {
    if (this.player && volume >= 0 && volume <= 1) {
      this.player.volume(volume);
    }
  }

  getVolume() {
    return this.player ? this.player.volume() : 0;
  }

  isMuted() {
    return this.player ? this.player.muted() : false;
  }

  mute() {
    if (this.player) {
      this.player.muted(true);
    }
  }

  unmute() {
    if (this.player) {
      this.player.muted(false);
    }
  }

  toggleMute() {
    if (this.player) {
      this.player.muted(!this.player.muted());
    }
  }

  getCurrentTime() {
    return this.player ? this.player.currentTime() : 0;
  }

  getDuration() {
    return this.player ? this.player.duration() : 0;
  }

  seek(time) {
    if (this.player && time >= 0) {
      this.player.currentTime(time);
    }
  }

  setSize(width, height) {
    if (this.player) {
      this.player.dimensions(width, height);
      this.options.width = width;
      this.options.height = height;
    }
  }

  /**
   * Remove existing CustomControlBar (if any) and add a new one.
   * Used when loading a new source so controls reflect the new media.
   * Also wires one-time loadedmetadata to apply audio-only play button poster for the new source.
   */
  _recreateCustomControlBar() {
    if (this.options.classicControls || !this.options.controls) {
      return;
    }
    const existing = this.player.getChild && this.player.getChild("CustomControlBar");
    if (existing) {
      this.player.removeChild(existing);
      if (typeof existing.dispose === "function") {
        existing.dispose();
      }
    }
    this.player.addChild("CustomControlBar");
    this.logger.info("Custom control bar recreated for new source");

    // Apply audio-only play button poster when new source metadata is loaded (same as initial load)
    this.player.one("loadedmetadata", () => this._applyAudioOnlyPlayButtonPoster());
  }

  /**
   * Load a new source into the current player instance.
   * Only updates source and DRM configuration; all other options remain unchanged.
   * Recreates the custom control bar for the new source.
   *
   * @param {string} source - New media source URL
   * @param {object} drmConfig - New DRM configuration (optional)
   */
  loadSource(source, drmConfig = this.options.drmConfig) {
    if (!source) {
      throw new Error("source parameter is required");
    }

    if (!this.player) {
      this.logger.warn("Cannot load source: player is not initialized");
      this._handlePlayerError(
        new Error("Cannot load source: player is not initialized")
      );
      return;
    }

    this._hidePlayerError();

    // Update only fields related to source & DRM
    this.source = source;
    if (drmConfig) {
      this.isDRM = true;
      this.options.isDRM = this.isDRM;
      this.options.drmConfig = drmConfig;
    }

    try {
      const type = detectSourceType(this.source);

      // Shaka tech path
      if (this.options.useShakaTech) {
        const tech = this.player.tech && this.player.tech();

        // Ensure Shaka tech sees the updated DRM config
        if (tech && tech.options_ && this.isDRM && this.options.drmConfig) {
          tech.options_.drmConfig = this.options.drmConfig;
        }

        // Delegate to Video.js; Shaka tech's setSrc will be called internally
        this.player.src({
          src: this.source,
          type,
        });
        this._recreateCustomControlBar();
        return;
      }

      // HTML5 + DRM path
      if (this.isDRM && this.options.drmConfig) {
        setupDRM(
          this.player,
          {
            url: this.source,
            type,
          },
          this.options.drmConfig,
          this.logger,
          this.containerId
        );
        this._recreateCustomControlBar();
        return;
      }

      // Plain HTML5 (no DRM)
      this.player.src({
        src: this.source,
        type,
      });
      this._recreateCustomControlBar();
    } catch (error) {
      this.logger.error("Error loading new source:", error);
      this._handlePlayerError(error);
    }
  }

  // Cleanup method
  dispose() {
    if (this.player && !this.player.isDisposed()) {
      this.player.dispose();
      // this.player = null;
    }

    // Don't remove the video element since it's provided by the user
    this.videoElement = null;
  }
}

// Export for different module systems
export default VideoJSDRMPlayer;

// Also make it available globally if not using modules
if (typeof window !== "undefined") {
  window.VideoJSDRMPlayer = VideoJSDRMPlayer;
}
