import React, { useCallback, useEffect, useLayoutEffect, useMemo, useRef } from 'react';

import classNames from 'classnames';
import { useDispatch } from 'react-redux';

import { Job } from '@enact/core/util';
import Spotlight from '@enact/spotlight';
import SpotlightContainerDecorator from '@enact/spotlight/SpotlightContainerDecorator';
import { setContainerLastFocusedElement } from '@enact/spotlight/src/container';

import dummyVtt from '../../../assets/mock/video.vtt';
import { createDebugHelpers } from '../../utils/debug';
import {
  changeAppStatus,
  changeLocalSettings,
  requestLiveSubtitle,
  sendBroadCast,
  setHidePopup,
} from '../../actions/commonActions';
import {
  sendLogGNB,
  sendLogLive,
  sendLogTotalRecommend,
  sendLogVOD,
} from '../../actions/logActions';
import {
  clearShopNowInfo,
  getHomeFullVideoInfo,
  getMainCategoryShowDetail,
  getMainLiveShow,
  getMainLiveShowNowProduct,
} from '../../actions/mainActions';
import * as PanelActions from '../../actions/panelActions';
import { updatePanel } from '../../actions/panelActions';
import {
  CLEAR_PLAYER_INFO,
  clearSubtitleBlob,
  getChatLog,
  getSubTitle,
  startVideoPlayer,
  pauseModalVideo,
  resumeModalVideo,
  resumeFullscreenVideo,
  updateVideoPlayState,
} from '../../actions/playActions';
import { resetPlayerOverlays } from '../../actions/videoPlayActions';
import { convertUtcToLocal } from '../../components/MediaPlayer/util';
import TPanel from '../../components/TPanel/TPanel';
import TPopUp from '../../components/TPopUp/TPopUp';
import Media from '../../components/VideoPlayer/Media';
import TReactPlayer from '../../components/VideoPlayer/TReactPlayer';
import { VideoPlayer } from '../../components/VideoPlayer/VideoPlayer';
import useWhyDidYouUpdate from '../../hooks/useWhyDidYouUpdate';
import * as Config from '../../utils/Config';
import { ACTIVE_POPUP, panel_names } from '../../utils/Config';
import { $L, formatGMTString } from '../../utils/helperMethods';
import { SpotlightIds } from '../../utils/SpotlightIds';
import { removeDotAndColon } from './PlayerItemCard/PlayerItemCard';
import PlayerOverlayChat from './PlayerOverlay/PlayerOverlayChat';
import PlayerOverlayQRCode from './PlayerOverlay/PlayerOverlayQRCode';
import css from './PlayerPanel.module.less';
import PlayerTabButton from './PlayerTabContents/TabButton/PlayerTabButton';
import TabContainer from './PlayerTabContents/TabContainer';
import TabContainerV2 from './PlayerTabContents/v2/TabContainer.v2';
// import LiveShowContainer from './PlayerTabContents/v2/LiveShowContainer';
// import ShopNowContainer from './PlayerTabContents/v2/ShopNowContainer';
// import ShopNowButton from './PlayerTabContents/v2/ShopNowButton';

const Container = SpotlightContainerDecorator({ enterTo: 'last-focused', preserveId: true }, 'div');

// 디버그 헬퍼 설정
const DEBUG_MODE = false;
const { dlog, dwarn, derror } = createDebugHelpers(DEBUG_MODE);

const findSelector = (selector, maxAttempts = 5, currentAttempts = 0) => {
  try {
    if (currentAttempts >= maxAttempts) {
      throw new Error('selector not found');
    }

    const initialSelector = document.querySelector(selector);

    if (initialSelector) {
      return initialSelector;
    } else {
      return findSelector(selector, maxAttempts, currentAttempts + 1);
    }
  } catch (error) {
    // console.error(error.message);
  }
};

const getLogTpNo = (type, nowMenu) => {
  if (type === 'LIVE') {
    switch (nowMenu) {
      case Config.LOG_MENU.HOME_TOP:
        return Config.LOG_TP_NO.LIVE.HOME;
      // case Config.LOG_MENU.FEATURED_BRANDS_LIVE_CHANNELS:
      // return Config.LOG_TP_NO.LIVE.FEATURED_BRANDS;
      case Config.LOG_MENU.FULL_SHOP_NOW:
      case Config.LOG_MENU.FULL_YOU_MAY_LIKE:
      case Config.LOG_MENU.FULL_LIVE_CHANNELS:
        return Config.LOG_TP_NO.LIVE.FULL;
      default:
        return Config.LOG_TP_NO.LIVE.FEATURED_BRANDS;
    }
  } else if (type === 'VOD') {
    switch (nowMenu) {
      case Config.LOG_MENU.HOME_TOP:
        return Config.LOG_TP_NO.VOD.HOME_VOD; // 153
      case Config.LOG_MENU.TRENDING_NOW_POPULAR_SHOWS:
        return Config.LOG_TP_NO.VOD.POPULAR_SHOWS_AND_HOT_PICKS; // 151
      case Config.LOG_MENU.FULL_SHOP_NOW:
      case Config.LOG_MENU.FULL_YOU_MAY_LIKE:
      case Config.LOG_MENU.FULL_FEATURED_SHOWS:
        return Config.LOG_TP_NO.VOD.FULL_VOD; // 150
      default:
        return;
    }
  } else if (type === 'MEDIA') {
    switch (nowMenu) {
      case Config.LOG_MENU.HOME_TOP:
        return Config.LOG_TP_NO.VOD.HOME_VOD; // 153
      case Config.LOG_MENU.HOT_PICKS:
        return Config.LOG_TP_NO.VOD.POPULAR_SHOWS_AND_HOT_PICKS; // 151
      case Config.LOG_MENU.DETAIL_PAGE_BILLING_PRODUCT_DETAIL:
      case Config.LOG_MENU.DETAIL_PAGE_PRODUCT_DETAIL:
      case Config.LOG_MENU.DETAIL_PAGE_GROUP_DETAIL:
      case Config.LOG_MENU.DETAIL_PAGE_THEME_DETAIL:
      case Config.LOG_MENU.DETAIL_PAGE_TRAVEL_THEME_DETAIL:
        return Config.LOG_TP_NO.VOD.ITEM_DETAIL_MEDIA; // 156
      case Config.LOG_MENU.FULL:
        return Config.LOG_TP_NO.VOD.FULL_MEDIA; // 155
      default:
        return;
    }
  }
};

const YOUTUBECONFIG = {
  playerVars: {
    controls: 0, // 플레이어 컨트롤 표시
    autoplay: 1,
    disablekb: 1,
    enablejsapi: 1,
    listType: 'user_uploads',
    fs: 0,
    rel: 0, // 관련 동영상 표시 안 함
    showinfo: 0,
    loop: 0,
    iv_load_policy: 3,
    modestbranding: 1,
    wmode: 'opaque',
    cc_lang_pref: 'en',
    cc_load_policy: 0,
    playsinline: 1,
  },
};

const INITIAL_TIMEOUT = 30000;
const REGULAR_TIMEOUT = 30000;
const TAB_CONTAINER_SPOTLIGHT_ID = 'tab-container-spotlight-id';
const TAB_CONTAINER_V2_SPOTLIGHT_ID = 'tab-container-v2-spotlight-id';
const TARGET_EVENTS = ['mousemove', 'keydown', 'click'];

// last time error
const VIDEO_END_ACTION_DELAY = 1500;

const PlayerPanel = ({ isTabActivated, panelInfo, isOnTop, spotlightId, ...props }) => {
  const dispatch = useDispatch();
  const { USE_STATE, USE_SELECTOR } = useWhyDidYouUpdate(spotlightId, {
    isTabActivated,
    panelInfo,
    isOnTop,
    ...props,
  });

  const videoPlayer = useRef(null);
  const focusReturnRef = useRef(null);
  const modalPrevRef = useRef(panelInfo?.modal);
  const prevIsTopPanelDetailFromPlayerRef = useRef(false);
  const [playListInfo, setPlayListInfo] = USE_STATE('playListInfo', '');
  const [shopNowInfo, setShopNowInfo] = USE_STATE('shopNowInfo');
  const [backupInitialIndex, setBackupInitialIndex] = USE_STATE('backupInitialIndex', 0);
  const [modalStyle, setModalStyle] = USE_STATE('modalStyle', {});
  const [modalScale, setModalScale] = USE_STATE('modalScale', 1);
  const [mediaId, setMediaId] = USE_STATE('mediaId', null);
  const [currentLiveTimeSeconds, setCurrentLiveTimeSeconds] = USE_STATE(
    'currentLiveTimeSeconds',
    1
  );
  const [prevChannelIndex, setPrevChannelIndex] = USE_STATE('prevChannelIndex', 0);
  const [sideContentsVisible, setSideContentsVisible] = USE_STATE('sideContentsVisible', true);
  const [belowContentsVisible, setBelowContentsVisible] = USE_STATE('belowContentsVisible', true);
  const [currentTime, setCurrentTime] = USE_STATE('currentTime', 0);
  const [isInitialFocusOccurred, setIsInitialFocusOccurred] = USE_STATE(
    'isInitialFocusOccurred',
    false
  );
  const [selectedIndex, setSelectedIndex] = USE_STATE(
    'selectedIndex',
    panelInfo.shptmBanrTpNm === 'LIVE' ? null : 0
  );
  const [isUpdate, setIsUpdate] = USE_STATE('isUpdate', false);
  const [isSubtitleActive, setIsSubtitleActive] = USE_STATE('isSubtitleActive', true);
  const [logStatus, setLogStatus] = USE_STATE('logStatus', {
    isModalLiveLogReady: false,
    isFullLiveLogReady: false,
    isDetailLiveLogReady: false,
    isModalVodLogReady: false,
    isFullVodLogReady: false,
    isDetailVodLogReady: false,
    isModalMediaLogReady: false,
    isFullMediaLogReady: false,
    isDetailMediaReady: false,
  });
  const [isVODPaused, setIsVODPaused] = USE_STATE('isVODPaused', false);
  const [tabIndexV2, setTabIndexV2] = USE_STATE('tabIndexV2', 1); // 0: ShopNow, 1: LiveChannel, 2: ShopNowButton
  const [tabContainerVersion, setTabContainerVersion] = USE_STATE('tabContainerVersion', 2); // 1: TabContainer (우측), 2: TabContainerV2 (하단)
  const [isModalClosed, setIsModalClosed] = USE_STATE('isModalClosed', true); // 모달이 false 상태인지 나타내는 플래그

  const panels = USE_SELECTOR('panels', (state) => state.panels.panels);
  const chatData = USE_SELECTOR('chatData', (state) => state.play.chatData);
  const shouldHideOverlays = USE_SELECTOR(
    'shouldHideOverlays',
    (state) => state.videoPlay.shouldHideOverlays
  );
  const shouldShowOverlays = USE_SELECTOR(
    'shouldShowOverlays',
    (state) => state.videoPlay.shouldShowOverlays
  );

  const popupVisible = USE_SELECTOR('popupVisible', (state) => state.common.popup.popupVisible);
  const activePopup = USE_SELECTOR('activePopup', (state) => state.common.popup.activePopup);
  const showDetailInfo = USE_SELECTOR('showDetailInfo', (state) => state.main.showDetailInfo);
  const productImageLength = USE_SELECTOR(
    'productImageLength',
    (state) => state.product.productImageLength
  );
  const themeProductInfos = USE_SELECTOR(
    'themeProductInfos',
    (state) => state.home.themeCurationDetailInfoData
  );
  const hotelInfos = USE_SELECTOR('hotelInfos', (state) => state.home.themeCurationHotelDetailData);
  const captionEnable = USE_SELECTOR(
    'captionEnable',
    (state) => state.common.appStatus.captionEnable
  );
  const fullVideolgCatCd = USE_SELECTOR('fullVideolgCatCd', (state) => state.main.fullVideolgCatCd);
  const featuredShowsInfos = USE_SELECTOR(
    'featuredShowsInfos',
    (state) => state.main.featuredShowsInfos
  );
  const localRecentItems = USE_SELECTOR(
    'localRecentItems',
    (state) => state.localSettings?.recentItems
  );
  const httpHeader = USE_SELECTOR('httpHeader', (state) => state.common?.httpHeader);
  const countryCode = USE_SELECTOR('countryCode', (state) => state.common.httpHeader?.cntry_cd);
  const liveChannelInfos = USE_SELECTOR('liveChannelInfos', (state) => state.main.liveChannelInfos);
  const showNowInfos = USE_SELECTOR('showNowInfos', (state) => state.main.showNowInfo);
  const liveShowInfos = USE_SELECTOR('liveShowInfos', (state) => state.main.liveShowInfos);
  const vodSubtitleData = USE_SELECTOR('vodSubtitleData', (state) => state.play.subTitleBlobs);
  const previousSubtitleBlobRef = useRef(null);
  const previousSubtitleUrlRef = useRef(null);
  const broadcast = USE_SELECTOR('broadcast', (state) => state.common.broadcast);
  const videoPlayState = USE_SELECTOR('videoPlayState', (state) => state.play.videoPlayState);

  const lastPanelAction = USE_SELECTOR('lastPanelAction', (state) => state.panels.lastPanelAction);
  const nowMenu = USE_SELECTOR('nowMenu', (state) => state.common.menu.nowMenu);
  const nowMenuRef = useRef(null);
  const entryMenu = USE_SELECTOR('entryMenu', (state) => state.common.menu.entryMenu);
  const [videoLoaded, setVideoLoaded] = USE_STATE('videoLoaded', false);
  const entryMenuRef = useRef(null);
  const panelInfoRef = useRef(null);

  const initialFocusTimeoutJob = useRef(new Job((func) => func(), 100));
  const liveLogParamsRef = useRef(null);
  const vodLogParamsRef = useRef(null);
  const mediaLogParamsRef = useRef(null);
  const prevNowMenuRef = useRef(null);
  const watchIntervalLive = useRef(null);
  const watchIntervalVod = useRef(null);
  const watchIntervalMedia = useRef(null);
  const timeoutRef = useRef(null);
  // useEffect(() => {
  //   console.log("###videoLoaded", videoLoaded);
  //   if (nowMenu) {
  //   }
  // }, [videoLoaded]);
  const currentLiveShowInfo = useMemo(() => {
    if (liveShowInfos && liveShowInfos.length > 0) {
      const panelInfoChanId = panelInfo?.chanId;
      const isLive = panelInfo?.shptmBanrTpNm === 'LIVE';
      const isModal = panelInfo?.modal;

      if (isLive) {
        // live full 화면에서 modal 전환시 로그 전송 추가
        if (isModal) {
          dispatch(sendLogGNB(Config.LOG_MENU.FULL));
          prevNowMenuRef.current = nowMenuRef.current;
          return () => dispatch(sendLogGNB(prevNowMenuRef.current));
        }
        const liveShowInfo = liveShowInfos //
          .find(({ chanId }) => panelInfoChanId === chanId);

        return liveShowInfo;
      }

      return {};
    }

    return {};
  }, [liveShowInfos, panelInfo?.chanId, panelInfo?.shptmBanrTpNm, panelInfo?.modal]);

  const currentVODShowInfo = useMemo(() => {
    if (showDetailInfo && showDetailInfo.length > 0) {
      const isVOD = panelInfo?.shptmBanrTpNm === 'VOD';

      if (isVOD) {
        const vodShowInfo = showDetailInfo[0];

        return vodShowInfo;
      }

      return {};
    }

    return {};
  }, [panelInfo?.shptmBanrTpNm, showDetailInfo]);

  useEffect(() => {
    if (!panelInfo?.modal && panelInfo?.shptmBanrTpNm === 'MEDIA') {
      dispatch(sendLogGNB(Config.LOG_MENU.FULL));
      prevNowMenuRef.current = nowMenuRef.current;

      return () => dispatch(sendLogGNB(prevNowMenuRef.current));
    } else if (panelInfo?.modal && panelInfo?.shptmBanrTpNm !== 'LIVE') {
      dispatch(sendLogGNB(entryMenu));
    }
  }, [panelInfo?.modal, panelInfo?.shptmBanrTpNm]);

  // useEffect(()=>{
  //   console.log('[PlayerPanel] isOnTop:', {
  //     isOnTop,
  //     panelInfo
  //   });
  //   if (panelInfo && panelInfo.modal) {
  //     if (!isOnTop) {
  //       console.log('[PlayerPanel] Not on top - pausing video');
  //       dispatch(pauseModalVideo());
  //     } else if (isOnTop && panelInfo.isPaused) {
  //       console.log('[PlayerPanel] Back on top - resuming video');
  //       dispatch(resumeModalVideo());
  //     }
  //   }
  // },[isOnTop, panelInfo])

  // useRef들 업데이트
  // prevIsTopPanelDetailFromPlayerRef 업데이트
  useEffect(() => {
    prevIsTopPanelDetailFromPlayerRef.current = isTopPanelDetailFromPlayer;
  }, [isTopPanelDetailFromPlayer]);

  // nowMenuRef 업데이트
  useEffect(() => {
    nowMenuRef.current = nowMenu;
  }, [nowMenu]);

  // entryMenuRef 업데이트
  useEffect(() => {
    entryMenuRef.current = entryMenu;
  }, [entryMenu]);

  // panelInfoRef 업데이트
  useEffect(() => {
    panelInfoRef.current = panelInfo;
  }, [panelInfo]);

  // PlayerPanel.jsx의 라인 313-327 useEffect 수정 - detailPanelClosed flag 감지 추가
  useEffect(() => {
    dlog('[PlayerPanel] 🔍 isOnTop useEffect 호출:', {
      isOnTop,
      modal: panelInfo?.modal,
      isPaused: panelInfo?.isPaused,
      detailPanelClosed: panelInfo?.detailPanelClosed,
    });

    if (isOnTop) {
      // 1. Resume Video if needed (isPaused or detailPanelClosed)
      if (panelInfo.isPaused || panelInfo.detailPanelClosed) {
        if (panelInfo.modal) {
          dlog('[PlayerPanel] ▶️ Back on top (Modal) - resuming video');
          dispatch(resumeModalVideo());
        } else {
          dlog('[PlayerPanel] ▶️ Back on top (Fullscreen) - resuming video');
          dispatch(resumeFullscreenVideo());
        }
      }

      // 2. Reset detailPanelClosed flag
      if (panelInfo.detailPanelClosed) {
        dlog('[PlayerPanel] 🔄 detailPanelClosed flag 초기화 시작');
        dlog('[PlayerPanel] 🔙 DetailPanel에서 복귀 정보:', {
          detailPanelClosedAt: panelInfo.detailPanelClosedAt,
          detailPanelClosedFromSource: panelInfo.detailPanelClosedFromSource,
          lastFocusedTargetId: panelInfo.lastFocusedTargetId,
        });

        // 포커스 복원 로직 추가 (1000ms 지연)
        if (panelInfo.lastFocusedTargetId) {
          dlog(
            '[PlayerPanel] 🎯 DetailPanel 복귀 후 1000ms 지연 포커스 복원 예약:',
            panelInfo.lastFocusedTargetId
          );

          const focusTimeoutId = setTimeout(() => {
            dlog(
              '[PlayerPanel] 🔍 DetailPanel 복귀 후 포커스 복원 실행:',
              panelInfo.lastFocusedTargetId
            );
            Spotlight.focus(panelInfo.lastFocusedTargetId);
          }, 1000);

          // cleanup 함수를 통해 컴포넌트 unmount 시 타이머 정리
          return () => {
            if (focusTimeoutId) {
              clearTimeout(focusTimeoutId);
              dlog('[PlayerPanel] 🧹 포커스 복원 타이머 정리됨');
            }
          };
        }

        dispatch(
          updatePanel({
            name: panel_names.PLAYER_PANEL,
            panelInfo: {
              detailPanelClosed: false,
              detailPanelClosedAt: undefined,
              detailPanelClosedFromSource: undefined,
              lastFocusedTargetId: undefined, // 포커스 복원 후 초기화
            },
          })
        );
        dlog('[PlayerPanel] 🏁 detailPanelClosed flag 초기화 완료');
      }
    } else {
      // Not on top
      if (panelInfo && panelInfo.modal) {
        dlog('[PlayerPanel] ⏸️ Not on top (Modal) - pausing video logic commented out');
        // dispatch(pauseModalVideo());
      }
    }
  }, [isOnTop, panelInfo]);

  // 이전 상태 저장을 위한 ref
  const previousVideoPlayState = useRef(null);
  const previousPanelInfo = useRef(null);
  const previousVideoSource = useRef(null);
  const previousIsOnTop = useRef(null);

  // Redux 상태 변화 모니터링 useEffect (중요 변화만)
  useEffect(() => {
    // HomePanel이 최상위이고 videoPlayState가 실제로 변경되었을 때만 로그
    const isOnTop = panel_names.HOME_PANEL === topPanel?.name;
    const hasSignificantChange =
      previousVideoPlayState.current?.isPlaying !== videoPlayState?.isPlaying ||
      previousVideoPlayState.current?.isPaused !== videoPlayState?.isPaused ||
      Math.abs(
        (previousVideoPlayState.current?.currentTime || 0) - (videoPlayState?.currentTime || 0)
      ) > 1;

    // 🔍 DetailPanel으로 인한 일시정지 상태 변화도 모니터링
    const isDetailPanelOnTop = panel_names.DETAIL_PANEL === topPanel?.name;
    const isPlayingChanged =
      previousVideoPlayState.current?.isPlaying !== videoPlayState?.isPlaying;
    const isPausedChanged = previousVideoPlayState.current?.isPaused !== videoPlayState?.isPaused;

    if (
      (isOnTop && videoPlayState && hasSignificantChange) ||
      (isDetailPanelOnTop && (isPlayingChanged || isPausedChanged))
    ) {
      dlog('📊 [PlayerPanel] Significant videoPlayState change', {
        previousState: previousVideoPlayState.current,
        currentState: videoPlayState,
        topPanelName: topPanel?.name,
        isOnTop,
        isDetailPanelOnTop,
        videoPlayerExists: !!videoPlayer.current,
        currentPlayingUrl,
        changeReason: isDetailPanelOnTop ? 'DetailPanel transition' : 'HomePanel state change',
        // 🔍 Redux paused 상태 특별 확인
        reduxIsPaused: videoPlayState?.isPaused,
        reduxIsPlaying: videoPlayState?.isPlaying,
        panelInfoIsPaused: panelInfo?.isPaused,
        timestamp: new Date().toISOString(),
        source: 'useEffect videoPlayState',
      });
    }

    previousVideoPlayState.current = videoPlayState;
  }, [videoPlayState, topPanel?.name, panelInfo?.isPaused]);

  // PanelInfo 상태 변화 모니터링 useEffect (isPaused가 실제로 변경될 때만)
  useEffect(() => {
    const isOnTop = panel_names.HOME_PANEL === topPanel?.name;
    const isPausedChanged = previousPanelInfo.current?.isPaused !== panelInfo?.isPaused;

    if (isOnTop && panelInfo?.isPaused !== undefined && isPausedChanged) {
      // 상태 변경 시에만 디버깅 로그 출력
      dlog('🔍 [PlayerPanel] PanelInfo isPaused changed', {
        previousIsPaused: previousPanelInfo.current?.isPaused,
        currentIsPaused: panelInfo.isPaused,
        isOnTop,
        videoPlayerExists: !!videoPlayer.current,
        currentPlayingUrl,
        timestamp: new Date().toISOString(),
      });
      dlog('🎮 [PlayerPanel] PanelInfo isPaused changed', {
        previousIsPaused: previousPanelInfo.current?.isPaused,
        currentIsPaused: panelInfo.isPaused,
        videoPlayerExists: !!videoPlayer.current,
        currentPlayingUrl,
        shptmBanrTpNm: panelInfo?.shptmBanrTpNm,
        showId: panelInfo?.showId,
        timestamp: new Date().toISOString(),
        source: 'useEffect panelInfo.isPaused',
      });

      if (videoPlayer.current) {
        if (panelInfo.isPaused === true) {
          dlog('🔴 [PlayerPanel] Calling VideoPlayer.pause() due to PanelInfo change');
          videoPlayer.current.pause();
        } else if (panelInfo.isPaused === false) {
          dlog('🟢 [PlayerPanel] Calling VideoPlayer.play() due to PanelInfo change');
          videoPlayer.current.play();
        }
      }
    }

    previousPanelInfo.current = panelInfo;
  }, [panelInfo?.isPaused, topPanel?.name, currentPlayingUrl]);

  // VideoPlayer 인스턴스 및 소스 변경 모니터링 (중요 변화만)
  useEffect(() => {
    const isOnTop = panel_names.HOME_PANEL === topPanel?.name;
    const isDetailPanelOnTop = panel_names.DETAIL_PANEL === topPanel?.name;
    const isVideoSourceChanged = previousVideoSource.current !== currentPlayingUrl;
    const isOnTopChanged = previousIsOnTop.current !== isOnTop;
    const isDetailPanelOnTopChanged =
      previousIsOnTop.current === false && isDetailPanelOnTop === true;
    const videoPlayerJustCreated = previousVideoSource.current === null && videoPlayer.current;

    if (
      (isVideoSourceChanged ||
        isOnTopChanged ||
        isDetailPanelOnTopChanged ||
        videoPlayerJustCreated) &&
      videoPlayer.current
    ) {
      const changeReason = isDetailPanelOnTopChanged
        ? 'DetailPanel opened'
        : isVideoSourceChanged
          ? 'Video source changed'
          : isOnTopChanged
            ? 'Top panel changed'
            : 'VideoPlayer created';

      dlog('🎬 [PlayerPanel] VideoPlayer state change', {
        hasVideoPlayer: !!videoPlayer.current,
        currentPlayingUrl,
        previousVideoSource: previousVideoSource.current,
        topPanelName: topPanel?.name,
        isOnTop,
        isDetailPanelOnTop,
        isVideoSourceChanged,
        isOnTopChanged,
        isDetailPanelOnTopChanged,
        videoPlayerJustCreated,
        changeReason,
        timestamp: new Date().toISOString(),
        source: 'useEffect videoPlayer.current',
      });

      // VideoPlayer 상태 확인 (소스 변경이나 PlayerPanel 생성 시에만)
      if (isVideoSourceChanged || videoPlayerJustCreated || isDetailPanelOnTopChanged) {
        const mediaState = videoPlayer.current.getMediaState?.();
        if (mediaState) {
          dlog('📊 [PlayerPanel] VideoPlayer current state', {
            mediaState,
            videoPlayState,
            changeReason,
            timestamp: new Date().toISOString(),
            source: 'useEffect getMediaState',
          });
        }
      }
    }

    previousVideoSource.current = currentPlayingUrl;
    previousIsOnTop.current = isOnTop || isDetailPanelOnTop; // 현재 최상위 패널 상태 저장
  }, [videoPlayer.current, currentPlayingUrl, topPanel?.name]);

  // Modal 상태 변화 감지 (true → false, false → true)
  useEffect(() => {
    const currentModalState = panelInfo?.modal;
    const prevModalState = modalPrevRef.current;

    dlog('[PlayerPanel] 🔍 Modal 상태 체크:', {
      prevModalState,
      currentModalState,
      shouldExecuteTrueToFalse: prevModalState === true && currentModalState === false,
      shouldExecuteFalseToTrue: prevModalState === false && currentModalState === true,
    });

    // true → false 변화 감지
    if (prevModalState === true && currentModalState === false) {
      dlog('[PlayerPanel] ▶️ Modal 상태 변화: true → false (전체화면 모드로 복귀)');
      dlog(
        '[PlayerPanel] 🎯 포커스 복원 준비 - lastFocusedTargetId:',
        panelInfo?.lastFocusedTargetId
      );
      setIsModalClosed(true); // 모달이 닫혔음을 표시

      // DetailPanel에서 복귀 시 포커스 복원
      const lastFocusedTargetId = panelInfo?.lastFocusedTargetId;

      if (lastFocusedTargetId) {
        // ShopNowContents가 렌더링될 때까지 대기 후 포커스 복원
        timeoutRef.current = setTimeout(() => {
          dlog('[PlayerPanel] 🔍 800ms 후 포커스 복원 시도:', lastFocusedTargetId);
          Spotlight.focus(lastFocusedTargetId);
        }, 800);
      }
    }

    // false → true 변화 감지 (모달이 열림)
    if (prevModalState === false && currentModalState === true) {
      dlog('[PlayerPanel] 📱 Modal 상태 변화: false → true (모달 열림)');
      setIsModalClosed(false); // 모달이 열렸음을 표시
    }

    modalPrevRef.current = currentModalState; // 현재 modal 상태를 ref에 저장
  }, [panelInfo?.modal, panelInfo?.lastFocusedTargetId]);

  // creating live log params
  useEffect(() => {
    if (currentLiveShowInfo && Object.keys(currentLiveShowInfo).length > 0) {
      if (currentLiveShowInfo.showId !== panelInfo?.showId) {
        dispatch(
          updatePanel({
            name: panel_names.PLAYER_PANEL,
            panelInfo: {
              chanId: currentLiveShowInfo.chanId,
              modalContainerId: panelInfo?.modalContainerId,
              patnrId: currentLiveShowInfo.patnrId,
              showId: currentLiveShowInfo.showId,
              showUrl: currentLiveShowInfo.showUrl,
            },
          })
        );

        return;
      }

      let logTemplateNo;

      if (isOnTop && panelInfo?.modal) {
        logTemplateNo = getLogTpNo('LIVE', nowMenu);
        setLogStatus((status) => ({ ...status, isModalLiveLogReady: true }));
      }
      //
      else if (isOnTop && !panelInfo?.modal) {
        logTemplateNo = Config.LOG_TP_NO.LIVE.FULL;
        setLogStatus((status) => ({ ...status, isFullLiveLogReady: true }));
      }
      //
      else if (!isOnTop && !panelInfo?.modal) {
        logTemplateNo = Config.LOG_TP_NO.LIVE.ITEM_DETAIL;
        setLogStatus((status) => ({ ...status, isDetailLiveLogReady: true }));
      }
      liveLogParamsRef.current = {
        entryMenu: entryMenuRef.current,
        lgCatCd: currentLiveShowInfo.catCd ?? '',
        lgCatNm: currentLiveShowInfo.catNm ?? '',
        linkTpCd: panelInfo?.linkTpCd ?? '',
        logTpNo: logTemplateNo,
        nowMenu: nowMenuRef.current,
        patncNm: currentLiveShowInfo.patncNm,
        patnrId: currentLiveShowInfo.patnrId,
        showId: currentLiveShowInfo.showId,
        showNm: currentLiveShowInfo.showNm,
        vdoTpNm: currentLiveShowInfo.vtctpYn
          ? currentLiveShowInfo.vtctpYn === 'Y'
            ? 'Vertical'
            : 'Horizontal'
          : '',
      };
    }
  }, [
    currentLiveShowInfo,
    isOnTop,
    nowMenu,
    panelInfo?.linkTpCd,
    panelInfo?.modal,
    panelInfo?.modalContainerId,
    panelInfo?.showId,
    setLogStatus,
  ]);

  // send live log
  useEffect(() => {
    if (broadcast && Object.keys(broadcast).length === 0) {
      // case: live, modal
      if (
        logStatus.isModalLiveLogReady &&
        isOnTop &&
        panelInfo?.modal &&
        liveLogParamsRef.current?.showId === panelInfo?.showId
      ) {
        dlog('[PlayerPanel] 📡 LIVE Modal Log Ready and Conditions Met:', {
          isModalLiveLogReady: logStatus.isModalLiveLogReady,
          isOnTop,
          isModal: panelInfo?.modal,
          showIdMatch: liveLogParamsRef.current?.showId === panelInfo?.showId,
          logParams: liveLogParamsRef.current,
        });
        let watchStrtDt = formatGMTString(new Date());

        watchIntervalLive.current = setInterval(() => {
          let watchEndDt = formatGMTString(new Date());
          let watchRecord = {
            ...liveLogParamsRef.current,
            watchStrtDt,
            watchEndDt,
          };
          dispatch(changeLocalSettings({ watchRecord }));
        }, 10000);

        return () => {
          setLogStatus((status) => ({
            ...status,
            isModalLiveLogReady: false,
          }));
          clearInterval(watchIntervalLive.current);
          dlog('[PlayerPanel] 🚀 Dispatching LIVE Modal Log:', {
            logParams: liveLogParamsRef.current,
            watchStrtDt,
          });
          dispatch(
            sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
              dispatch(changeLocalSettings({ watchRecord: {} }))
            )
          );
        };
      }

      // case: live, full
      if (
        logStatus.isFullLiveLogReady &&
        isOnTop &&
        !panelInfo?.modal &&
        liveLogParamsRef.current?.showId === panelInfo?.showId
      ) {
        let watchStrtDt = formatGMTString(new Date());

        watchIntervalLive.current = setInterval(() => {
          let watchEndDt = formatGMTString(new Date());
          let watchRecord = {
            ...liveLogParamsRef.current,
            watchStrtDt,
            watchEndDt,
          };
          dispatch(changeLocalSettings({ watchRecord }));
        }, 10000);

        return () => {
          setLogStatus((status) => ({
            ...status,
            isFullLiveLogReady: false,
          }));
          clearInterval(watchIntervalLive.current);
          dispatch(
            sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
              dispatch(changeLocalSettings({ watchRecord: {} }))
            )
          );
        };
      }

      // case: live, item detail
      if (
        logStatus.isDetailLiveLogReady &&
        !isOnTop &&
        !panelInfo?.modal &&
        liveLogParamsRef.current?.showId === panelInfo?.showId
      ) {
        let watchStrtDt = formatGMTString(new Date());

        watchIntervalLive.current = setInterval(() => {
          let watchEndDt = formatGMTString(new Date());
          let watchRecord = {
            ...liveLogParamsRef.current,
            watchStrtDt,
            watchEndDt,
          };
          dispatch(changeLocalSettings({ watchRecord }));
        }, 10000);

        return () => {
          setLogStatus((status) => ({
            ...status,
            isDetailLiveLogReady: false,
          }));
          clearInterval(watchIntervalLive.current);
          dispatch(
            sendLogLive({ ...liveLogParamsRef.current, watchStrtDt }, () =>
              dispatch(changeLocalSettings({ watchRecord: {} }))
            )
          );
        };
      }
    }
  }, [
    broadcast,
    isOnTop,
    logStatus.isDetailLiveLogReady,
    logStatus.isFullLiveLogReady,
    logStatus.isModalLiveLogReady,
    panelInfo?.modal,
    panelInfo?.showId,
    setLogStatus,
  ]);

  // creating VOD log params
  useEffect(() => {
    if (currentVODShowInfo && Object.keys(currentVODShowInfo).length > 0 && !isVODPaused) {
      let logTemplateNo;

      if (isOnTop && panelInfo?.modal) {
        logTemplateNo = getLogTpNo('VOD', nowMenu);
        setLogStatus((status) => ({ ...status, isModalVodLogReady: true }));
      }
      //
      else if (isOnTop && !panelInfo?.modal) {
        logTemplateNo = Config.LOG_TP_NO.VOD.FULL_VOD;
        setLogStatus((status) => ({ ...status, isFullVodLogReady: true }));
      }
      //
      else if (!isOnTop && !panelInfo?.modal) {
        logTemplateNo = Config.LOG_TP_NO.VOD.ITEM_DETAIL_VOD;
        setLogStatus((status) => ({ ...status, isDetailVodLogReady: true }));
      }

      vodLogParamsRef.current = {
        entryMenu: entryMenuRef.current,
        lgCatCd: currentVODShowInfo?.showCatCd ?? '',
        lgCatNm: currentVODShowInfo?.showCatNm ?? '',
        logTpNo: logTemplateNo,
        linkTpCd: panelInfo?.linkTpCd ?? '',
        nowMenu: nowMenuRef.current,
        patncNm: currentVODShowInfo?.patncNm,
        patnrId: currentVODShowInfo?.patnrId,
        showId: currentVODShowInfo?.showId,
        showNm: currentVODShowInfo?.showNm,
        vdoTpNm: currentVODShowInfo?.vtctpYn
          ? currentVODShowInfo.vtctpYn === 'Y'
            ? 'Vertical'
            : 'Horizontal'
          : '',
      };
    }
  }, [currentVODShowInfo, isOnTop, isVODPaused, nowMenu, panelInfo?.linkTpCd, panelInfo?.modal]);

  // send VOD log
  useEffect(() => {
    if (broadcast && Object.keys(broadcast).length === 0 && !isVODPaused) {
      // case: VOD, modal
      if (
        logStatus.isModalVodLogReady &&
        isOnTop &&
        panelInfo?.modal &&
        vodLogParamsRef.current?.showId === panelInfo?.showId
      ) {
        let watchStrtDt = formatGMTString(new Date());

        watchIntervalVod.current = setInterval(() => {
          let watchEndDt = formatGMTString(new Date());
          let watchRecord = {
            ...vodLogParamsRef.current,
            watchStrtDt,
            watchEndDt,
          };
          dispatch(changeLocalSettings({ watchRecord }));
        }, 10000);

        return () => {
          setLogStatus((status) => ({
            ...status,
            isModalVodLogReady: false,
          }));
          clearInterval(watchIntervalVod.current);
          dispatch(
            sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
              dispatch(changeLocalSettings({ watchRecord: {} }))
            )
          );
        };
      }

      // case: VOD, full
      if (
        logStatus.isFullVodLogReady &&
        isOnTop &&
        !panelInfo?.modal &&
        vodLogParamsRef.current?.showId === panelInfo?.showId
      ) {
        let watchStrtDt = formatGMTString(new Date());

        watchIntervalVod.current = setInterval(() => {
          let watchEndDt = formatGMTString(new Date());
          let watchRecord = {
            ...vodLogParamsRef.current,
            watchStrtDt,
            watchEndDt,
          };
          dispatch(changeLocalSettings({ watchRecord }));
        }, 10000);

        return () => {
          setLogStatus((status) => ({
            ...status,
            isFullVodLogReady: false,
          }));
          clearInterval(watchIntervalVod.current);
          dispatch(
            sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
              dispatch(changeLocalSettings({ watchRecord: {} }))
            )
          );
        };
      }

      // case: VOD, item detail
      if (
        logStatus.isDetailVodLogReady &&
        !isOnTop &&
        !panelInfo?.modal &&
        vodLogParamsRef.current?.showId === panelInfo?.showId
      ) {
        let watchStrtDt = formatGMTString(new Date());

        watchIntervalVod.current = setInterval(() => {
          let watchEndDt = formatGMTString(new Date());
          let watchRecord = {
            ...vodLogParamsRef.current,
            watchStrtDt,
            watchEndDt,
          };
          dispatch(changeLocalSettings({ watchRecord }));
        }, 10000);

        return () => {
          setLogStatus((status) => ({
            ...status,
            isDetailVodLogReady: false,
          }));
          clearInterval(watchIntervalVod.current);
          dispatch(
            sendLogVOD({ ...vodLogParamsRef.current, watchStrtDt }, () =>
              dispatch(changeLocalSettings({ watchRecord: {} }))
            )
          );
        };
      }
    }
  }, [
    broadcast,
    isOnTop,
    isVODPaused,
    logStatus.isDetailVodLogReady,
    logStatus.isFullVodLogReady,
    logStatus.isModalVodLogReady,
    panelInfo?.modal,
    panelInfo?.showId,
    setLogStatus,
  ]);

  // creating media log params
  useEffect(() => {
    if (panelInfo?.shptmBanrTpNm === 'MEDIA' && isOnTop && !isVODPaused) {
      let logTemplateNo;

      if (panelInfo?.modal) {
        logTemplateNo = getLogTpNo('MEDIA', nowMenu);
        setLogStatus((status) => ({ ...status, isModalMediaLogReady: true }));
      }
      //
      else if (!panelInfo?.modal) {
        logTemplateNo = Config.LOG_TP_NO.VOD.FULL_MEDIA;
        setLogStatus((status) => ({ ...status, isFullMediaLogReady: true }));
      }

      mediaLogParamsRef.current = {
        entryMenu: entryMenuRef.current,
        lgCatCd: panelInfo?.lgCatCd ?? '',
        lgCatNm: panelInfo?.lgCatNm ?? '',
        logTpNo: logTemplateNo,
        linkTpCd: panelInfo?.linkTpCd ?? '',
        nowMenu: nowMenuRef.current,
        patncNm: panelInfo?.patncNm ?? '',
        patnrId: panelInfo?.patnrId ?? '',
        showId: panelInfo?.prdtId ?? panelInfo?.showId,
        showNm: panelInfo?.prdtNm ?? panelInfo?.showNm,
        vdoTpNm: 'Horizontal',
      };
    }
  }, [
    isOnTop,
    isVODPaused,
    nowMenu,
    panelInfo?.lgCatCd,
    panelInfo?.lgCatNm,
    panelInfo?.linkTpCd,
    panelInfo?.modal,
    panelInfo?.patncNm,
    panelInfo?.patnrId,
    panelInfo?.prdtId,
    panelInfo?.prdtNm,
    panelInfo?.showId,
    panelInfo?.showNm,
    panelInfo?.shptmBanrTpNm,
    setLogStatus,
  ]);

  // send log media
  useEffect(() => {
    if (broadcast && Object.keys(broadcast).length === 0 && !isVODPaused) {
      // case: media, modal
      if (logStatus.isModalMediaLogReady && panelInfo?.modal) {
        let watchStrtDt = formatGMTString(new Date());

        watchIntervalMedia.current = setInterval(() => {
          let watchEndDt = formatGMTString(new Date());
          let watchRecord = {
            ...mediaLogParamsRef.current,
            watchStrtDt,
            watchEndDt,
          };
          dispatch(changeLocalSettings({ watchRecord }));
        }, 10000);

        return () => {
          setLogStatus((status) => ({
            ...status,
            isModalMediaLogReady: false,
          }));
          clearInterval(watchIntervalMedia.current);
          dispatch(
            sendLogVOD({ ...mediaLogParamsRef.current, watchStrtDt }, () =>
              dispatch(changeLocalSettings({ watchRecord: {} }))
            )
          );
        };
      }
    }

    // case: media, full
    if (logStatus.isFullMediaLogReady && !panelInfo?.modal) {
      let watchStrtDt = formatGMTString(new Date());

      watchIntervalMedia.current = setInterval(() => {
        let watchEndDt = formatGMTString(new Date());
        let watchRecord = {
          ...mediaLogParamsRef.current,
          watchStrtDt,
          watchEndDt,
        };
        dispatch(changeLocalSettings({ watchRecord }));
      }, 10000);

      return () => {
        setLogStatus((status) => ({
          ...status,
          isFullMediaLogReady: false,
        }));
        clearInterval(watchIntervalMedia.current);
        dispatch(
          sendLogVOD({ ...mediaLogParamsRef.current, watchStrtDt }, () =>
            dispatch(changeLocalSettings({ watchRecord: {} }))
          )
        );
      };
    }
  }, [
    broadcast,
    isVODPaused,
    logStatus.isFullMediaLogReady,
    logStatus.isModalMediaLogReady,
    panelInfo?.modal,
    setLogStatus,
  ]);

  const videoVerticalVisible = useMemo(() => {
    if (playListInfo && playListInfo[selectedIndex]?.vtctpYn === 'Y') {
      return true;
    }
    return false;
  }, [playListInfo, selectedIndex]);

  const handleItemFocus = useCallback(
    (menu) => {
      dispatch(sendLogGNB(menu));

      if (!videoVerticalVisible) {
        resetTimer(REGULAR_TIMEOUT);
      }
    },
    [dispatch, resetTimer, videoVerticalVisible]
  );

  const onClickBack = useCallback(
    (ev, isEnd) => {
      //modal로부터 Full 전환된 경우 다시 preview 모드로 돌아감.

      // TabContainer(v1)만: Side Contents가 보이는 경우 먼저 숨기고 return
      if (
        tabContainerVersion === 1 &&
        sideContentsVisible &&
        !videoVerticalVisible &&
        panelInfo.shptmBanrTpNm !== 'MEDIA'
      ) {
        setSideContentsVisible(false);
        ev?.stopPropagation();
        // ev?.preventDefault();
        return;
      }
      if (panelInfo.modalContainerId && !panelInfo.modal) {
        dispatch(
          startVideoPlayer({
            ...panelInfo,
            modal: true,
            modalClassName: '',
          })
        );
        videoPlayer.current?.hideControls();
        setSelectedIndex(backupInitialIndex);
        if (panelInfo.shptmBanrTpNm === 'MEDIA') {
          dispatch(
            updatePanel({
              name: panel_names.DETAIL_PANEL,
              panelInfo: {
                launchedFromPlayer: false,
              },
            })
          );
        }
        ev?.stopPropagation();
        // ev?.preventDefault();
        return;
      }

      if (!panelInfo.modal) {
        console.log('[PlayerPanel] popPanel - closeButtonHandler');
        dispatch(PanelActions.popPanel());
        dispatch(changeAppStatus({ cursorVisible: false }));

        //딮링크로 플레이어 진입 후 이전버튼 클릭시
        if (panels.length === 1) {
          timeoutRef.current = setTimeout(() => {
            Spotlight.focus(SpotlightIds.HOME_TBODY);
          });
        }
        ev?.stopPropagation();
        // ev?.preventDefault();
        return;
      }
    },
    [
      dispatch,
      panelInfo,
      videoPlayer,
      sideContentsVisible,
      videoVerticalVisible,
      backupInitialIndex,
      panels,
      tabContainerVersion,
    ]
  );

  useEffect(() => {
    //todo if(modal)
    return () => {
      // 패널이 2개 존재할때만 popPanel 진행
      if (panelInfo.modal && !isOnTop) {
        console.log('[PlayerPanel] popPanel - useEffect cleanup');
        dispatch(PanelActions.popPanel());
      } else {
        Spotlight.focus('tbody');
      }
    };
  }, [panelInfo?.modal, isOnTop]);

  useEffect(() => {
    if (showNowInfos && panelInfo.shptmBanrTpNm === 'LIVE') {
      const period = showNowInfos.period !== undefined ? showNowInfos.period : 30;
      const periodInMilliseconds = period * 1000;

      const timer = setTimeout(() => {
        dispatch(
          getMainLiveShowNowProduct({
            patnrId: panelInfo.patnrId ? panelInfo.patnrId : playListInfo[selectedIndex].patnrId,
            showId: panelInfo.showId ? panelInfo.showId : playListInfo[selectedIndex].showId,
            lstChgDt: showNowInfos.lstChgDt,
          })
        );
      }, periodInMilliseconds);

      return () => {
        clearTimeout(timer);
      };
    }
  }, [showNowInfos, panelInfo]);

  const videoItemFocused = useCallback(() => {
    // 아이템클릭 진입시 포커스
    let hasProperSpot = false;
    let targetId;
    if (!isInitialFocusOccurred) {
      targetId = panelInfo.targetId;

      initialFocusTimeoutJob.current.start(() => {
        const initialFocusTarget = findSelector(`[data-spotlight-id="${targetId}"]`);

        if (initialFocusTarget) {
          hasProperSpot = true;
          Spotlight.focus(initialFocusTarget);
          setIsInitialFocusOccurred(true);

          return;
        }
      });
    }
  }, [isInitialFocusOccurred, panelInfo.targetId, panelInfo.modal, videoVerticalVisible]);

  const videoInitialFocused = useCallback(() => {
    if (panelInfo.isUpdatedByClick || !isOnTop) {
      return;
    }
    setContainerLastFocusedElement(null, ['playVideoShopNowBox']);

    // 세로형모드 포커스
    if (videoVerticalVisible) {
      Spotlight.focus('tab-0');
      return;
    }

    // 화살표버튼 포커스
    const current = Spotlight.getCurrent();
    let hasProperSpot = false;
    if (current) {
      const spotId = current.getAttribute('data-spotlight-id');
      if (spotId && spotId.indexOf('tabChannel-video') >= 0) {
        hasProperSpot = true;
      }
    }

    if (!panelInfo.modal && !videoVerticalVisible && !hasProperSpot) {
      if (tabContainerVersion === 1) {
        Spotlight.focus(SpotlightIds.PLAYER_TAB_BUTTON);
      } else if (tabContainerVersion === 2) {
        Spotlight.focus('below-tab-live-channel-button');
      }
      return;
    }
    //비디오 진입시 포커스
    if (panelInfo.isIndicatorByClick && shopNowInfo?.length > 0) {
      Spotlight.focus('playVideoShopNowBox');
      return;
    }
  }, [
    shopNowInfo,
    videoVerticalVisible,
    isOnTop,
    panelInfo.modal,
    panelInfo.isUpdatedByClick,
    panelInfo.isIndicatorByClick,
    panelInfo.shptmBanrTpNm,
    tabContainerVersion,
  ]);

  // 최상단 패널 정보 (여러 useMemo에서 공통으로 사용)
  const topPanel = useMemo(() => {
    return panels[panels.length - 1];
  }, [panels]);

  // 최상단 패널이 DetailPanel이고 PlayerPanel에서 진입했는지 확인
  const isTopPanelDetailFromPlayer = useMemo(() => {
    const result =
      topPanel?.name === panel_names.DETAIL_PANEL &&
      topPanel?.panelInfo?.launchedFromPlayer === true;

    // 🔍 DetailPanel 상태 변화 로깅
    if (result) {
      dlog('🎬 [PlayerPanel] DetailPanel is now on top (from Player)', {
        topPanelName: topPanel?.name,
        launchedFromPlayer: topPanel?.panelInfo?.launchedFromPlayer,
        modalPlayerPanelExists: panels.some(
          (p) => p.name === panel_names.PLAYER_PANEL && p.panelInfo?.modal
        ),
        currentVideoState: {
          isPlaying: videoPlayState?.isPlaying,
          isPaused: videoPlayState?.isPaused,
          currentTime: videoPlayState?.currentTime,
        },
        timestamp: new Date().toISOString(),
      });
    }

    return result;
  }, [topPanel, panels, videoPlayState]);

  // 🔍 PlayerPanel이 밑에 깔렸을 때 자신의 상태를 업데이트하는 useEffect
  useEffect(() => {
    const isDetailPanelOnTop = topPanel?.name === panel_names.DETAIL_PANEL;
    const isModalPlayerPanel = panelInfo?.modal === true;
    const isCurrentPanelOnTop = topPanel?.name === panel_names.PLAYER_PANEL;

    // PlayerPanel이 modal이고 밑에 깔렸을 때
    if (isModalPlayerPanel && !isCurrentPanelOnTop && isDetailPanelOnTop) {
      dlog('🔴 [PlayerPanel] Self-pausing due to DetailPanel on top', {
        isDetailPanelOnTop,
        isModalPlayerPanel,
        isCurrentPanelOnTop,
        currentReduxState: videoPlayState,
        needsPause: videoPlayState?.isPlaying === true || videoPlayState?.isPaused === false,
        timestamp: new Date().toISOString(),
      });

      // PlayerPanel 자신의 상태를 일시정지로 업데이트
      if (videoPlayState?.isPlaying === true || videoPlayState?.isPaused === false) {
        dlog('🔄 [PlayerPanel] Dispatching self-pause to Redux');
        dispatch(
          updateVideoPlayState({
            isPlaying: false,
            isPaused: true,
            currentTime: videoPlayState?.currentTime || 0,
            duration: videoPlayState?.duration || 0,
            playbackRate: videoPlayState?.playbackRate || 1,
            source: 'PlayerPanel-self-pause',
          })
        );
      }
    }
  }, [topPanel?.name, panelInfo?.modal, videoPlayState, dispatch]);

  const cannotPlay = useMemo(() => {
    return !isOnTop && topPanel?.name === panel_names.PLAYER_PANEL;
  }, [topPanel, isOnTop]);

  const getPlayer = useCallback((ref) => {
    videoPlayer.current = ref;
  }, []);

  /** for VOD */
  const addPanelInfoToPlayList = useCallback(
    (featuredShowsInfos) => {
      if (Array.isArray(featuredShowsInfos)) {
        const showId = showDetailInfo[0]?.showId;

        const index = featuredShowsInfos.findIndex((show) => show.showId === showId);

        let newArray = [];
        if (index > -1) {
          // 인덱스를 찾은 경우 그대로
          newArray = [...featuredShowsInfos];
          setBackupInitialIndex(index);
          setSelectedIndex(index);
        } else {
          // 인덱스를 찾지 못한 경우 showDetailInfo 를 제일 앞에 배치
          newArray = [{ ...showDetailInfo[0] }, ...featuredShowsInfos];
          setBackupInitialIndex(0);
          setSelectedIndex(0);
        }
        setPlayListInfo(newArray);
      }
    },

    [showDetailInfo]
  );

  useEffect(() => {
    if (panelInfo.shptmBanrTpNm === 'LIVE') {
      if (panelInfo.patnrId && panelInfo.showId) {
        dispatch(
          getMainCategoryShowDetail({
            patnrId: panelInfo.patnrId,
            showId: panelInfo.showId,
            curationId: panelInfo.curationId,
          })
        );
      }
      dispatch(getMainLiveShow({ vodIncFlag: 'Y' }));
    }
  }, [
    dispatch,
    panelInfo?.curationId,
    panelInfo?.lgCatCd,
    panelInfo?.patnrId,
    panelInfo?.showId,
    panelInfo?.shptmBanrTpNm,
  ]);

  useEffect(() => {
    // console.log('[PlayerPanel] VOD useEffect 진입', {
    //   shptmBanrTpNm: panelInfo.shptmBanrTpNm,
    //   panelInfoShowId: panelInfo.showId,
    //   showDetailInfoLength: showDetailInfo?.length,
    //   showDetailInfoId: showDetailInfo?.[0]?.showId,
    // });

    if (panelInfo.shptmBanrTpNm === 'VOD' && showDetailInfo && showDetailInfo.length > 0) {
      // console.log('[PlayerPanel] VOD 조건 만족');
      // 현재 panelInfo의 showId와 showDetailInfo의 showId가 일치할 때만 처리
      if (showDetailInfo[0]?.showId === panelInfo.showId) {
        // console.log('[PlayerPanel] showId 일치! 동영상 설정 시작', {
        //   showId: showDetailInfo[0]?.showId,
        //   patnrId: showDetailInfo[0]?.patnrId,
        // });

        if (showDetailInfo[0]?.showCatCd && fullVideolgCatCd !== showDetailInfo[0]?.showCatCd) {
          dispatch(
            getHomeFullVideoInfo({
              lgCatCd: showDetailInfo[0].showCatCd,
            })
          );
        }
        if (showDetailInfo[0].showId && showDetailInfo[0].patnrId) {
          // console.log('[PlayerPanel] setPlayListInfo 호출');

          // featuredShowsInfos가 있으면 addPanelInfoToPlayList로 여러 동영상 처리
          if (featuredShowsInfos && featuredShowsInfos.length > 0) {
            // console.log('[PlayerPanel] addPanelInfoToPlayList 호출 (여러 배너)', {
            //   featuredShowsInfosLength: featuredShowsInfos.length,
            // });
            addPanelInfoToPlayList(featuredShowsInfos);
          } else {
            // featuredShowsInfos가 없으면 현재 showDetailInfo만 설정
            // console.log('[PlayerPanel] setPlayListInfo 호출 (단일 배너만)');
            setPlayListInfo(showDetailInfo);
            setSelectedIndex(0);
          }

          setShopNowInfo(showDetailInfo[0].productInfos);
          saveToLocalSettings(showDetailInfo[0].showId, showDetailInfo[0].patnrId);
        }
      } else {
        // showId가 일치하지 않으면 이전 상태를 재활용하지 않고 초기화
        // console.log('[PlayerPanel] VOD showDetailInfo mismatch. Clearing playListInfo.', {
        //   panelInfoShowId: panelInfo.showId,
        //   showDetailInfoId: showDetailInfo[0]?.showId,
        // });
        dlog('[PlayerPanel] VOD showDetailInfo mismatch. Clearing playListInfo.', {
          panelInfoShowId: panelInfo.showId,
          showDetailInfoId: showDetailInfo[0]?.showId,
        });
        setPlayListInfo(null);
        setSelectedIndex(null);
        setShopNowInfo(null);
      }
    }
  }, [
    showDetailInfo,
    panelInfo.showId,
    panelInfo.shptmBanrTpNm,
    fullVideolgCatCd,
    featuredShowsInfos,
  ]);

  //LIVE
  useEffect(() => {
    if (playListInfo && playListInfo.length > 0 && panelInfo.shptmBanrTpNm === 'LIVE') {
      if (playListInfo[selectedIndex]?.patnrId) {
        dispatch(
          getMainLiveShowNowProduct({
            patnrId: playListInfo[selectedIndex]?.patnrId,
            showId: playListInfo[selectedIndex]?.showId,
          })
        );
      }

      if (playListInfo[selectedIndex]?.catCd) {
        dispatch(
          getHomeFullVideoInfo({
            lgCatCd: playListInfo[selectedIndex]?.catCd,
          })
        );
      }
    }
  }, [selectedIndex]);

  useEffect(() => {
    if (showDetailInfo && showDetailInfo.length > 0) {
      dispatch(CLEAR_PLAYER_INFO());

      if (
        showDetailInfo[0]?.liveFlag === 'N' &&
        showDetailInfo[0]?.chatLogFlag === 'Y' &&
        panelInfo.shptmBanrTpNm === 'VOD'
      ) {
        dispatch(getChatLog({ patnrId: panelInfo.patnrId, showId: panelInfo.showId }));
      }
    }
  }, [showDetailInfo]);

  // videoClick focused
  useEffect(() => {
    if (playListInfo && playListInfo.length > 0) {
      if (panelInfo.targetId) {
        videoItemFocused();
      } else {
        videoInitialFocused();
      }
    }
  }, [playListInfo]);

  //10초 후 닫힐때 TabButton 포커스
  useEffect(() => {
    if (playListInfo && playListInfo.length > 0) {
      videoInitialFocused();
    }
  }, [sideContentsVisible, panelInfo.modal]);

  // liveChannel initial selectedIndex
  useEffect(() => {
    if (panelInfo?.shptmBanrTpNm === 'LIVE' && playListInfo?.length > 0) {
      const index = playListInfo.findIndex((item) => item.chanId === panelInfo.chanId);
      if (index !== -1 && !isUpdate) {
        setBackupInitialIndex(index);
        setSelectedIndex(index);
        setIsUpdate(true);
      }
    }
  }, [panelInfo?.shptmBanrTpNm, playListInfo]);

  // 컴포넌트 언마운트 시 Job 정리
  useEffect(() => {
    return () => {
      initialFocusTimeoutJob.current?.stop?.();
    };
  }, []);

  // live subtitle Luna API
  useEffect(() => {
    if (currentSubtitleBlob) {
      return;
    } else if (isYoutube) {
      if (mediaId) {
        dispatch(requestLiveSubtitle({ mediaId, enable: false }));
        setMediaId(null);
      }
      return;
      //do caption action on VideoPlayer(componentDidUpdate)
    } else {
      if (mediaId && captionEnable && isSubtitleActive && !panelInfo?.modal) {
        dispatch(requestLiveSubtitle({ mediaId, enable: true }));
      } else {
        if (mediaId) {
          dispatch(requestLiveSubtitle({ mediaId, enable: false }));
        }
      }
    }
  }, [
    mediaId,
    isYoutube,
    captionEnable,
    isSubtitleActive,
    currentSubtitleBlob,
    panelInfo.modal,
    panelInfo.shptmBanrTpNm,
  ]);

  // get PlayListInfo
  useEffect(() => {
    if (panelInfo?.shptmBanrTpNm === 'VOD') {
      if (showDetailInfo && showDetailInfo.length > 0) {
        if (featuredShowsInfos && featuredShowsInfos.length > 0) {
          addPanelInfoToPlayList(featuredShowsInfos);
        }
      }
    }
  }, [featuredShowsInfos]);

  // get PlayListInfo
  useEffect(() => {
    if (!panelInfo) return;

    switch (panelInfo.shptmBanrTpNm) {
      case 'LIVE': {
        const playlist = liveShowInfos ?? liveChannelInfos;

        if (!Array.isArray(playlist)) return;

        const modifiedList = [];

        playlist.forEach((item) => {
          if (item.showType === 'vod' && Array.isArray(item.vodInfos)) {
            const mergedVodInfos = item.vodInfos.map((vod) => ({
              ...vod,
              patnrId: item.patnrId,
              patncNm: item.patncNm,
              patncLogoPath: item.patncLogoPath,
              showType: 'vod',
            }));

            modifiedList.push(...mergedVodInfos);
          } else {
            modifiedList.push(item);
          }
        });

        setPlayListInfo(modifiedList);

        if (showNowInfos?.prdtChgYn === 'N') {
          return;
        }

        if (showNowInfos || showDetailInfo?.length > 0) {
          const productInfos = showNowInfos
            ? showNowInfos.productInfos
            : showDetailInfo[0]?.productInfos;
          setShopNowInfo(productInfos);
        }
        break;
      }
      case 'MEDIA':
        setPlayListInfo([panelInfo]);
        break;
      default:
        break;
    }
  }, [
    panelInfo,
    showDetailInfo,
    featuredShowsInfos,
    liveChannelInfos,
    liveShowInfos,
    showNowInfos,
    dispatch,
  ]);

  const liveTotalTime = useMemo(() => {
    let liveTotalTime;
    if (liveShowInfos && panelInfo?.shptmBanrTpNm === 'LIVE') {
      const startDtMoment = new Date(liveShowInfos[selectedIndex]?.strtDt);
      const endDtMoment = new Date(liveShowInfos[selectedIndex]?.endDt);

      liveTotalTime = Math.floor((endDtMoment - startDtMoment) / 1000);

      return liveTotalTime;
    }
  }, [liveShowInfos, selectedIndex, panelInfo.shptmBanrTpNm]);

  useEffect(() => {
    const handleVisibilityChange = () => {
      if (
        document.visibilityState === 'visible' &&
        liveShowInfos &&
        panelInfo?.shptmBanrTpNm === 'LIVE'
      ) {
        const localStartDt = convertUtcToLocal(liveShowInfos[selectedIndex]?.strtDt);

        const curDt = new Date();
        const localStartSec = localStartDt?.getTime() / 1000;
        const curSec = curDt?.getTime() / 1000;

        const calculatedLiveTime = curSec - localStartSec;
        if (calculatedLiveTime >= liveTotalTime) {
          setCurrentLiveTimeSeconds(0);
        } else {
          setCurrentLiveTimeSeconds(calculatedLiveTime);
        }
      }
    };

    document.addEventListener('visibilitychange', handleVisibilityChange);

    if (panelInfo.offsetHour) {
      setCurrentLiveTimeSeconds(parseInt(panelInfo.offsetHour));
    } else if (liveShowInfos && panelInfo?.shptmBanrTpNm === 'LIVE') {
      const localStartDt = convertUtcToLocal(liveShowInfos[selectedIndex]?.strtDt);

      const curDt = new Date();
      const localStartSec = localStartDt?.getTime() / 1000;
      const curSec = curDt?.getTime() / 1000;

      const calculatedLiveTime = curSec - localStartSec;
      if (calculatedLiveTime >= liveTotalTime) {
        setCurrentLiveTimeSeconds(0);
      } else {
        setCurrentLiveTimeSeconds(calculatedLiveTime);
      }
    } else {
      setCurrentLiveTimeSeconds(0);
    }

    return () => {
      document.removeEventListener('visibilitychange', handleVisibilityChange);
    };
  }, [
    liveShowInfos,
    selectedIndex,
    panelInfo.offsetHour,
    panelInfo.shptmBanrTpNm,
    playListInfo,
    liveTotalTime,
  ]);

  useEffect(() => {
    if (panelInfo.shptmBanrTpNm == 'LIVE' && liveTotalTime > 0) {
      const interval = setInterval(() => {
        setCurrentLiveTimeSeconds((prev) => {
          if (prev >= liveTotalTime) {
            return 1;
          }
          return prev + 1;
        });
      }, 1000);

      return () => clearInterval(interval);
    }
  }, [liveTotalTime]);

  useEffect(() => {
    if (currentLiveTimeSeconds > liveTotalTime) {
      timeoutRef.current = setTimeout(() => {
        dispatch(getMainLiveShow());
        setShopNowInfo('');
        dispatch(
          getHomeFullVideoInfo({
            lgCatCd: playListInfo[selectedIndex].showCatCd,
          })
        );
      }, 3000);

      return () => {
        if (timeoutRef.current) clearTimeout(timeoutRef.current);
      };
    }
  }, [currentLiveTimeSeconds, liveTotalTime, dispatch, playListInfo, selectedIndex]);

  useEffect(() => {
    return () => {
      if (timeoutRef.current) clearTimeout(timeoutRef.current);
      if (watchIntervalLive.current) clearInterval(watchIntervalLive.current);
      if (watchIntervalVod.current) clearInterval(watchIntervalVod.current);
      if (watchIntervalMedia.current) clearInterval(watchIntervalMedia.current);
    };
  }, []);

  const mediainfoHandler = useCallback(
    (ev) => {
      const type = ev.type;
      if (type !== 'timeupdate' && type !== 'durationchange') {
        dlog('mediainfoHandler....', type, ev, videoPlayer.current?.getMediaState());
      }
      if (ev === 'hlsError' && isNaN(Number(videoPlayer.current?.getMediaState().playbackRate))) {
        dispatch(
          sendBroadCast({
            type: 'videoError',
            moreInfo: { reason: 'hlsError' },
          })
        );

        return;
      }

      switch (type) {
        case 'timeupdate': {
          setCurrentTime(videoPlayer.current?.getMediaState()?.currentTime);
          break;
        }
        case 'error': {
          dispatch(
            sendBroadCast({
              type: 'videoError',
              moreInfo: { reason: videoPlayer.current?.getMediaState().error },
            })
          );
          break;
        }
        case 'loadeddata': {
          const mediaId = videoPlayer.current?.video?.media?.mediaId;
          setMediaId(mediaId);
          setVideoLoaded(true);
          dlog(
            '[PlayerPanel] 🎬 Video Loaded - shptmBanrTpNm:',
            panelInfoRef.current?.shptmBanrTpNm
          );
        }
      }
    },
    [dispatch]
  );

  useEffect(() => {
    // case: video error when the video is in fullscreen mode
    if (
      broadcast?.type === 'videoError' &&
      isOnTop &&
      !panelInfo?.modal &&
      panelInfo?.modalContainerId
    ) {
      // case: Featured Brands
      // if (panelInfo?.sourcePanel === panel_names.FEATURED_BRANDS_PANEL) {
      //   dispatch(PanelActions.popPanel());
      // }
      console.log('[PlayerPanel] Condition 4: Handling video error in fullscreen mode');
    }
  }, [
    broadcast?.type,
    isOnTop,
    panelInfo?.modal,
    panelInfo?.modalContainerId,
    panelInfo?.sourcePanel,
  ]);

  useEffect(() => {
    // 복구 직후: skipModalStyleRecalculation이 true면 저장된 modalStyle 적용
    if (panelInfo.skipModalStyleRecalculation && !panelInfo.shouldShrinkTo1px) {
      dlog('[PlayerPanel] Condition 2.5: Using saved modalStyle from expand');
      const shrinkInfo = panelInfo.playerState?.shrinkInfo;

      // 저장된 modalStyle 사용 (top, left 포함)
      if (shrinkInfo?.modalStyle) {
        setModalStyle(shrinkInfo.modalStyle);
        setModalScale(panelInfo.modalScale || shrinkInfo.modalScale);
      } else {
        setModalStyle(panelInfo.modalStyle);
        setModalScale(panelInfo.modalScale);
      }

      // DOM 렌더링 후 플래그 제거
      if (typeof window !== 'undefined') {
        window.requestAnimationFrame(() => {
          window.requestAnimationFrame(() => {
            dlog('[PlayerPanel] Condition 2.5: Removing skipFlag after DOM render');
            dispatch(
              updatePanel({
                name: panel_names.PLAYER_PANEL,
                panelInfo: {
                  ...panelInfo,
                  skipModalStyleRecalculation: false,
                },
              })
            );
          });
        });
      }
    }

    if (
      panelInfo.modal &&
      !panelInfo.shouldShrinkTo1px &&
      panelInfo.modalContainerId &&
      (lastPanelAction === 'previewPush' || lastPanelAction === 'previewUpdate')
    ) {
      dlog('[PlayerPanel] Condition 1: Calculating modalStyle from DOM', {
        lastPanelAction,
      });
      const node = document.querySelector(`[data-spotlight-id="${panelInfo.modalContainerId}"]`);
      if (node) {
        const { width, height, top, left } = node.getBoundingClientRect();
        const modalStyle = {
          width: width + 'px',
          height: height + 'px',
          top: top + 'px',
          left: left + 'px',
          position: 'fixed',
          overflow: 'visible',
        };
        setModalStyle(modalStyle);
        let scale = 1;
        if (typeof window === 'object') {
          scale = width / window.innerWidth;
          setModalScale(scale);
        }
        dispatch(
          updatePanel({
            name: panel_names.PLAYER_PANEL,
            panelInfo: { modalStyle: modalStyle, modalScale: scale },
          })
        );
      } else {
        dlog('[PlayerPanel] Condition 1: Node not found, using saved modalStyle');
        setModalStyle(panelInfo.modalStyle);
        setModalScale(panelInfo.modalScale);
      }
    } else if (panelInfo.shouldShrinkTo1px) {
      dlog('[PlayerPanel] Condition 2: Shrinking - clearing modalStyle');
      // 축소 상태: 인라인 스타일 제거 (CSS만 적용)
      setModalStyle({});
      setModalScale(1);
    } else if (isOnTop && !panelInfo.modal && videoPlayer.current) {
      dlog('[PlayerPanel] Condition 3: Playing fullscreen video');
      if (videoPlayer.current?.getMediaState()?.paused) {
        videoPlayer.current.play();
      }

      if (videoPlayer.current.areControlsVisible && !videoPlayer.current.areControlsVisible()) {
        videoPlayer.current.showControls();
      }
    }
  }, [panelInfo, isOnTop, dispatch]);

  const smallestOffsetHourIndex = useMemo(() => {
    if (shopNowInfo) {
      const filteredVideos = shopNowInfo.filter((video) => video.offsetHour >= currentTime);
      const newSmallestOffsetHour = Math.min(...filteredVideos.map((video) => video.offsetHour));

      const newSmallestOffsetHourIndex = shopNowInfo.findIndex(
        (video) => video.offsetHour === newSmallestOffsetHour.toString()
      );

      if (shopNowInfo.length === 1) {
        return 0;
      }
      if (newSmallestOffsetHourIndex >= 1) {
        return newSmallestOffsetHourIndex - 1;
      }
      return newSmallestOffsetHourIndex;
    }
  }, [shopNowInfo, currentTime]);

  const currentSubtitleUrl = useMemo(() => {
    if (panelInfo?.shptmBanrTpNm === 'MEDIA') {
      return panelInfo.subtitle;
    }
    return playListInfo && playListInfo[selectedIndex]?.showSubtitlUrl;
  }, [playListInfo, selectedIndex, panelInfo]);

  const currentPlayingUrl = useMemo(() => {
    // return "https://download.blender.org/peach/bigbuckbunny_movies/BigBuckBunny_320x180.mp4";

    if (broadcast.type === 'videoError') {
      return null;
    }

    return playListInfo && playListInfo[selectedIndex]?.showUrl;
  }, [playListInfo, selectedIndex, broadcast]);

  const isYoutube = useMemo(() => {
    if (currentPlayingUrl && currentPlayingUrl.includes('youtu')) {
      return true;
    } else {
      return false;
    }
  }, [currentPlayingUrl]);

  const currentSubtitleBlob = useMemo(() => {
    if (Config.DEBUG_VIDEO_SUBTITLE_TEST) {
      return dummyVtt;
    }
    return vodSubtitleData[currentSubtitleUrl];
  }, [vodSubtitleData, currentSubtitleUrl]);

  // 자막 Blob URL 수명 관리: 이전 Blob을 해제해 메모리 누수 방지
  useEffect(() => {
    const prevBlobUrl = previousSubtitleBlobRef.current;

    // 더 엄격한 타입과 형식 체크
    if (prevBlobUrl &&
        typeof prevBlobUrl === 'string' &&
        prevBlobUrl.startsWith('blob:') &&
        prevBlobUrl !== currentSubtitleBlob) {
      try {
        URL.revokeObjectURL(prevBlobUrl);
        dlog('[PlayerPanel] Previous subtitle Blob URL revoked:', prevBlobUrl);
      } catch (error) {
        derror('[PlayerPanel] Failed to revoke Blob URL:', error);
      }
    }

    previousSubtitleBlobRef.current = currentSubtitleBlob;

    return () => {
      const lastBlobUrl = previousSubtitleBlobRef.current;
      if (lastBlobUrl && typeof lastBlobUrl === 'string' && lastBlobUrl.startsWith('blob:')) {
        try {
          URL.revokeObjectURL(lastBlobUrl);
          dlog('[PlayerPanel] Final subtitle Blob URL revoked:', lastBlobUrl);
        } catch (error) {
          derror('[PlayerPanel] Failed to revoke final Blob URL:', error);
        }
      }
      previousSubtitleBlobRef.current = null;
    };
  }, [currentSubtitleBlob]);

  const isReadyToPlay = useMemo(() => {
    if (!currentPlayingUrl) {
      return false;
    }
    if (!Config.DEBUG_VIDEO_SUBTITLE_TEST && currentSubtitleUrl && !currentSubtitleBlob) {
      return false;
    }
    return true;
  }, [currentPlayingUrl, currentSubtitleUrl, currentSubtitleBlob, broadcast]);

  const chatVisible = useMemo(() => {
    if (
      playListInfo &&
      chatData &&
      !panelInfo.modal &&
      isOnTop &&
      panelInfo?.shptmBanrTpNm !== 'MEDIA'
    ) {
      return true;
    }
    return false;
  }, [playListInfo, chatData, panelInfo.modal, isOnTop]);

  useEffect(() => {
    if (currentSubtitleUrl) {
      dispatch(getSubTitle({ showSubtitleUrl: currentSubtitleUrl }));
    }

    // 이전 자막 URL 정리 (Redux 메모리 누수 방지)
    const prevSubtitleUrl = previousSubtitleUrlRef.current;
    if (prevSubtitleUrl && prevSubtitleUrl !== currentSubtitleUrl) {
      dispatch(clearSubtitleBlob(prevSubtitleUrl));
      dlog('[PlayerPanel] Clearing previous subtitle URL:', prevSubtitleUrl);
    }

    previousSubtitleUrlRef.current = currentSubtitleUrl;

    // 컴포넌트 언마운트 시 마지막 자막 URL 정리
    return () => {
      if (previousSubtitleUrlRef.current) {
        dispatch(clearSubtitleBlob(previousSubtitleUrlRef.current));
      }
    };
  }, [currentSubtitleUrl, dispatch]);

  useEffect(() => {
    setVideoLoaded(false);
  }, [currentPlayingUrl]);

  // 비디오가 새로 선택될 때 타이머 초기화
  useEffect(() => {
    if (currentPlayingUrl) {
      dlog('[PlayerPanel] 🎬 비디오 선택됨 - tabIndexV2 타이머 초기화');
      resetTimerTabAutoAdvance(10000);
    }
  }, [selectedIndex, resetTimerTabAutoAdvance]);

  const handlePopupClose = useCallback(() => {
    dispatch(setHidePopup());
    timeoutRef.current = setTimeout(() => Spotlight.focus(SpotlightIds.PLAYER_SUBTITLE_BUTTON));
  }, [dispatch]);
  const reactPlayerSubtitleConfig = useMemo(() => {
    if (isSubtitleActive && currentSubtitleBlob) {
      return {
        file: {
          attributes: {
            crossOrigin: 'true',
            preload: 'metadata',
          },
          tracks: [{ kind: 'subtitles', src: currentSubtitleBlob, default: true }],
          hlsOptions: {
            // 버퍼 길이를 60초 기준으로 설정
            maxBufferLength: 60,
            maxMaxBufferLength: 180,
            backBufferLength: 0,
            maxBufferSize: 100 * 1000 * 1000, // 최대 버퍼 크기 100MB
            liveSyncDuration: 16,
            liveMaxLatencyDuration: 32,
          },
        },
        youtube: YOUTUBECONFIG,
      };
    } else {
      return {
        youtube: YOUTUBECONFIG,
        file: {
          attributes: {
            preload: 'metadata',
          },
          hlsOptions: {
            maxBufferLength: 60,
            maxMaxBufferLength: 180,
            backBufferLength: 0,
            maxBufferSize: 100 * 1000 * 1000,
            liveSyncDuration: 16,
            liveMaxLatencyDuration: 32,
          },
        },
      };
    }
  }, [currentSubtitleBlob, isSubtitleActive]);

  const currentSideButtonStatus = useMemo(() => {
    if (panelInfo?.shptmBanrTpNm !== 'MEDIA' && !panelInfo?.modal && sideContentsVisible) {
      return true;
    }
    return false;
  }, [panelInfo, sideContentsVisible]);

  const videoType = useMemo(() => {
    if (currentPlayingUrl) {
      if (currentPlayingUrl.toLowerCase().endsWith('.mp4')) {
        return 'video/mp4';
      } else if (currentPlayingUrl.toLowerCase().endsWith('.mpd')) {
        return 'application/dash+xml';
      } else if (currentPlayingUrl.toLowerCase().endsWith('.m3u8')) {
        return 'application/mpegurl';
      }
    }
    return 'application/mpegurl';
  }, [currentPlayingUrl]);

  const orderPhoneNumber = useMemo(() => {
    if (panelInfo?.shptmBanrTpNm !== 'MEDIA' && showDetailInfo) {
      return showDetailInfo[0]?.orderPhnNo;
    } else {
      return playListInfo[selectedIndex]?.orderPhnNo;
    }
  }, [panelInfo?.shptmBanrTpNm, showDetailInfo, playListInfo, selectedIndex]);

  const videoThumbnailUrl = useMemo(() => {
    let res = null;

    if (panelInfo.shptmBanrTpNm === 'MEDIA') {
      res = panelInfo?.thumbnailUrl;
    } else if (playListInfo && playListInfo.length > 0) {
      res = playListInfo[selectedIndex]?.thumbnailUrl;
    } else if (!res) {
      res = showDetailInfo[0]?.thumbnailUrl;
    }

    return res;
  }, [
    showDetailInfo,
    playListInfo,
    selectedIndex,
    panelInfo.thumbnailUrl,
    panelInfo.shptmBanrTpNm,
  ]);

  const saveToLocalSettings = useCallback(
    (showId, patnrId) => {
      let recentItems = [];
      if (localRecentItems) {
        recentItems = [...localRecentItems];
      }

      const currentDate = new Date();

      const formattedDate = `${currentDate.getMonth() + 1}/${currentDate.getDate()}`;

      const existingProductIndex = recentItems.findIndex((item) => {
        if (item.showId) return item.showId === showId;
      });

      if (existingProductIndex !== -1) {
        recentItems.splice(existingProductIndex, 1);
      }

      recentItems.push({
        patnrId: patnrId,
        showId: showId,
        date: formattedDate,
        expireTime: currentDate.getTime() + 1000 * 60 * 60 * 24 * 14,
        cntryCd: httpHeader['X-Device-Country'],
      });

      if (recentItems.length >= 51) {
        const data = [...recentItems];
        dispatch(changeLocalSettings({ recentItems: data.slice(1) }));
      } else {
        dispatch(changeLocalSettings({ recentItems }));
      }
    },
    [httpHeader, localRecentItems, dispatch]
  );

  const handleIndicatorDownClick = useCallback(() => {
    if (!initialEnter) {
      setInitialEnter(true);
    }

    let newIndex = selectedIndex === playListInfo.length - 1 ? 0 : selectedIndex + 1;
    let initialIndex = newIndex;
    let attempts = 0;

    while (!playListInfo[newIndex]?.showId && attempts < playListInfo.length) {
      newIndex = newIndex === playListInfo.length - 1 ? 0 : newIndex + 1;
      attempts++;
      if (newIndex === initialIndex) break;
    }
    if (playListInfo[newIndex]?.showId) {
      setSelectedIndex(newIndex);
      if (panelInfo.shptmBanrTpNm === 'VOD') {
        dispatch(
          getMainCategoryShowDetail({
            patnrId: playListInfo[newIndex]?.patnrId,
            showId: playListInfo[newIndex]?.showId,
            curationId: playListInfo[newIndex]?.curationId,
          })
        );
        Spotlight.focus('playVideoShopNowBox');
      } else {
        dispatch(
          updatePanel({
            name: panel_names.PLAYER_PANEL,
            panelInfo: {
              chanId: playListInfo[newIndex].chanId,
              patnrId: playListInfo[newIndex].patnrId,
              showId: playListInfo[newIndex].showId,
              shptmBanrTpNm: panelInfo?.shptmBanrTpNm,
              isIndicatorByClick: true,
            },
          })
        );
      }
    }
    if (!sideContentsVisible) {
      setPrevChannelIndex(selectedIndex);
    }
    setSideContentsVisible(true);
  }, [dispatch, playListInfo, selectedIndex, sideContentsVisible, initialEnter]);

  const handleIndicatorUpClick = useCallback(() => {
    if (!initialEnter) {
      setInitialEnter(true);
    }

    let newIndex = selectedIndex === 0 ? playListInfo.length - 1 : selectedIndex - 1;
    let initialIndex = newIndex;
    let attempts = 0;

    while (!playListInfo[newIndex]?.showId && attempts < playListInfo.length) {
      newIndex = newIndex === 0 ? playListInfo.length - 1 : newIndex - 1;
      attempts++;
      if (newIndex === initialIndex) break;
    }

    if (playListInfo[newIndex]?.showId) {
      setSelectedIndex(newIndex);
      if (panelInfo.shptmBanrTpNm === 'VOD') {
        dispatch(
          getMainCategoryShowDetail({
            patnrId: playListInfo[newIndex]?.patnrId,
            showId: playListInfo[newIndex]?.showId,
            curationId: playListInfo[newIndex]?.curationId,
          })
        );
        Spotlight.focus('playVideoShopNowBox');
      } else {
        dispatch(
          updatePanel({
            name: panel_names.PLAYER_PANEL,
            panelInfo: {
              chanId: playListInfo[newIndex].chanId,
              patnrId: playListInfo[newIndex].patnrId,
              showId: playListInfo[newIndex].showId,
              shptmBanrTpNm: panelInfo?.shptmBanrTpNm,
              isIndicatorByClick: true,
            },
          })
        );
      }
    }
    if (!sideContentsVisible) {
      setPrevChannelIndex(selectedIndex);
    }
    setSideContentsVisible(true);
  }, [dispatch, playListInfo, selectedIndex, sideContentsVisible, initialEnter]);

  useEffect(() => {
    if (panelInfo.shptmBanrTpNm === 'VOD' && panelInfo.patnrId && panelInfo.showId) {
      //VOD의 panelInfo.showId 가 변경된 최초 한번만 호출하고, FearchedShow 항목에서 선택시 또는 상하 indicator 선택시 호출한다.
      dispatch(
        getMainCategoryShowDetail({
          patnrId: panelInfo.patnrId,
          showId: panelInfo.showId,
          curationId: panelInfo.curationId,
        })
      );
    }
  }, [panelInfo.showId]);

  useEffect(() => {
    return () => {
      // console.log('[PlayerPanel] unmount cleanup start');
      cleanupPlayerOnUnmount();
      stopExternalPlayer();
      dispatch(clearShopNowInfo());
      dispatch(CLEAR_PLAYER_INFO());
      setShopNowInfo([]);
      // console.log('[PlayerPanel] unmount cleanup done');
    };
  }, [cleanupPlayerOnUnmount, stopExternalPlayer, dispatch]);

  const focusBackToPanel = useCallback(() => {
    // 포커스를 PlayerPanel 쪽으로 강제 이동해 YouTube iframe이 포커스를 가져가는 것을 차단
    if (focusReturnRef.current) {
      focusReturnRef.current.focus();
      return true;
    }
    return false;
  }, []);

  const focusBackButtonOrFallback = useCallback(() => {
    // 종료 시 Back 아이콘으로 포커스를 이동시키되 실패하면 센티넬로 폴백
    if (Spotlight.focus('player-back-button')) {
      return true;
    }
    return focusBackToPanel();
  }, [focusBackToPanel]);

  const stopExternalPlayer = useCallback(() => {
    const playerInstance = videoPlayer.current;
    const media = playerInstance?.video;

    if (!media) {
      return;
    }

    // ReactPlayer의 YouTube 인스턴스 대응: 종료 직후 리플레이 오버레이가 뜨지 않도록 정지/초기화 시도
    if (typeof media.stopVideo === 'function') {
      media.stopVideo();
    }
    if (typeof media.seekTo === 'function') {
      media.seekTo(0);
    }
    if (typeof media.pause === 'function') {
      media.pause();
    }
  }, []);

  // PlayerPanel 언마운트 시 비디오 자원 강제 해제 (popPanel 시점)
  const cleanupPlayerOnUnmount = useCallback(() => {
    const playerInstance = videoPlayer.current;
    const media = playerInstance?.video;

    try {
      playerInstance?.pause?.();
      playerInstance?.stopVideo?.();
    } catch (err) {
      // ignore
    }

    if (media) {
      try {
        media.pause?.();
        media.stopVideo?.();
        media.seekTo?.(0);
        if ('currentTime' in media) {
          media.currentTime = 0;
        }
        if ('src' in media) {
          media.src = '';
          media.removeAttribute?.('src');
        }
        if ('srcObject' in media) {
          media.srcObject = null;
        }
        media.load?.();
      } catch (err) {
        // ignore
      }
    }
  }, []);

  const onEnded = useCallback(
    (e) => {
      if (panelInfoRef.current.shptmBanrTpNm === 'MEDIA') {
        dispatch(
          updatePanel({
            name: panel_names.DETAIL_PANEL,
            panelInfo: {
              launchedFromPlayer: true,
              isPlayerFinished: true,
            },
          })
        );
        Spotlight.pause();
        timeoutRef.current = setTimeout(() => {
          console.log('[PlayerPanel] popPanel - VIDEO_END_ACTION_DELAY');
          Spotlight.resume();
          dispatch(PanelActions.popPanel());
        }, VIDEO_END_ACTION_DELAY);
        return;
      }
      if (panelInfoRef.current.shptmBanrTpNm === 'VOD') {
        Spotlight.pause();
        timeoutRef.current = setTimeout(() => {
          stopExternalPlayer();
          if (panelInfoRef.current.modal) {
            // 모달 모드에서는 종료 후 화면을 유지하고 Back 아이콘으로 포커스 이동
            videoPlayer.current?.showControls?.();
          } else {
            // 전체화면 모드에서도 종료 후 즉시 닫지 않고 제어권 회수
            videoPlayer.current?.showControls?.();
          }
          Spotlight.resume();
          focusBackButtonOrFallback();
        }, VIDEO_END_ACTION_DELAY);
        e?.stopPropagation();
        e?.preventDefault();
        return;
      }
    },
    [dispatch, focusBackButtonOrFallback, stopExternalPlayer]
  );

  const onKeyDown = (ev) => {
    if (ev.keyCode === 34) {
      handleIndicatorDownClick();
      ev.stopPropagation();
      ev.preventDefault();
    } else if (ev.keyCode === 33) {
      handleIndicatorUpClick();
      ev.stopPropagation();
      ev.preventDefault();
    }
  };

  const [initialEnter, setInitialEnter] = USE_STATE('initialEnter', true);
  const [initialEnterV2, setInitialEnterV2] = USE_STATE('initialEnterV2', true);
  const timerId = useRef(null);
  const timerIdV2 = useRef(null);
  const timerIdTabAutoAdvance = useRef(null);
  const prevTabIndexV2 = useRef(null);

  const showSideContents = useMemo(() => {
    return (
      sideContentsVisible &&
      playListInfo &&
      panelInfo?.shptmBanrTpNm !== 'MEDIA' &&
      !panelInfo?.modal &&
      isOnTop
    );
  }, [sideContentsVisible, playListInfo, panelInfo, isOnTop]);

  const showBelowContents = useMemo(() => {
    return (
      belowContentsVisible &&
      playListInfo &&
      panelInfo?.shptmBanrTpNm !== 'MEDIA' &&
      !panelInfo?.modal &&
      isOnTop
    );
  }, [belowContentsVisible, playListInfo, panelInfo, isOnTop]);

  const qrCurrentItem = useMemo(() => {
    if (shopNowInfo?.length && panelInfo?.shptmBanrTpNm === 'LIVE') {
      return shopNowInfo[shopNowInfo.length - 1];
    }

    if (
      shopNowInfo?.length &&
      smallestOffsetHourIndex >= 0 &&
      panelInfo?.shptmBanrTpNm !== 'LIVE'
    ) {
      return shopNowInfo[smallestOffsetHourIndex];
    }

    if (panelInfo?.shptmBanrTpNm === 'MEDIA' && panelInfo?.qrCurrentItem) {
      return panelInfo.qrCurrentItem;
    }

    return null;
  }, [shopNowInfo, smallestOffsetHourIndex, panelInfo?.shptmBanrTpNm, panelInfo?.qrCurrentItem]);

  const isShowType = useMemo(() => {
    if (['VOD', 'MEDIA'].includes(panelInfo.shptmBanrTpNm)) {
      return panelInfo.shptmBanrTpNm;
    }

    const showType = playListInfo?.[selectedIndex]?.showType;
    if (showType === 'live') return panelInfo.shptmBanrTpNm;
    if (showType === 'vod') return 'VOD';

    return panelInfo.shptmBanrTpNm;
  }, [panelInfo.shptmBanrTpNm, playListInfo, selectedIndex]);
  const clearTimer = useCallback(() => {
    clearTimeout(timerId.current);
    timerId.current = null;
  }, []);

  const resetTimer = useCallback(
    (timeout) => {
      if (timerId.current) {
        clearTimer();
      }

      if (initialEnter) {
        setInitialEnter(false);
      }

      timerId.current = setTimeout(() => {
        setSideContentsVisible(false);
        // setBelowContentsVisible(false);
      }, timeout);
    },
    [clearTimer, initialEnter, setInitialEnter, setSideContentsVisible]
  );

  const clearTimerV2 = useCallback(() => {
    if (timerIdV2.current) {
      dlog('[clearTimerV2] 타이머 클리어됨');
      const stack = new Error().stack;
      const lines = stack.split('\n').slice(1, 4).join(' → ');
      dlog('[clearTimerV2] 호출 스택:', lines);
    }
    clearTimeout(timerIdV2.current);
    timerIdV2.current = null;
  }, []);

  const resetTimerV2 = useCallback(
    (timeout) => {
      dlog('[TabContainerV2] resetTimerV2 호출', timeout);
      if (timerIdV2.current) {
        dlog('[TabContainerV2] 기존 타이머 클리어');
        clearTimerV2();
      }

      if (initialEnterV2) {
        dlog('[TabContainerV2] initialEnterV2 false로 변경');
        setInitialEnterV2(false);
      }

      timerIdV2.current = setTimeout(() => {
        dlog('[TabContainerV2] 타이머 실행 - belowContentsVisible false로 변경 (30초 경과)');
        setBelowContentsVisible(false);
      }, timeout);
    },
    [clearTimerV2, initialEnterV2, setInitialEnterV2, setBelowContentsVisible]
  );

  const clearTimerTabAutoAdvance = useCallback(() => {
    clearTimeout(timerIdTabAutoAdvance.current);
    timerIdTabAutoAdvance.current = null;
  }, []);

  const resetTimerTabAutoAdvance = useCallback(
    (timeout) => {
      if (timerIdTabAutoAdvance.current) {
        clearTimerTabAutoAdvance();
      }

      timerIdTabAutoAdvance.current = setTimeout(() => {
        setTabIndexV2(2);
      }, timeout);
    },
    [clearTimerTabAutoAdvance]
  );

  // Redux로 오버레이 숨김
  useEffect(() => {
    if (shouldHideOverlays) {
      dlog('[PlayerPanel] shouldHideOverlays true - 오버레이 숨김');
      setSideContentsVisible(false);
      dlog('[setBelowContentsVisible] Redux로 오버레이 숨김 - false로 변경');
      setBelowContentsVisible(false);

      if (videoPlayer.current?.hideControls) {
        videoPlayer.current.hideControls();
      }

      // 모든 타이머 클리어
      if (timerId.current) {
        clearTimer();
      }
      if (timerIdV2.current) {
        clearTimerV2();
      }
      if (timerIdTabAutoAdvance.current) {
        clearTimerTabAutoAdvance();
      }

      dispatch(resetPlayerOverlays());
    }
  }, [shouldHideOverlays, dispatch, clearTimer, clearTimerV2, clearTimerTabAutoAdvance]);

  // Redux로 오버레이 표시
  useEffect(() => {
    if (shouldShowOverlays) {
      dlog('[PlayerPanel] shouldShowOverlays true - 오버레이 표시');
      setSideContentsVisible(true);
      dlog('[setBelowContentsVisible] Redux로 오버레이 표시 - true로 변경');
      setBelowContentsVisible(true);

      if (videoPlayer.current?.showControls) {
        videoPlayer.current.showControls();
      }

      dispatch(resetPlayerOverlays());
    }
  }, [shouldShowOverlays, dispatch]);

  // PlayerPanel이 최상단이 될 때 오버레이 표시 (DetailPanel에서 복귀)
  useEffect(() => {
    if (isOnTop && !panelInfo.modal && !videoVerticalVisible) {
      // 정확한 복귀 종류 구분:
      // 1. HomePanel 복귀: modalPrevRef.current === true && prevIsTopPanelDetailFromPlayerRef.current === false
      // 2. DetailPanel 복귀: prevIsTopPanelDetailFromPlayerRef.current === true
      const isHomePanelReturn =
        modalPrevRef.current === true && prevIsTopPanelDetailFromPlayerRef.current === false;
      const isDetailPanelReturn = prevIsTopPanelDetailFromPlayerRef.current === true;

      if (isDetailPanelReturn) {
        dlog('[PlayerPanel] ✅ PlayerPanel 내부 DetailPanel에서 복귀함! - 오버레이 표시');
      } else if (isHomePanelReturn) {
        dlog('[PlayerPanel] 📺 HomePanel에서 복귀함 - 오버레이 표시');
        // HomePanel에서 복귀 시 콘텐츠 타입에 따라 tabIndex 설정
        dlog('[PlayerPanel] 🔄 HomePanel 복귀 - tabIndex를 콘텐츠 타입에 따라 설정');
        if (tabContainerVersion === 2) {
          if (panelInfoRef.current.shptmBanrTpNm === 'VOD') {
            setTabIndexV2(1);
            dlog('[PlayerPanel] 📝 VOD 콘텐츠 - tabIndexV2를 1로 설정됨 (FeaturedShowContents 표시)');
          } else {
            setTabIndexV2(1);
            dlog('[PlayerPanel] 📝 LIVE 콘텐츠 - tabIndexV2를 1로 설정됨 (LiveChannelContents 표시)');
          }
        }
      } else {
        dlog('[PlayerPanel] 🔄 그 외 복귀 - 오버레이 표시');
      }

      setSideContentsVisible(true);
      dlog('[setBelowContentsVisible] 복귀 - true로 변경');
      setBelowContentsVisible(true);
      // VideoPlayer가 belowContentsVisible prop을 감지해서 자동으로 controls 표시함

      // PlayerPanel 내부 DetailPanel에서 복귀 시에만 포커스 복원 시도
      if (isDetailPanelReturn) {
        const lastFocusedTargetId = panelInfo?.lastFocusedTargetId;
        dlog(
          '[PlayerPanel] 🎯 PlayerPanel DetailPanel 복귀 - lastFocusedTargetId:',
          lastFocusedTargetId
        );

        if (lastFocusedTargetId) {
          // ShopNowContents가 렌더링될 때까지 잠시 대기 후 포커스 복원
          timeoutRef.current = setTimeout(() => {
            dlog('[PlayerPanel] 🔍 500ms 후 포커스 복원 시도:', lastFocusedTargetId);
            Spotlight.focus(lastFocusedTargetId);
          }, 500);
        }

        // 한 번 처리한 복귀 플래그는 즉시 해제해 중복 영향을 막는다.
        prevIsTopPanelDetailFromPlayerRef.current = false;
      }

      return () => {
        if (timeoutRef.current) clearTimeout(timeoutRef.current);
      };
    }
  }, [
    isOnTop,
    panelInfo.modal,
    videoVerticalVisible,
    panelInfo?.lastFocusedTargetId,
    tabContainerVersion,
    setTabIndexV2,
  ]);

  // PopularShow에서 처음 호출할 때처럼 modal이 false인 상태에서 VOD/LIVE 구분
  useEffect(() => {
    if (isOnTop && !panelInfo.modal && !videoVerticalVisible && tabContainerVersion === 2) {
      // DetailPanel에서 막 복귀한 경우 탭 인덱스를 초기화하지 않는다.
      if (panelInfo?.detailPanelClosed) {
        return;
      }

      dlog(
        '[PlayerPanel] 📺 Modal false 상태 - shptmBanrTpNm:',
        panelInfoRef.current?.shptmBanrTpNm
      );

      if (panelInfoRef.current?.shptmBanrTpNm === 'VOD') {
        setTabIndexV2(1);
        dlog('[PlayerPanel] 📝 VOD 콘텐츠 - tabIndexV2를 1로 설정됨 (FeaturedShowContents 표시)');
      } else {
        setTabIndexV2(1);
        dlog('[PlayerPanel] 📝 LIVE 콘텐츠 - tabIndexV2를 1로 설정됨 (LiveChannelContents 표시)');
      }
    }
  }, [isOnTop, panelInfo.modal, videoVerticalVisible, tabContainerVersion]);

  useEffect(() => {
    // tabContainerVersion === 1일 때만 실행
    if (tabContainerVersion !== 1) return;

    const node = document.querySelector(`[data-spotlight-id=${TAB_CONTAINER_SPOTLIGHT_ID}]`);

    if (!showSideContents || !node || videoVerticalVisible) return;

    // NOTE 첫 진입 시에는 10초 후 탭이 닫히도록 설정
    if (initialEnter) {
      resetTimer(INITIAL_TIMEOUT);
    }

    const handleEvent = () => resetTimer(REGULAR_TIMEOUT);
    TARGET_EVENTS.forEach((event) => node.addEventListener(event, handleEvent));

    return () => {
      TARGET_EVENTS.forEach((event) => node.removeEventListener(event, handleEvent));

      if (timerId.current) {
        clearTimer();
      }
    };
  }, [
    showSideContents,
    videoVerticalVisible,
    tabContainerVersion,
    resetTimer,
    initialEnter,
    clearTimer,
  ]);

  useEffect(() => {
    if (initialEnter || !sideContentsVisible || videoVerticalVisible) return;

    // NOTE button을 통해 탭을 연 경우 5초 후 탭이 닫히도록 설정
    if (sideContentsVisible) {
      resetTimer(REGULAR_TIMEOUT);
    }

    return () => {
      if (timerId.current) {
        clearTimer();
      }
    };
  }, [sideContentsVisible]);

  // TabContainerV2 자동 닫기 (tabIndex 1 → 2 변경 감지)
  useEffect(() => {
    // tabContainerVersion === 2일 때만 실행
    if (tabContainerVersion !== 2) {
      prevTabIndexV2.current = tabIndexV2;
      return;
    }

    // modal === false일 때만 실행
    if (panelInfo?.modal) {
      prevTabIndexV2.current = tabIndexV2;
      return;
    }

    // tabIndexV2가 1에서 2로 정확하게 변경되는 시점만 감지
    const isTransitionedTo2 = prevTabIndexV2.current === 1 && tabIndexV2 === 2;
    prevTabIndexV2.current = tabIndexV2;

    if (!isTransitionedTo2) {
      if (timerIdV2.current) {
        dlog('[TabContainerV2] 타이머 클리어 - tabIndex가 2가 아님', tabIndexV2);
        clearTimerV2();
      }
      return;
    }

    dlog('[TabContainerV2] tabIndex 1 → 2 감지, 타이머 시작');

    if (!belowContentsVisible || videoVerticalVisible) {
      dlog(
        '[TabContainerV2] early return - belowContentsVisible 또는 videoVerticalVisible 조건 불만족'
      );
      return;
    }

    // tabIndex 1 → 2로 변경된 정확한 시점에 30초 타이머 시작
    dlog('[TabContainerV2] 30초 타이머 시작');
    resetTimerV2(REGULAR_TIMEOUT);

    return () => {
      // cleanup: tabIndex가 2가 아니거나 오버레이가 사라질 때만 타이머 클리어
      if (!belowContentsVisible || videoVerticalVisible || tabIndexV2 !== 2) {
        if (timerIdV2.current) {
          dlog('[TabContainerV2] cleanup - 타이머 클리어');
          clearTimerV2();
        }
      }
    };
  }, [
    tabContainerVersion,
    tabIndexV2,
    belowContentsVisible,
    videoVerticalVisible,
    panelInfo?.modal,
  ]);

  // TabIndex 1 자동 다음 단계로 이동
  useEffect(() => {
    // tabIndex === 1일 때만 실행
    if (tabIndexV2 !== 1 || !belowContentsVisible || videoVerticalVisible) {
      if (timerIdTabAutoAdvance.current) {
        clearTimerTabAutoAdvance();
      }
      return;
    }

    // 10초 후 tabIndex를 2로 변경
    resetTimerTabAutoAdvance(10000);

    return () => {
      if (timerIdTabAutoAdvance.current) {
        clearTimerTabAutoAdvance();
      }
    };
  }, [
    tabIndexV2,
    belowContentsVisible,
    videoVerticalVisible,
    resetTimerTabAutoAdvance,
    clearTimerTabAutoAdvance,
  ]);

  useLayoutEffect(() => {
    const videoContainer = document.querySelector(`.${css.videoContainer}`);

    if (panelInfo.thumbnail && !videoVerticalVisible) {
      videoContainer.style.background = `url(${panelInfo.thumbnail}) center/contain no-repeat`;
      videoContainer.style.backgroundColor = 'black';
    }

    if (broadcast.type === 'videoError' && videoThumbnailUrl) {
      videoContainer.style.background = `url(${videoThumbnailUrl}) center/contain no-repeat`;
      videoContainer.style.backgroundColor = 'black';
    }
  }, [panelInfo.thumbnail, broadcast]);

  const isPlayer = useMemo(() => {
    if (!panelInfo?.modal) {
      return 'full player';
    }

    switch (panels[0].name) {
      case 'categorypanel':
        return 'category';
      case 'mypagepanel':
        return 'my page';
      case 'searchpanel':
        return 'search';
      case 'hotpickpanel':
        return 'hot picks';
      case 'featuredbrandspanel':
        return 'featured brands';
      case 'trendingnowpanel':
        return 'trending now';
      case 'playerpanel':
        return 'home';
    }
  }, [panelInfo.modal, panels]);

  const createLogParams = useCallback(
    (visible) => {
      if (videoLoaded && isShowType) {
        if (showDetailInfo?.[0]) {
          return {
            visible,
            showType: isShowType,
            player: isPlayer,
            category: showDetailInfo[0].showCatNm,
            showId: showDetailInfo[0].showId,
            showTitle: showDetailInfo[0].showNm,
            partner: showDetailInfo[0].patncNm,
            contextName: Config.LOG_CONTEXT_NAME.SHOW,
            messageId: Config.LOG_MESSAGE_ID.SHOWVIEW,
          };
        } else if (playListInfo?.[selectedIndex]) {
          const currentItem = playListInfo[selectedIndex];
          return {
            visible,
            showType: isShowType,
            player: isPlayer,
            category: currentItem.catNm,
            showId: currentItem.showId,
            contentTitle: currentItem.showNm,
            partner: currentItem.patncNm,
            contextName: Config.LOG_CONTEXT_NAME.SHOW,
            messageId: Config.LOG_MESSAGE_ID.SHOWVIEW,
          };
        }
      }
      return null;
    },
    [isShowType, videoLoaded, showDetailInfo?.[0]?.showId, playListInfo?.[selectedIndex]?.showId]
  );

  // isVODPaused 상태 변경 시에만 로그를 보냄
  useEffect(() => {
    if (showDetailInfo?.[0]) {
      const params = createLogParams(!isVODPaused);
      if (params) {
        dispatch(sendLogTotalRecommend(params));
      }
    } else if (playListInfo?.[selectedIndex]) {
      const params = createLogParams(true);
      if (params) {
        dispatch(sendLogTotalRecommend(params));
      }
    }
  }, [isVODPaused, createLogParams, showDetailInfo]);

  // 컴포넌트 언마운트 시에만 로그를 보냄
  useEffect(() => {
    return () => {
      const params = createLogParams(false);
      if (params) {
        dispatch(sendLogTotalRecommend(params));
      }
    };
  }, [createLogParams, dispatch, showDetailInfo]);

  const containerClassName = classNames(
    css.videoContainer,
    panelInfo.modal && css.modal,
    panelInfo.shouldShrinkTo1px && css.shrinkTo1px,
    // PlayerPanel이 최상단 아니고, 최상단이 DetailPanel(from Player)이면 비디오 보이도록
    !isOnTop && isTopPanelDetailFromPlayer && css['background-visible'],
    // PlayerPanel이 최상단 아니고, 위 조건 아니면 1px로 숨김
    !isOnTop && !isTopPanelDetailFromPlayer && css.background,
    !captionEnable && css.hideSubtitle
  );

  return (
    <TPanel
      isTabActivated={false}
      {...props}
      className={containerClassName}
      handleCancel={onClickBack}
      spotlightId={spotlightId}
    >
      <Container
        spotlightRestrict="self-only"
        spotlightId="spotlightId-video-contaienr"
        onKeyDown={onKeyDown}
        // onClick={() => {
        //   if (!panelInfo?.modal && isOnTop && panelInfo?.shptmBanrTpNm !== 'MEDIA') {
        //     setBelowContentsVisible((prev) => !prev);
        //   }
        // }}
      >
        <div
          ref={focusReturnRef}
          tabIndex={-1}
          aria-hidden="true"
          style={{ width: 1, height: 1, opacity: 0, position: 'absolute' }}
        />
        {isReadyToPlay && (
          <VideoPlayer
            key={currentPlayingUrl}
            setApiProvider={getPlayer}
            disabled={panelInfo.modal}
            onEnded={onEnded}
            noAutoPlay={cannotPlay}
            autoCloseTimeout={10000}
            onBackButton={onClickBack}
            spotlightDisabled={panelInfo.modal}
            isYoutube={isYoutube}
            src={currentPlayingUrl}
            style={panelInfo.modal ? modalStyle : {}}
            modalScale={panelInfo.modal ? modalScale : 1}
            modalClassName={panelInfo.modal && panelInfo.modalClassName}
            handleIndicatorDownClick={handleIndicatorDownClick}
            handleIndicatorUpClick={handleIndicatorUpClick}
            onError={mediainfoHandler}
            onTimeUpdate={mediainfoHandler}
            onLoadedData={mediainfoHandler}
            onLoadedMetadata={mediainfoHandler}
            onDurationChange={mediainfoHandler}
            reactPlayerConfig={reactPlayerSubtitleConfig}
            thumbnailUrl={videoLoaded ? '' : videoThumbnailUrl}
            videoComponent={
              (typeof window === 'object' && !window.PalmSystem) || isYoutube ? TReactPlayer : Media
            }
            liveTotalTime={liveTotalTime}
            currentLiveTimeSeconds={currentLiveTimeSeconds}
            countryCode={countryCode}
            // VideoOverlay props
            type={isShowType}
            panelInfo={panelInfo}
            captionEnable={captionEnable}
            orderPhnNo={orderPhoneNumber}
            disclaimer={shopNowInfo && shopNowInfo[smallestOffsetHourIndex]?.disclaimer}
            themeProductInfos={themeProductInfos ? themeProductInfos : hotelInfos}
            detailThemeProductImageLength={productImageLength}
            playListInfo={playListInfo && playListInfo}
            selectedIndex={selectedIndex}
            qrCurrentItem={qrCurrentItem}
            setIsSubtitleActive={setIsSubtitleActive}
            setSideContentsVisible={setSideContentsVisible}
            sideContentsVisible={sideContentsVisible}
            setBelowContentsVisible={setBelowContentsVisible}
            belowContentsVisible={belowContentsVisible}
            videoVerticalVisible={videoVerticalVisible}
            setCurrentTime={setCurrentTime}
            setIsVODPaused={setIsVODPaused}
            broadcast={broadcast}
            tabContainerVersion={tabContainerVersion}
            tabIndexV2={tabIndexV2}
            dispatch={dispatch}
            videoPlayState={videoPlayState}
          >
            {typeof window === 'object' && window.PalmSystem && (
              <source src={currentPlayingUrl} type={videoType} />
            )}
            {isSubtitleActive &&
              !panelInfo.modal &&
              captionEnable &&
              typeof window === 'object' &&
              window.PalmSystem &&
              currentSubtitleBlob && <track kind="subtitles" src={currentSubtitleBlob} default />}
          </VideoPlayer>
        )}

        {chatVisible && (
          <PlayerOverlayChat
            currentTime={currentTime}
            videoVerticalVisible={videoVerticalVisible}
            imageQRCodeUrl={playListInfo[selectedIndex]?.chatQrImgUrl}
            QRCodeUrl={playListInfo[selectedIndex]?.chatUrl}
          />
        )}

        {tabContainerVersion === 1 &&
          currentSideButtonStatus &&
          !videoVerticalVisible &&
          playListInfo && (
            <PlayerTabButton
              setSideContentsVisible={setSideContentsVisible}
              sideContentsVisible={sideContentsVisible}
              videoType={isShowType}
            />
          )}

        {tabContainerVersion === 1 && showSideContents && (
          <TabContainer
            spotlightId={TAB_CONTAINER_SPOTLIGHT_ID}
            panelInfo={panelInfo}
            shopNowInfo={shopNowInfo}
            playListInfo={playListInfo}
            selectedIndex={selectedIndex}
            setSelectedIndex={setSelectedIndex}
            liveChannelInfos={liveChannelInfos || liveShowInfos}
            videoVerticalVisible={videoVerticalVisible}
            handleItemFocus={handleItemFocus}
            prevChannelIndex={prevChannelIndex}
            currentTime={currentTime}
          />
        )}

        {/* {shouldShowBelowTab && (
          <>
            {belowTabMode === 'liveShow' && (
              <LiveShowContainer
                panelInfo={panelInfo}
                liveInfos={playListInfo}
                currentTime={currentTime}
                setSelectedIndex={setSelectedIndex}
                videoVerticalVisible={videoVerticalVisible}
                currentVideoShowId={playListInfo && playListInfo[selectedIndex]?.showId}
                handleItemFocus={handleItemFocus}
                onLiveChannelButtonClick={() => setBelowTabMode('shopNowButton')}
                tabTitle={[
                  $L('SHOP NOW'),
                  panelInfo?.shptmBanrTpNm === 'LIVE' ? $L('LIVE CHANNEL') : $L('FEATURED SHOWS'),
                ]}
                selectedIndex={selectedIndex}
                tabIndex={1}
              />
            )}
            {belowTabMode === 'shopNowButton' && (
              <ShopNowButton onClick={() => setBelowTabMode('shopNow')} />
            )}
            {belowTabMode === 'shopNow' && (
              <ShopNowContainer
                panelInfo={panelInfo}
                liveInfos={playListInfo}
                currentTime={currentTime}
                setSelectedIndex={setSelectedIndex}
                videoVerticalVisible={videoVerticalVisible}
                currentVideoShowId={playListInfo && playListInfo[selectedIndex]?.showId}
                handleItemFocus={handleItemFocus}
              />
            )}
          </>
        )} */}

        {tabContainerVersion === 2 && showBelowContents && (
          <TabContainerV2
            panelInfo={panelInfo}
            playListInfo={playListInfo}
            shopNowInfo={shopNowInfo}
            selectedIndex={selectedIndex}
            setSelectedIndex={setSelectedIndex}
            liveChannelInfos={liveChannelInfos || liveShowInfos}
            videoVerticalVisible={videoVerticalVisible}
            handleItemFocus={handleItemFocus}
            prevChannelIndex={prevChannelIndex}
            currentTime={currentTime}
            spotlightId={TAB_CONTAINER_V2_SPOTLIGHT_ID}
            tabIndex={tabIndexV2}
            onShopNowButtonClick={() => setTabIndexV2(0)}
            onLiveChannelButtonClick={() => setTabIndexV2(2)}
            onLiveNext={() => setTabIndexV2(1)}
            onTabClose={(newTabIndex) => setTabIndexV2(newTabIndex)}
            tabVisible={belowContentsVisible}
          />
        )}
      </Container>

      {activePopup === ACTIVE_POPUP.alertPopup && (
        <TPopUp
          kind="textPopup"
          hasText
          hasButton
          text={$L(
            "To view the subtitles, please change the setting to 'On' in the system settings menu."
          )}
          button2Text={$L('OK')}
          open={popupVisible}
          onClose={handlePopupClose}
        ></TPopUp>
      )}
    </TPanel>
  );
};

const propsAreEqual = (prev, next) => {
  const keys = Object.keys(prev);
  const nextKeys = Object.keys(next);
  // if (!next.isOnTop) {
  //   //ignore event on background
  //   return true;
  // }
  if (keys.length !== nextKeys.length) {
    return false;
  }
  for (let i = 0; i < keys.length; i++) {
    if (prev[keys[i]] !== next[keys[i]]) {
      if (JSON.stringify(prev[keys[i]]) === JSON.stringify(next[keys[i]])) {
        continue;
      }
      return false;
    }
  }
  return true;
};
export default React.memo(PlayerPanel, propsAreEqual);
