import React, {
  useState,
  useRef,
  useEffect,
  useContext,
  useMemo,
  useCallback,
} from "react";
import { OutletContext } from "../AppMainLayout";
import FullPageLoader from "../fullPageLoader";
import screenfull from "screenfull";
import Hls from "hls.js";
import {
  LoadingScreen,
  RangeContainer,
  VideoController,
  VideoPlayerContainer,
} from "./index.styled";
import PlayIcon from "./icons/playVideoIcon";
import PauseIcon from "./icons/pause";
import useTranslation from "../../hooks/useTranslation.jsx";
import wordStore from "../../mc/wordStore.json";
import ZoomInIcon from "./icons/zoomIn.jsx";
import ZoomOutIcon from "./icons/zoomOut.jsx";
import useMediaServiceV2 from "../../hooks/useMediaServiceV2";
import MediaError from "../mediaError";

/**
 * =========================
 * Global (module) de-dupe
 * =========================
 */
const _inflightResolve = new Map();
const _resolvedCache = new Map();

function _extractUrl(res) {
  if (!res) return null;
  if (typeof res === "string") return res;
  if (typeof res?.url === "string") return res.url;
  if (typeof res?.data?.url === "string") return res.data.url;
  if (typeof res?.data?.data?.url === "string") return res.data.data.url;
  if (typeof res?.result?.url === "string") return res.result.url;
  return null;
}

function _normalizeUrl(u) {
  if (!u || u === "undefined" || u === "null") return null;
  if (typeof u === "string" && u.trim() === "") return null;
  return u;
}

function _parsePresignedExpiry(url) {
  try {
    const u = new URL(url);
    const expiresSec = Number(u.searchParams.get("X-Amz-Expires"));
    const dateStr = u.searchParams.get("X-Amz-Date");
    if (!expiresSec || !dateStr) return null;

    const yyyy = Number(dateStr.slice(0, 4));
    const mm = Number(dateStr.slice(4, 6));
    const dd = Number(dateStr.slice(6, 8));
    const HH = Number(dateStr.slice(9, 11));
    const MM = Number(dateStr.slice(11, 13));
    const SS = Number(dateStr.slice(13, 15));
    const start = Date.UTC(yyyy, mm - 1, dd, HH, MM, SS);

    return start + expiresSec * 1000;
  } catch {
    return null;
  }
}

function _isCacheFresh(entry) {
  if (!entry?.url) return false;
  if (entry.expiresAt == null) return true;
  return entry.expiresAt - Date.now() > 30_000;
}

function _isPresignedUrl(u) {
  if (!u) return false;
  if (u.startsWith("blob:")) return true;
  return /X-Amz-Algorithm=|X-Amz-Signature=|X-Amz-Credential=|digitaloceanspaces\.com/i.test(
    u,
  );
}

function _withAccountParam(u, accountId) {
  if (!u || !accountId) return u;
  if (/_account=/.test(u)) return u;
  const sep = u.includes("?") ? "&" : "?";
  return `${u}${sep}_account=${encodeURIComponent(accountId)}`;
}

async function resolveUrlDeduped(fileId, resolver, cacheKey) {
  if (!fileId) return null;

  const cached = _resolvedCache.get(cacheKey);
  if (_isCacheFresh(cached)) return cached.url;

  if (_inflightResolve.has(cacheKey)) return _inflightResolve.get(cacheKey);

  const p = (async () => {
    try {
      const res = await resolver(fileId);
      const url = _normalizeUrl(_extractUrl(res));
      if (!url) return null;

      const expiresAt = _parsePresignedExpiry(url);
      _resolvedCache.set(cacheKey, { url, expiresAt });
      return url;
    } catch {
      return null;
    }
  })().finally(() => {
    _inflightResolve.delete(cacheKey);
  });

  _inflightResolve.set(cacheKey, p);
  return p;
}

const VideoPlayer2 = ({
  width,
  height = "360px",
  style,
  hideRange,
  controlSize,
  src,
  streamUrl,
  fileId,
  small,
  onFullModeClick,
  debug = false,
  onDetail = () => {},
  errorBgColor = "#F5F7F7",
  errorReturnNull = false,
  onError = () => false,
}) => {
  const { findText } = useTranslation(wordStore);
  const { accessToken, generalData } = useContext(OutletContext);

  const getAccountIdFromCookie = () => {
    const cookies = document.cookie.split(";");
    for (let i = 0; i < cookies.length; i++) {
      const cookie = cookies[i].trim();
      if (cookie.startsWith("defaultAccountID=")) {
        return cookie.substring("defaultAccountID=".length);
      }
    }
    return null;
  };

  const accountId =
    generalData?.selectedAccount?.id || getAccountIdFromCookie();
  const mediaService = useMediaServiceV2();
  const playContainerRef = useRef(null);
  const videoRef = useRef(null);
  const hlsRef = useRef(null);
  const hideTimerRef = useRef(null);
  const sourceKeyRef = useRef(null);

  const [isFullscreen, setIsFullscreen] = useState(false);
  const [isPlaying, setIsPlaying] = useState(false);
  const [isSeeking, setIsSeeking] = useState(false);
  const [isBuffering, setIsBuffering] = useState(false);
  const [isReady, setIsReady] = useState(false);
  const [progress, setProgress] = useState({
    played: 0,
    currentTime: 0,
    duration: 0,
  });
  const [rangeProgress, setRangeProgress] = useState(0);
  const [hideController, setHideController] = useState(false);
  const [resolvedUrl, setResolvedUrl] = useState(null);
  const [isResolvingUrl, setIsResolvingUrl] = useState(false);
  const [resolveFailed, setResolveFailed] = useState(false);
  const [playbackError, setPlaybackError] = useState(null);

  const fileIdMode = !!fileId && !src;
  const resolver = useMemo(
    () => mediaService?.resolvePresignedUrl,
    [mediaService],
  );

  const log = useCallback(
    (...args) => {
      if (debug) console.log("[VideoPlayer2]", ...args);
    },
    [debug],
  );

  /**
   * Effect: URL Resolution Logic
   */
  useEffect(() => {
    let cancelled = false;
    const sourceKey = `${src || ""}:${fileId || ""}:${streamUrl || ""}`;
    const sourceChanged = sourceKeyRef.current !== sourceKey;
    sourceKeyRef.current = sourceKey;

    if (sourceChanged) {
      setResolveFailed(false);
      setPlaybackError(null);
      setResolvedUrl(null);
    }

    if (src) {
      setIsResolvingUrl(false);
      return;
    }

    if (!fileId) {
      setIsResolvingUrl(false);
      return;
    }

    const cacheKey = `${accountId || "na"}:${fileId}`;

    // 1. Sync Cache Check (Instant switch)
    const cached = _resolvedCache.get(cacheKey);
    if (_isCacheFresh(cached)) {
      setResolvedUrl(cached.url);
      setIsResolvingUrl(false);
      return;
    }

    if (typeof resolver !== "function") {
      if (mediaService) {
        setResolveFailed(true);
        onDetail?.({ isLoading: false, error: true });
      }
      return;
    }

    setIsResolvingUrl(true);
    onDetail?.({ isLoading: true });

    resolveUrlDeduped(fileId, resolver, cacheKey)
      .then((url) => {
        if (cancelled) return;
        if (!url) {
          setResolveFailed(true);
          onDetail?.({ isLoading: false, error: true });
        } else {
          setResolvedUrl(url);
        }
      })
      .catch(() => {
        if (cancelled) return;
        setResolveFailed(true);
        onDetail?.({ isLoading: false, error: true });
      })
      .finally(() => {
        if (cancelled) return;
        setIsResolvingUrl(false);
      });

    return () => {
      cancelled = true;
    };
  }, [fileId, src, streamUrl, accountId, resolver, mediaService]);

  const playbackUrl = useMemo(() => {
    if (src) return _normalizeUrl(src);
    if (fileIdMode) return _normalizeUrl(resolvedUrl);
    return _normalizeUrl(streamUrl || resolvedUrl);
  }, [src, fileIdMode, resolvedUrl, streamUrl]);

  const isHls = useMemo(
    () => (playbackUrl ? /\.m3u8(\?|$)/i.test(playbackUrl) : false),
    [playbackUrl],
  );

  /**
   * Effect: Media Pipeline (Video Tag / HLS.js)
   */
  useEffect(() => {
    const video = videoRef.current;
    if (!video) return;

    const destroyHls = () => {
      if (hlsRef.current) {
        try {
          hlsRef.current.destroy();
        } catch {}
        hlsRef.current = null;
      }
    };

    setIsReady(false);
    setIsPlaying(false);
    setIsBuffering(false);
    setIsSeeking(false);
    setProgress({ played: 0, currentTime: 0, duration: 0 });
    setRangeProgress(0);
    setPlaybackError(null);

    destroyHls();
    video.removeAttribute("src");
    video.load();

    if (!playbackUrl) return;

    const presigned = _isPresignedUrl(playbackUrl);
    const finalUrl = presigned
      ? playbackUrl
      : _withAccountParam(playbackUrl, accountId);
    const shouldUseHlsJs = isHls && Hls.isSupported();

    if (shouldUseHlsJs) {
      const hls = new Hls({
        lowLatencyMode: true,
        xhrSetup: (xhr, reqUrl) => {
          const reqPresigned = _isPresignedUrl(reqUrl);
          const reqFinal = reqPresigned
            ? reqUrl
            : _withAccountParam(reqUrl, accountId);
          xhr.open("GET", reqFinal, true);
          if (!reqPresigned && accessToken) {
            xhr.setRequestHeader("Authorization", `Bearer ${accessToken}`);
          }
        },
      });

      hlsRef.current = hls;
      hls.on(Hls.Events.MEDIA_ATTACHED, () => hls.loadSource(finalUrl));
      hls.on(Hls.Events.MANIFEST_PARSED, () => {
        setIsReady(true);
        setIsBuffering(false);
      });
      hls.on(Hls.Events.ERROR, (_evt, data) => {
        if (!data?.fatal) return;
        setIsReady(false);
        setIsBuffering(false);
        setPlaybackError({ type: "hls", data });
        onDetail?.({ isLoading: false, error: true });
      });
      hls.attachMedia(video);
      return () => destroyHls();
    }

    video.src = finalUrl;
    video.load();
    return () => destroyHls();
  }, [playbackUrl, isHls, accessToken, accountId]);

  // Video Event Handlers
  const handleLoadedMetadata = (e) => {
    const v = e.target;
    setIsReady(true);
    onDetail?.({
      width: v.videoWidth,
      height: v.videoHeight,
      isLoading: false,
      error: false,
      type: "video",
    });
  };

  const handleVideoError = () => {
    setIsReady(false);
    setIsBuffering(false);
    setPlaybackError({ type: "video" });
    onDetail?.({ isLoading: false, error: true });
    onError?.(true);
  };

  const play = useCallback(() => {
    if (videoRef.current && isReady)
      videoRef.current.play().catch((e) => log(e));
  }, [isReady, log]);

  const pause = useCallback(() => {
    if (videoRef.current && isReady) videoRef.current.pause();
  }, [isReady]);

  const handleTimeUpdate = useCallback(() => {
    const v = videoRef.current;
    if (!v) return;
    const currentTime = v.currentTime || 0;
    const duration = v.duration || 0;
    const played = duration > 0 ? currentTime / duration : 0;
    setProgress({ currentTime, duration, played });
    if (!isSeeking) setRangeProgress(played);
  }, [isSeeking]);

  const handleSeekChange = (e) =>
    setRangeProgress(parseFloat(e.target.value) / 100);
  const handleSeekMouseUp = () => {
    if (videoRef.current) {
      videoRef.current.currentTime =
        rangeProgress * (videoRef.current.duration || 0);
    }
    setIsSeeking(false);
  };

  const formatTime = (secs) => {
    const s = Math.floor(secs || 0);
    const m = Math.floor(s / 60) % 60;
    const ss = s % 60;
    return `${m < 10 ? "0" : ""}${m}:${ss < 10 ? "0" : ""}${ss}`;
  };

  const handleMouseMove = () => {
    setHideController(false);
    if (hideTimerRef.current) clearTimeout(hideTimerRef.current);
    hideTimerRef.current = setTimeout(() => setHideController(true), 2500);
  };

  const handleFullScreen = () => {
    if (onFullModeClick) {
      onFullModeClick(!isFullscreen);
      setIsFullscreen(!isFullscreen);
    } else if (screenfull.isEnabled) {
      screenfull.toggle(playContainerRef.current);
    }
  };

  const showLoader = useMemo(() => {
    if (fileIdMode)
      return isResolvingUrl || !playbackUrl || !isReady || isBuffering;
    return !!playbackUrl && (!isReady || isBuffering);
  }, [fileIdMode, isResolvingUrl, playbackUrl, isReady, isBuffering]);

  const showError = useMemo(() => {
    if (playbackError) return true;
    if (fileIdMode)
      return (
        (!isResolvingUrl && resolveFailed) ||
        (!isResolvingUrl && !playbackUrl && !!fileId)
      );
    return !src && !streamUrl && !playbackUrl;
  }, [
    playbackError,
    fileIdMode,
    isResolvingUrl,
    resolveFailed,
    playbackUrl,
    fileId,
    src,
    streamUrl,
  ]);

  if (showError && errorReturnNull) return null;

  return (
    <VideoPlayerContainer
      ref={playContainerRef}
      style={style}
      width={width}
      height={height}
      onMouseMove={handleMouseMove}
    >
      {showError ? (
        <MediaError
          width={width}
          height={height}
          small={small}
          bgColor={errorBgColor}
          message={findText("This media is unavailable at the moment")}
        />
      ) : (
        <>
          {showLoader && (
            <LoadingScreen>
              <FullPageLoader
                isSectionLoader
                hasBackground
                loaderWidth={small ? 150 : 200}
                loaderHeight={small ? 150 : 200}
                zIndex={5}
              />
            </LoadingScreen>
          )}
          <video
            ref={videoRef}
            width="100%"
            height="100%"
            preload="metadata"
            controls={false}
            playsInline
            onPlay={() => setIsPlaying(true)}
            onPause={() => setIsPlaying(false)}
            onEnded={() => setIsPlaying(false)}
            onTimeUpdate={handleTimeUpdate}
            onLoadedMetadata={handleLoadedMetadata}
            onWaiting={() => setIsBuffering(true)}
            onPlaying={() => setIsBuffering(false)}
            onError={handleVideoError}
          />
          <VideoController hide={hideController} size={controlSize}>
            {!hideRange && (
              <RangeContainer>
                <div className="range_body">
                  <p className="range-text duration_text">
                    {formatTime(progress.currentTime)}
                  </p>
                  <input
                    type="range"
                    min={0}
                    max={100}
                    className="range"
                    value={(rangeProgress || 0) * 100}
                    onChange={handleSeekChange}
                    onMouseDown={() => setIsSeeking(true)}
                    onMouseUp={handleSeekMouseUp}
                  />
                  <p className="range-text">{formatTime(progress.duration)}</p>
                </div>
                {isFullscreen ? (
                  <ZoomOutIcon onClick={handleFullScreen} />
                ) : (
                  <ZoomInIcon onClick={handleFullScreen} />
                )}
              </RangeContainer>
            )}
            {isReady && (
              <div
                className="play_pause_wrap"
                onClick={isPlaying ? pause : play}
              >
                {isPlaying ? (
                  <PauseIcon fill="#fff" />
                ) : (
                  <PlayIcon fill="#fff" />
                )}
              </div>
            )}
          </VideoController>
        </>
      )}
    </VideoPlayerContainer>
  );
};

export default VideoPlayer2;
