/**
 * @fileoverview js/beta-app.jsx
 * React DOM & Bento Grid Architecture for Map Page
 * Ultra-Fast Hardware GPU Marker Rendering Powered by PixiJS + L.PixiOverlay
 * Pure Clean Architecture, Full SOC (Separation of Concerns) & OOP Service Integration
 */

import { renderMarkers, clearOverlay, destroyOverlay } from '/js/beta-pixi.js';
import { REGIONS, OFFICIAL_CATEGORY_GROUPS, ALL_OFFICIAL_CATEGORY_IDS } from '/js/beta/constants.js';
import { storageService } from '/js/beta/services/StorageService.js';
import { urlSyncService } from '/js/beta/services/UrlSyncService.js';
import { markerDataService } from '/js/beta/services/MarkerDataService.js';
import { markerPopupService } from '/js/beta/services/MarkerPopupService.js';
import { BentoHeader } from '/js/beta/components/BentoHeader.js';
import { BentoSidebar } from '/js/beta/components/BentoSidebar.js';
import { BentoOptionsModal } from '/js/beta/components/BentoOptionsModal.js';
import {
  BentoModalsContainer,
  BentoMobileDock,
  LiveChatModal,
  ImageLightboxModal
} from '/js/beta/components/BentoModals.js';

import { BentoAuthModal } from '/js/beta/components/BentoAuthModal.js';
import { BentoToastContainer } from '/js/beta/components/common/BentoUI.js';
import { authService } from '/js/beta/services/AuthService.js';
import { loadCategoryNames } from '/js/data/categoryNames.js';

const { useState, useEffect, useRef, useCallback, useMemo } = React;

// --------------------------------------------------------------------------
// MAIN BETA REACT APP (Powered by Clean Architecture & OOP Services)
// --------------------------------------------------------------------------
export function BetaApp() {
  // 1. URL 기반 초기 지역 결정 (UrlSyncService OOP)
  const [currentRegion, setCurrentRegion] = useState(() => urlSyncService.getInitialRegion(REGIONS));

  // 2. 카테고리 필터링 상태 (새로고침 유지 & StorageService 지속성 연동)
  const [activeCategoryIds, setActiveCategoryIds] = useState(() => {
    const initRegion = urlSyncService.getInitialRegion(REGIONS);
    const saved = storageService.getActiveCategoryIds(initRegion.id);
    if (saved !== null) {
      return new Set(saved);
    }
    const allIds = new Set();
    Object.values(OFFICIAL_CATEGORY_GROUPS).forEach(g => g.ids.forEach(id => allIds.add(id)));
    return allIds;
  });

  // 2-2. 세부 구역/지역 필터링 상태 (새로고침 유지 & StorageService 연동)
  const [activeRegionNames, setActiveRegionNames] = useState(() => {
    const initRegion = urlSyncService.getInitialRegion(REGIONS);
    const saved = storageService.getActiveRegionNames(initRegion.id);
    return saved !== null ? new Set(saved) : new Set();
  });

  const [activeGroup, setActiveGroup] = useState('all');
  const [searchQuery, setSearchQuery] = useState('');
  const [sidebarOpen, setSidebarOpen] = useState(true);
  const [sidebarTab, setSidebarTab] = useState('category');
  const [mobileTab, setMobileTab] = useState('map');
  const [isChatOpen, setIsChatOpen] = useState(false);
  const [isAuthOpen, setIsAuthOpen] = useState(false);
  const [currentUser, setCurrentUser] = useState(() => authService.getUser());
  const [hideCompleted, setHideCompleted] = useState(false);
  const [activeLightboxImg, setActiveLightboxImg] = useState(null);
  const [activeVideoUrl, setActiveVideoUrl] = useState(null);
  const [wikiModalState, setWikiModalState] = useState(null); // { type: 'edit'|'history'|'move'|'delete', markerItem, ... }
  const [isOptionsOpen, setIsOptionsOpen] = useState(false);

  // 3. 클러스터링 옵션 (StorageService OOP)
  const [enableClustering, setEnableClustering] = useState(() => storageService.getClustering());

  const handleToggleClustering = useCallback(() => {
    setEnableClustering(prev => {
      const next = !prev;
      storageService.setClustering(next);
      return next;
    });
  }, []);

  // 4. 마커 데이터 및 완료 상태 (StorageService & MarkerDataService OOP)
  const [markersData, setMarkersData] = useState([]);
  const [isLoadingData, setIsLoadingData] = useState(false);
  const [completedIds, setCompletedIds] = useState(() => storageService.getCompletedMarkers());
  const [favoriteItems, setFavoriteItems] = useState(() => storageService.getFavoriteItems());
  const favoriteIds = useMemo(() => favoriteItems.map(i => String(i.id)), [favoriteItems]);

  // 4-1. map.html의 IndexedDB(WwmVaultDB)와 비동기 완료 데이터, 즐겨찾기 및 동적 카테고리/인증 로딩
  useEffect(() => {
    loadCategoryNames().catch(() => {});
    authService.checkSession().catch(() => {});
    const unsubAuth = authService.subscribe(setCurrentUser);
    storageService.loadCompletedMarkersAsync().then(ids => {
      if (ids && ids.length > 0) {
        setCompletedIds(ids);
      }
    });
    storageService.loadFavoriteItemsAsync().then(async (favs) => {
      if (favs && favs.length > 0) {
        setFavoriteItems(favs);
        let hasUpdates = false;
        const enriched = await Promise.all(favs.map(async (item) => {
          if (!item.name && (!item.title || item.title.startsWith('즐겨찾기 마커 (#') || item.title.startsWith('마커 (#'))) {
            try {
              const detail = await markerDataService.fetchMarkerDetail(item.id);
              if (detail) {
                hasUpdates = true;
                const mRegionMapId = markerPopupService.resolveRegionMapId(detail);
                return {
                  ...item,
                  title: detail.title || detail.name || item.title,
                  name: detail.name || detail.title || item.name,
                  categoryId: String(detail.category_id || detail.category || item.categoryId || '17310010006'),
                  region: detail.region || (mRegionMapId === 'qinghe' ? '청하' : mRegionMapId === 'kaifeng' ? '개봉' : mRegionMapId === 'zhentian' ? '진천' : ''),
                  mapId: mRegionMapId || item.mapId || 'qinghe',
                  lat: parseFloat(detail.latitude || detail.lat || item.lat || 0),
                  lng: parseFloat(detail.longitude || detail.lng || item.lng || 0),
                  description: detail.description || item.description || '',
                  images: detail.images || item.images || []
                };
              }
            } catch (e) {}
          }
          return item;
        }));

        if (hasUpdates) {
          setFavoriteItems(enriched);
          storageService.setFavoriteItems(enriched);
        }
      }
    });
    return unsubAuth;
  }, []);

  // 4-2. markersData 로드 시 favoriteItems의 이름/설명이 누락된 항목 실시간 동기화
  useEffect(() => {
    if (!markersData || markersData.length === 0 || !favoriteItems || favoriteItems.length === 0) return;

    let hasChange = false;
    const updatedFavs = favoriteItems.map(fav => {
      const found = markersData.find(m => String(m.id) === String(fav.id));
      if (found) {
        const needsUpdate = !fav.name || fav.title?.startsWith('즐겨찾기 마커 (#') || fav.title?.startsWith('마커 (#') || !fav.description;
        if (needsUpdate) {
          hasChange = true;
          return {
            ...fav,
            title: found.title || found.name || fav.title,
            name: found.name || found.title || fav.name,
            categoryId: String(found.categoryId || fav.categoryId || '17310010006'),
            region: found.region || currentRegion.name || fav.region,
            mapId: currentRegion.id || fav.mapId || 'qinghe',
            lat: found.lat || fav.lat,
            lng: found.lng || fav.lng,
            description: found.description || fav.description,
            images: found.images || fav.images || []
          };
        }
      }
      return fav;
    });

    if (hasChange) {
      setFavoriteItems(updatedFavs);
      storageService.setFavoriteItems(updatedFavs);
    }
  }, [markersData, currentRegion]);

  useEffect(() => {
    window.__BETA_MARKERS_DATA__ = markersData;
  }, [markersData]);

  const mapRef = useRef(null);
  const leafletMapInstance = useRef(null);
  const tileLayerInstance = useRef(null);

  const currentRegionRef = useRef(currentRegion);
  useEffect(() => {
    currentRegionRef.current = currentRegion;
  }, [currentRegion]);

  const focusRingMarkerRef = useRef(null);
  const pendingFocusMarkerRef = useRef(null);

  // 5. 마커 완료 토글 핸들러
  const toggleMarkerCompleted = useCallback((markerId) => {
    setCompletedIds(prev => {
      const idStr = String(markerId);
      const isCompleted = prev.some(id => String(id) === idStr);
      const next = isCompleted
        ? prev.filter(id => String(id) !== idStr)
        : [...prev, markerId];
      storageService.setCompletedMarkers(next);
      return next;
    });
  }, []);

  // 5-2. 전역 즐겨찾기 토글 핸들러 (지역 무관 영구 보존)
  const toggleMarkerFavorite = useCallback((target) => {
    if (!target) return;
    const markerId = typeof target === 'object' ? (target.id || target.markerId) : target;
    const idStr = String(markerId);
    const curRegion = currentRegionRef.current || currentRegion;

    setFavoriteItems(prev => {
      const exists = prev.some(i => String(i.id) === idStr);
      let next;

      if (exists) {
        next = prev.filter(i => String(i.id) !== idStr);
      } else {
        // 현재 마커 데이터 또는 전달받은 객체에서 상세 메타데이터 보존
        const found = Array.isArray(markersData) ? markersData.find(m => String(m.id) === idStr) : null;
        const src = typeof target === 'object' ? target : (found || {});

        const itemToSave = {
          id: idStr,
          title: src.title || src.name || found?.title || found?.name || `마커 (#${idStr.slice(-4)})`,
          name: src.name || src.title || found?.name || '',
          categoryId: String(src.categoryId || src.category || found?.categoryId || '17310010006'),
          region: src.region || found?.region || curRegion.name || '',
          mapId: src.mapId || found?.mapId || curRegion.id || 'qinghe',
          lat: parseFloat(src.lat || src.latitude || found?.lat || 0),
          lng: parseFloat(src.lng || src.longitude || found?.lng || 0),
          description: src.description || found?.description || '',
          images: Array.isArray(src.images) ? src.images : (src.image ? [src.image] : (found?.images || []))
        };

        next = [...prev, itemToSave];
      }

      storageService.setFavoriteItems(next);
      return next;
    });
  }, [currentRegion, markersData]);

  // 6. 제안 제출 완료 시 낙관적 업데이트 & 팝업 실시간 갱신
  const handleProposalSubmitted = useCallback((updated) => {
    if (!updated || !updated.markerId) return;
    setMarkersData(prev => {
      const nextList = markerDataService.applyOptimisticUpdate(prev, updated);
      const updatedMarker = nextList.find(m => String(m.id) === String(updated.markerId));

      if (updatedMarker && leafletMapInstance.current) {
        const curRegion = currentRegionRef.current || currentRegion;
        setTimeout(() => {
          markerPopupService.openPopup(leafletMapInstance.current, updatedMarker, {
            currentRegion: curRegion,
            completedIds,
            favoriteIds,
            toggleMarkerCompleted,
            toggleMarkerFavorite,
            setWikiModalState,
            setActiveLightboxImg
          });
        }, 120);
      }
      return nextList;
    });
  }, [currentRegion, completedIds, favoriteIds, toggleMarkerCompleted, toggleMarkerFavorite]);

  // 7. 카테고리 필터 토글 핸들러 (StorageService 실시간 지속성 연동)
  const handleToggleCategory = useCallback((catId) => {
    setActiveCategoryIds(prev => {
      const next = new Set(prev);
      const strId = String(catId);
      if (next.has(strId)) next.delete(strId);
      else next.add(strId);
      const curRegion = currentRegionRef.current || currentRegion;
      storageService.setActiveCategoryIds(curRegion.id, Array.from(next));
      return next;
    });
  }, [currentRegion]);

  const handleToggleGroup = useCallback((categoryIds) => {
    setActiveCategoryIds(prev => {
      const next = new Set(prev);
      const strIds = categoryIds.map(String);
      const allActive = strIds.every(id => next.has(id));
      if (allActive) {
        strIds.forEach(id => next.delete(id));
      } else {
        strIds.forEach(id => next.add(id));
      }
      const curRegion = currentRegionRef.current || currentRegion;
      storageService.setActiveCategoryIds(curRegion.id, Array.from(next));
      return next;
    });
  }, [currentRegion]);

  const handleToggleAllCategories = useCallback(() => {
    setActiveCategoryIds(prev => {
      let next;
      if (prev.size > 0) {
        next = new Set();
      } else {
        next = new Set();
        Object.values(OFFICIAL_CATEGORY_GROUPS).forEach(g => g.ids.forEach(id => next.add(String(id))));
        // 지역 커스텀 카테고리 포함
        markersData.forEach(m => {
          if (m.categoryId) next.add(String(m.categoryId));
        });
      }
      const curRegion = currentRegionRef.current || currentRegion;
      storageService.setActiveCategoryIds(curRegion.id, Array.from(next));
      return next;
    });
  }, [currentRegion, markersData]);

  // 7-2. 세부 구역 토글 핸들러 (StorageService 실시간 지속성 연동)
  const handleToggleRegion = useCallback((regionName) => {
    setActiveRegionNames(prev => {
      const next = new Set(prev);
      if (next.has(regionName)) next.delete(regionName);
      else next.add(regionName);
      const curRegion = currentRegionRef.current || currentRegion;
      storageService.setActiveRegionNames(curRegion.id, Array.from(next));
      return next;
    });
  }, [currentRegion]);

  const handleToggleAllRegions = useCallback(() => {
    setActiveRegionNames(prev => {
      let next;
      if (prev.size > 0) {
        next = new Set();
      } else {
        const unique = new Set(markersData.map(m => m.region || '기타 구역'));
        next = unique;
      }
      const curRegion = currentRegionRef.current || currentRegion;
      storageService.setActiveRegionNames(curRegion.id, Array.from(next));
      return next;
    });
  }, [currentRegion, markersData]);

  // 8. 일괄 완료 / 취소 / 초기화
  const handleCompleteAllMarkers = () => {
    const targetMarkers = markerDataService.filterMarkers(markersData, { activeCategoryIds, activeRegionNames, searchQuery, activeGroup });
    if (targetMarkers.length === 0) return;

    const targetIdStrs = targetMarkers.map(m => String(m.id));
    setCompletedIds(prev => {
      const completedSet = new Set(prev.map(id => String(id)));
      const isAllTargetCompleted = targetIdStrs.every(id => completedSet.has(id));

      let next;
      if (isAllTargetCompleted) {
        next = prev.filter(id => !targetIdStrs.includes(String(id)));
      } else {
        const combined = new Set([...prev.map(id => String(id)), ...targetIdStrs]);
        next = Array.from(combined);
      }
      storageService.setCompletedMarkers(next);
      return next;
    });
  };

  const handleResetAllMarkers = () => {
    if (confirm('완료 수집한 항목을 초기화하시겠습니까?')) {
      setCompletedIds([]);
      storageService.clearCompletedMarkers();
    }
  };

  const isAllCompleted = useMemo(() => {
    const targetMarkers = markerDataService.filterMarkers(markersData, { activeCategoryIds, activeRegionNames, searchQuery, activeGroup });
    if (targetMarkers.length === 0) return false;
    const completedSet = new Set(completedIds.map(id => String(id)));
    return targetMarkers.every(m => completedSet.has(String(m.id)));
  }, [markersData, activeCategoryIds, activeRegionNames, searchQuery, activeGroup, completedIds]);

  // 9. 카테고리별/지역별 마커 카운트 및 필터링 (MarkerDataService OOP)
  const categoryCounts = useMemo(() => {
    return markerDataService.computeCategoryCounts(markersData);
  }, [markersData]);

  const regionCounts = useMemo(() => {
    return markerDataService.computeRegionCounts(markersData);
  }, [markersData]);

  const regionStats = useMemo(() => {
    return markerDataService.computeRegionStats(markersData, completedIds);
  }, [markersData, completedIds]);

  const handleFocusRegion = useCallback((center) => {
    const map = leafletMapInstance.current;
    if (!map || !center) return;
    const { lat, lng, zoom } = center;
    if (lat && lng) {
      map.flyTo([lat, lng], zoom || 12, { duration: 1.0 });
    }
  }, []);

  const filteredMarkers = useMemo(() => {
    return markerDataService.filterMarkers(markersData, {
      activeCategoryIds,
      activeRegionNames,
      searchQuery,
      hideCompleted,
      completedIds,
      activeGroup
    });
  }, [markersData, activeCategoryIds, activeRegionNames, searchQuery, hideCompleted, completedIds, activeGroup]);

  // 10. 타일 레이어 생성 헬퍼
  const createTileLayer = useCallback((region) => {
    if (region.type === 'image' && region.imageUrl && region.bounds) {
      return L.imageOverlay(region.imageUrl, region.bounds);
    }
    return L.tileLayer(region.tileUrl, {
      minZoom: region.minZoom || 9,
      maxZoom: region.maxZoom || 14,
      maxNativeZoom: region.maxNativeZoom || 13,
      noWrap: true,
      tileSize: 256,
      crossOrigin: "anonymous",
      referrerPolicy: "no-referrer",
      keepBuffer: 8,
      updateWhenIdle: false
    });
  }, []);

  // 11. 마커 데이터 비동기 로딩 (MarkerDataService OOP)
  useEffect(() => {
    let isSubscribed = true;
    const loadData = async () => {
      setIsLoadingData(true);
      const startPerfTime = performance.now();
      try {
        const list = await markerDataService.loadRegionMarkers(currentRegion);
        if (!isSubscribed) return;
        setMarkersData(list);

        // 저장된 필터가 없는 경우에만 초기 카테고리/구역 설정 저장
        const saved = storageService.getActiveCategoryIds(currentRegion.id);
        if (saved === null) {
          const allIds = new Set();
          Object.values(OFFICIAL_CATEGORY_GROUPS).forEach(g => g.ids.forEach(id => allIds.add(String(id))));
          list.forEach(m => {
            if (m.categoryId) allIds.add(String(m.categoryId));
          });
          setActiveCategoryIds(allIds);
          storageService.setActiveCategoryIds(currentRegion.id, Array.from(allIds));
        }

        const allRegs = new Set(list.map(m => m.region || '기타 구역'));
        const savedRegs = storageService.getActiveRegionNames(currentRegion.id);
        if (!savedRegs || savedRegs.length === 0) {
          setActiveRegionNames(allRegs);
          storageService.setActiveRegionNames(currentRegion.id, Array.from(allRegs));
        } else {
          // 저장된 구역명 중 현재 유효한 한국어 구역명 필터링 및 구버전 데이터 마이그레이션
          const validSaved = savedRegs.map(r => markerDataService.t(r) || r).filter(r => allRegs.has(r));
          if (validSaved.length === 0) {
            setActiveRegionNames(allRegs);
            storageService.setActiveRegionNames(currentRegion.id, Array.from(allRegs));
          } else {
            setActiveRegionNames(new Set(validSaved));
          }
        }

        const elapsed = (performance.now() - startPerfTime).toFixed(1);
        console.log(`%c[BetaPerf] ⚡ ${currentRegion.name} 마커 ${list.length}개 로딩 완료: ${elapsed}ms`, 'color: #daac71; font-weight: bold; font-size: 13px;');
      } catch (err) {
        console.warn('[BetaApp] loadRegionMarkers failed:', err);
      } finally {
        if (isSubscribed) setIsLoadingData(false);
      }
    };

    loadData();
    return () => { isSubscribed = false; };
  }, [currentRegion]);

  // 12. PixiJS 마커 렌더링 연동 (MarkerPopupService OOP)
  useEffect(() => {
    const map = leafletMapInstance.current;
    if (!map) return;

    const popupContext = {
      currentRegion,
      completedIds,
      favoriteIds,
      toggleMarkerCompleted,
      toggleMarkerFavorite,
      setWikiModalState,
      setActiveLightboxImg
    };

    const callbacks = {
      onMarkerClick: (markerData) => {
        markerPopupService.openPopup(map, markerData, popupContext);
      },
      onToggleCompleted: (markerId) => {
        toggleMarkerCompleted(markerId);
      }
    };

    renderMarkers(
      map,
      filteredMarkers,
      completedIds,
      activeCategoryIds,
      hideCompleted,
      callbacks,
      enableClustering
    );
  }, [filteredMarkers, completedIds, favoriteIds, toggleMarkerCompleted, toggleMarkerFavorite, activeCategoryIds, hideCompleted, enableClustering, currentRegion]);

  // 13. Leaflet Map 인스턴스 초기화
  useEffect(() => {
    if (!mapRef.current || leafletMapInstance.current) return;

    const crs = currentRegion.crs === 'Simple' ? L.CRS.Simple : L.CRS.EPSG3857;

    const map = L.map(mapRef.current, {
      crs,
      center: currentRegion.center,
      zoom: currentRegion.zoom,
      minZoom: currentRegion.minZoom || -3,
      maxZoom: currentRegion.maxZoom || 14,
      zoomControl: false,
      attributionControl: false,
      preferCanvas: true,
      fadeAnimation: false,
      zoomAnimation: false,
      markerZoomAnimation: false,
      inertia: false
    });

    const initialTileLayer = createTileLayer(currentRegion);
    initialTileLayer.addTo(map);
    tileLayerInstance.current = initialTileLayer;

    leafletMapInstance.current = map;

    setTimeout(() => {
      if (leafletMapInstance.current) {
        leafletMapInstance.current.invalidateSize();
      }
    }, 100);

    return () => {
      if (leafletMapInstance.current) {
        destroyOverlay(leafletMapInstance.current);
        leafletMapInstance.current.remove();
        leafletMapInstance.current = null;
      }
    };
  }, []);

  // 14. 지역 전환 핸들러 (UrlSyncService OOP)
  const handleSelectRegion = useCallback((region) => {
    if (region.id === currentRegion.id) return;

    setMarkersData([]);
    const map = leafletMapInstance.current;
    if (map) {
      clearOverlay(map);
    }

    // 대상 지역의 저장된 카테고리 필터 복원
    const savedCats = storageService.getActiveCategoryIds(region.id);
    if (savedCats !== null) {
      setActiveCategoryIds(new Set(savedCats));
    } else {
      const allIds = new Set();
      Object.values(OFFICIAL_CATEGORY_GROUPS).forEach(g => g.ids.forEach(id => allIds.add(String(id))));
      setActiveCategoryIds(allIds);
    }

    const savedRegs = storageService.getActiveRegionNames(region.id);
    if (savedRegs !== null) {
      setActiveRegionNames(new Set(savedRegs));
    } else {
      setActiveRegionNames(new Set());
    }

    setCurrentRegion(region);
    setIsLoadingData(true);

    if (map) {
      const prevCrs = currentRegion.crs === 'Simple' ? L.CRS.Simple : L.CRS.EPSG3857;
      const nextCrs = region.crs === 'Simple' ? L.CRS.Simple : L.CRS.EPSG3857;

      if (prevCrs !== nextCrs) {
        destroyOverlay(map);
        map.remove();

        const newMap = L.map('beta-map', {
          crs: nextCrs,
          center: region.center,
          zoom: region.zoom,
          minZoom: region.minZoom || -3,
          maxZoom: region.maxZoom || 14,
          zoomControl: false,
          attributionControl: false,
          preferCanvas: true,
          fadeAnimation: false,
          zoomAnimation: false,
          markerZoomAnimation: false,
          inertia: false
        });

        const newTileLayer = createTileLayer(region);
        newTileLayer.addTo(newMap);
        tileLayerInstance.current = newTileLayer;
        leafletMapInstance.current = newMap;
        return;
      }

      if (tileLayerInstance.current) {
        map.removeLayer(tileLayerInstance.current);
      }
      const newTileLayer = createTileLayer(region);
      newTileLayer.addTo(map);
      tileLayerInstance.current = newTileLayer;

      if (region.minZoom !== undefined) map.setMinZoom(region.minZoom);
      if (region.maxZoom !== undefined) map.setMaxZoom(region.maxZoom);
      map.setView(region.center, region.zoom);
    }

    urlSyncService.syncUrl(region.id, null);
  }, [currentRegion, createTileLayer]);

  // 15. 마커 포커스 실행 핸들러 (카메라 이동, 펄스 링, 팝업 오픈)
  const executeFocusOnMap = useCallback((targetMarker) => {
    if (!targetMarker || !targetMarker.lat || !targetMarker.lng) return;
    const map = leafletMapInstance.current;
    if (!map) return;

    if (targetMarker.categoryId && activeCategoryIds && !activeCategoryIds.has(targetMarker.categoryId)) {
      setActiveCategoryIds(prev => new Set([...prev, targetMarker.categoryId]));
    }
    if (hideCompleted) setHideCompleted(false);
    if (searchQuery) setSearchQuery('');

    setSidebarOpen(false);

    const curRegion = currentRegionRef.current || currentRegion;
    const targetZoom = Math.min(Math.max(map.getZoom(), 12), curRegion.maxZoom || 14);
    map.flyTo([targetMarker.lat, targetMarker.lng], targetZoom, {
      duration: 0.7,
      easeLinearity: 0.25
    });

    urlSyncService.syncUrl(curRegion.id, targetMarker.id);

    if (focusRingMarkerRef.current) {
      map.removeLayer(focusRingMarkerRef.current);
      focusRingMarkerRef.current = null;
    }

    const pulseMarker = markerPopupService.showFocusRing(map, targetMarker.lat, targetMarker.lng);
    focusRingMarkerRef.current = pulseMarker;

    setTimeout(() => {
      if (focusRingMarkerRef.current === pulseMarker) {
        map.removeLayer(pulseMarker);
        focusRingMarkerRef.current = null;
      }
    }, 3500);

    setTimeout(() => {
      markerPopupService.openPopup(map, targetMarker, {
        currentRegion: curRegion,
        completedIds,
        favoriteIds,
        toggleMarkerCompleted,
        toggleMarkerFavorite,
        setWikiModalState,
        setActiveLightboxImg
      });
    }, 500);
  }, [activeCategoryIds, hideCompleted, searchQuery, currentRegion, completedIds, favoriteIds, toggleMarkerCompleted, toggleMarkerFavorite]);

  // 대기 중인 포커스 마커 안전 실행
  useEffect(() => {
    if (!isLoadingData && pendingFocusMarkerRef.current) {
      const pending = pendingFocusMarkerRef.current;
      pendingFocusMarkerRef.current = null;
      setTimeout(() => {
        executeFocusOnMap(pending);
      }, 300);
    }
  }, [isLoadingData, executeFocusOnMap]);

  // 16. 위치 보기 진입점 (마커 탐색 및 지역 전환)
  const handleFocusMarker = useCallback(async (target) => {
    if (!target) return;

    const markerId = typeof target === 'object' ? (target.id || target.markerId || target.target_marker_id) : target;
    const idStr = String(markerId || '');
    const curRegion = currentRegionRef.current || currentRegion;

    // 1. 현재 로드된 markersData에서 먼저 원본 마커를 탐색
    let loadedMarker = markersData.find(m => String(m.id) === idStr);

    // 2. 현재 지역에 없으면 비동기 메타데이터 조회
    if (!loadedMarker && idStr) {
      const targetMapId = typeof target === 'object' ? (target.mapId || markerPopupService.resolveRegionMapId(target)) : null;
      loadedMarker = await markerDataService.fetchMarkerDetail(idStr, targetMapId);
    }

    if (!loadedMarker) {
      alert('해당 마커의 위치 정보를 찾을 수 없습니다.');
      return;
    }

    // 3. 타 지역 맵인 경우 지역 전환 처리
    const resolvedMapId = markerPopupService.resolveRegionMapId(loadedMarker) || loadedMarker.mapId;
    if (resolvedMapId && String(resolvedMapId) !== String(curRegion.id) && String(resolvedMapId) !== String(curRegion.mapId)) {
      const targetRegion = REGIONS.find(r => String(r.id) === String(resolvedMapId) || String(r.mapId) === String(resolvedMapId));
      if (targetRegion && targetRegion.id !== curRegion.id) {
        pendingFocusMarkerRef.current = loadedMarker;
        handleSelectRegion(targetRegion);
        return;
      }
    }

    if (!loadedMarker.lat || !loadedMarker.lng) {
      alert('해당 마커의 위치 정보를 찾을 수 없습니다.');
      return;
    }

    // 원본 마커 데이터를 100% 무손실로 전달
    executeFocusOnMap(loadedMarker);
  }, [markersData, currentRegion, handleSelectRegion, executeFocusOnMap]);

  useEffect(() => {
    window.__BETA_FOCUS_MARKER__ = handleFocusMarker;

    // 실시간 채팅 ↔ 지도 상호작용 리스너
    const handleShareToChat = () => {
      setIsChatOpen(true);
    };

    const handleFocusFromChat = (evt) => {
      const detail = evt.detail || {};
      const targetId = detail.markerId || detail.id;
      if (!targetId && (detail.lat === undefined || detail.lng === undefined)) return;
      handleFocusMarker(targetId ? { ...detail, id: targetId } : detail);
    };

    window.addEventListener('wwm:share-marker-to-chat', handleShareToChat);
    window.addEventListener('wwm:focus-marker-from-chat', handleFocusFromChat);

    return () => {
      window.removeEventListener('wwm:share-marker-to-chat', handleShareToChat);
      window.removeEventListener('wwm:focus-marker-from-chat', handleFocusFromChat);
    };
  }, [handleFocusMarker]);

  // 17. URL 초기 id 파라미터 자동 포커스 (UrlSyncService OOP)
  const initialUrlHandledRef = useRef(false);
  useEffect(() => {
    if (!isLoadingData && markersData.length > 0 && !initialUrlHandledRef.current) {
      initialUrlHandledRef.current = true;
      const sharedId = urlSyncService.getInitialMarkerId();
      if (sharedId) {
        setTimeout(() => {
          handleFocusMarker(sharedId);
        }, 400);
      }
    }
  }, [isLoadingData, markersData, handleFocusMarker]);

  const handleMobileTabSelect = (tab) => {
    setMobileTab(tab);
    if (tab === 'categories') {
      setSidebarOpen(true);
    } else {
      setSidebarOpen(false);
    }
  };

  return (
    <div className={`beta-app ${sidebarOpen ? 'sidebar-open' : 'sidebar-closed'}`}>
      {/* 1. Fullscreen Map Layer */}
      <div id="beta-map" ref={mapRef}></div>

      {/* 2. Premium Bento Map Loading Overlay */}
      {isLoadingData && (
        <div className="bento-map-loading-overlay">
          <div className="bento-map-loading-card">
            <div className="bento-map-loading-visual">
              <div className="bento-map-loading-pulse-ring"></div>
              <div className="bento-map-loading-spinner-ring"></div>
              <i className="fa-solid fa-map-location-dot bento-map-loading-icon"></i>
            </div>
            <div className="bento-map-loading-info">
              <span className="bento-map-loading-title">{currentRegion.name}</span>
              <span className="bento-map-loading-subtitle">지도 및 마커 로딩 중...</span>
            </div>
          </div>
        </div>
      )}

      {/* 3. Top Bento Navigation Header */}
      <BentoHeader
        currentRegion={currentRegion}
        onSelectRegion={handleSelectRegion}
        sidebarOpen={sidebarOpen}
        onToggleSidebar={() => setSidebarOpen(prev => !prev)}
        activeGroup={activeGroup}
        onSelectGroup={setActiveGroup}
        onOpenChat={() => setIsChatOpen(true)}
        onOpenOptions={() => setIsOptionsOpen(true)}
        isDataLoading={isLoadingData}
      />

      {/* 4. Bento Master Sidebar */}
      <BentoSidebar
        isOpen={sidebarOpen}
        onClose={() => setSidebarOpen(false)}
        activeTab={sidebarTab}
        onSelectTab={setSidebarTab}
        activeCategoryIds={activeCategoryIds}
        onToggleCategory={handleToggleCategory}
        onToggleGroup={handleToggleGroup}
        categoryCounts={categoryCounts}
        activeRegionNames={activeRegionNames}
        onToggleRegion={handleToggleRegion}
        regionCounts={regionCounts}
        regionStats={regionStats}
        onFocusRegion={handleFocusRegion}
        onToggleAllRegions={handleToggleAllRegions}
        searchQuery={searchQuery}
        setSearchQuery={setSearchQuery}
        totalMarkersCount={markersData.length}
        onToggleAllCategories={handleToggleAllCategories}
        onCompleteAllMarkers={handleCompleteAllMarkers}
        onResetAllMarkers={handleResetAllMarkers}
        hideCompleted={hideCompleted}
        onToggleHideCompleted={() => setHideCompleted(prev => !prev)}
        activeGroup={activeGroup}
        onSelectGroup={setActiveGroup}
        isAllCompleted={isAllCompleted}
        currentMapId={currentRegion.id}
        onFocusMarker={handleFocusMarker}
        onOpenHistory={(markerId, title) => setWikiModalState({ type: 'history', markerId, markerTitle: title })}
        onOpenOptions={() => setIsOptionsOpen(true)}
        onOpenAuth={() => setIsAuthOpen(true)}
        currentUser={currentUser}
        favoriteIds={favoriteIds}
        favoriteItems={favoriteItems}
        onToggleFavorite={toggleMarkerFavorite}
        markersData={markersData}
      />

      {/* 5. Mobile Bottom Dock Bar */}
      <BentoMobileDock
        activeTab={mobileTab}
        onSelectTab={handleMobileTabSelect}
        onOpenChat={() => setIsChatOpen(true)}
        sidebarOpen={sidebarOpen}
      />

      {/* 6. Bento Options & Settings Modal */}
      <BentoOptionsModal
        isOpen={isOptionsOpen}
        onClose={() => setIsOptionsOpen(false)}
        enableClustering={enableClustering}
        onToggleClustering={handleToggleClustering}
      />

      {/* 7. Bento Auth & Profile Modal (Thin Client Shell) */}
      <BentoAuthModal
        isOpen={isAuthOpen}
        onClose={() => setIsAuthOpen(false)}
      />

      {/* 8. Bento Wiki Modals Master Container (Edit, History, Move, Delete) */}
      <BentoModalsContainer
        wikiModalState={wikiModalState}
        onCloseWikiModal={() => setWikiModalState(null)}
        currentMapId={currentRegion.id}
        onProposalSubmitted={handleProposalSubmitted}
      />

      {/* 9. Realtime User Live Chat Modal */}
      <LiveChatModal
        isOpen={isChatOpen}
        onClose={() => setIsChatOpen(false)}
        currentUser={currentUser}
      />

      {/* 9. Image Lightbox Modal */}
      <ImageLightboxModal
        imgSrc={activeLightboxImg}
        onClose={() => setActiveLightboxImg(null)}
      />

      {/* 10. Video Lightbox Modal */}
      {activeVideoUrl && (
        <div className="bento-lightbox-overlay" onClick={() => setActiveVideoUrl(null)}>
          <div className="bento-lightbox-card" onClick={e => e.stopPropagation()} style={{ width: '85vw', maxWidth: '800px', height: '65vh', maxHeight: '520px' }}>
            <button className="bento-lightbox-close-btn" onClick={() => setActiveVideoUrl(null)} title="닫기">
              <i className="fa-solid fa-xmark"></i>
            </button>
            <iframe
              src={activeVideoUrl}
              title="비디오 팝업"
              style={{ width: '100%', height: '100%', border: 'none', borderRadius: '12px' }}
              allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"
              allowFullScreen
            ></iframe>
          </div>
        </div>
      )}

      {/* 11. Bento Global Toast Notification System */}
      <BentoToastContainer />
    </div>
  );
}

// --------------------------------------------------------------------------
// RENDER REACT APP
// --------------------------------------------------------------------------
const rootElement = document.getElementById('root') || document.getElementById('beta-root');
if (rootElement) {
  const root = ReactDOM.createRoot(rootElement);
  root.render(<BetaApp />);
}
