/* eslint-disable @typescript-eslint/no-require-imports */
"use client";

import React, { useEffect, useState, useCallback, useMemo, useRef } from "react";
import dynamic from "next/dynamic";
import { DashboardElement, ChartDataSource } from "@/components/admin/dashboard/types";
import { Button } from "@/components/ui/button";
import { Loader2, RefreshCw } from "lucide-react";
import { applyColumnMapping } from "@/lib/utils/columnMapping";
import { getApiUrl } from "@/lib/utils/apiUrl";
import "leaflet/dist/leaflet.css";

// Popup 말풍선 꼬리 제거 스타일
if (typeof document !== "undefined") {
  const style = document.createElement("style");
  style.textContent = `
    .leaflet-popup-tip-container {
      display: none !important;
    }
    .leaflet-popup-content-wrapper {
      border-radius: 8px !important;
    }
  `;
  document.head.appendChild(style);
}

// Leaflet 아이콘 경로 설정 (엑박 방지)
if (typeof window !== "undefined") {
  import("leaflet").then((L) => {
    delete (L.Icon.Default.prototype as any)._getIconUrl;
    L.Icon.Default.mergeOptions({
      iconRetinaUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon-2x.png",
      iconUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png",
      shadowUrl: "https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png",
    });
  });
}

// Leaflet 동적 import (SSR 방지)
const MapContainer = dynamic(() => import("react-leaflet").then((mod) => mod.MapContainer), { ssr: false });
const TileLayer = dynamic(() => import("react-leaflet").then((mod) => mod.TileLayer), { ssr: false });
const Marker = dynamic(() => import("react-leaflet").then((mod) => mod.Marker), { ssr: false });
const Popup = dynamic(() => import("react-leaflet").then((mod) => mod.Popup), { ssr: false });
const Polygon = dynamic(() => import("react-leaflet").then((mod) => mod.Polygon), { ssr: false });
const GeoJSON = dynamic(() => import("react-leaflet").then((mod) => mod.GeoJSON), { ssr: false });
const Polyline = dynamic(() => import("react-leaflet").then((mod) => mod.Polyline), { ssr: false });

// 브이월드 API 키
const VWORLD_API_KEY = "97AD30D5-FDC4-3481-99C3-158E36422033";

interface MapTestWidgetV2Props {
  element: DashboardElement;
}

interface MarkerData {
  id?: string;
  lat: number;
  lng: number;
  latitude?: number;
  longitude?: number;
  name: string;
  status?: string;
  description?: string;
  source?: string; // 어느 데이터 소스에서 왔는지
  color?: string; // 마커 색상
  heading?: number; // 진행 방향 (0-360도, 0=북쪽)
  prevLat?: number; // 이전 위도 (방향 계산용)
  prevLng?: number; // 이전 경도 (방향 계산용)
}

interface PolygonData {
  id?: string;
  name: string;
  coordinates: [number, number][] | [number, number][][]; // 단일 폴리곤 또는 멀티 폴리곤
  status?: string;
  description?: string;
  source?: string;
  color?: string;
  opacity?: number; // 투명도 (0.0 ~ 1.0)
}

// 이동경로 타입
interface RoutePoint {
  lat: number;
  lng: number;
  recordedAt: string;
}

export default function MapTestWidgetV2({ element }: MapTestWidgetV2Props) {
  const [markers, setMarkers] = useState<MarkerData[]>([]);
  const prevMarkersRef = useRef<MarkerData[]>([]); // 이전 마커 위치 저장 (useRef 사용)
  const [polygons, setPolygons] = useState<PolygonData[]>([]);
  const [loading, setLoading] = useState(false);
  const [error, setError] = useState<string | null>(null);
  const [geoJsonData, setGeoJsonData] = useState<any>(null);
  const [lastRefreshTime, setLastRefreshTime] = useState<Date | null>(null);

  // 이동경로 상태
  const [routePoints, setRoutePoints] = useState<RoutePoint[]>([]);
  const [selectedUserId, setSelectedUserId] = useState<string | null>(null);
  const [routeLoading, setRouteLoading] = useState(false);
  const [routeDate, setRouteDate] = useState<string>(new Date().toISOString().split("T")[0]); // YYYY-MM-DD 형식

  // dataSources를 useMemo로 추출 (circular reference 방지)
  const dataSources = useMemo(() => {
    return element?.dataSources || element?.chartConfig?.dataSources;
  }, [element?.dataSources, element?.chartConfig?.dataSources]);

  // 두 좌표 사이의 방향 계산 (0-360도, 0=북쪽)
  const calculateHeading = useCallback((lat1: number, lng1: number, lat2: number, lng2: number): number => {
    const dLng = (lng2 - lng1) * (Math.PI / 180);
    const lat1Rad = lat1 * (Math.PI / 180);
    const lat2Rad = lat2 * (Math.PI / 180);

    const y = Math.sin(dLng) * Math.cos(lat2Rad);
    const x = Math.cos(lat1Rad) * Math.sin(lat2Rad) - Math.sin(lat1Rad) * Math.cos(lat2Rad) * Math.cos(dLng);

    let heading = Math.atan2(y, x) * (180 / Math.PI);
    heading = (heading + 360) % 360; // 0-360 범위로 정규화

    return heading;
  }, []);

  // 이동경로 로드 함수
  const loadRoute = useCallback(
    async (userId: string, date?: string) => {
      if (!userId) {
        return;
      }

      setRouteLoading(true);
      setSelectedUserId(userId);

      try {
        // 선택한 날짜 기준으로 이동경로 조회
        const targetDate = date || routeDate;
        const startOfDay = `${targetDate}T00:00:00.000Z`;
        const endOfDay = `${targetDate}T23:59:59.999Z`;

        const query = `SELECT latitude, longitude, recorded_at 
                     FROM vehicle_location_history 
                     WHERE user_id = '${userId}' 
                     AND recorded_at >= '${startOfDay}'
                     AND recorded_at <= '${endOfDay}'
                     ORDER BY recorded_at ASC`;

        const response = await fetch(getApiUrl("/api/dashboards/execute-query"), {
          method: "POST",
          headers: {
            "Content-Type": "application/json",
            Authorization: `Bearer ${typeof window !== "undefined" ? localStorage.getItem("authToken") || "" : ""}`,
          },
          body: JSON.stringify({ query }),
        });

        if (response.ok) {
          const result = await response.json();
          if (result.success && result.data.rows.length > 0) {
            const points: RoutePoint[] = result.data.rows.map((row: any) => ({
              lat: parseFloat(row.latitude),
              lng: parseFloat(row.longitude),
              recordedAt: row.recorded_at,
            }));

            setRoutePoints(points);
          } else {
            setRoutePoints([]);
          }
        }
      } catch {
        setRoutePoints([]);
      }

      setRouteLoading(false);
    },
    [routeDate],
  );

  // 이동경로 숨기기
  const clearRoute = useCallback(() => {
    setSelectedUserId(null);
    setRoutePoints([]);
  }, []);

  // 다중 데이터 소스 로딩
  const loadMultipleDataSources = useCallback(async () => {
    if (!dataSources || dataSources.length === 0) {
      return;
    }
    setLoading(true);
    setError(null);

    try {
      // 모든 데이터 소스를 병렬로 로딩
      const results = await Promise.allSettled(
        dataSources.map(async (source) => {
          try {
            if (source.type === "api") {
              return await loadRestApiData(source);
            } else if (source.type === "database") {
              return await loadDatabaseData(source);
            }

            return { markers: [], polygons: [] };
          } catch (err: any) {
            return { markers: [], polygons: [] };
          }
        }),
      );

      // 성공한 데이터만 병합
      const allMarkers: MarkerData[] = [];
      const allPolygons: PolygonData[] = [];

      results.forEach((result, index) => {
        if (result.status === "fulfilled" && result.value) {
          const value = result.value as { markers: MarkerData[]; polygons: PolygonData[] };

          // 마커 병합
          if (value.markers && Array.isArray(value.markers)) {
            allMarkers.push(...value.markers);
          }

          // 폴리곤 병합
          if (value.polygons && Array.isArray(value.polygons)) {
            allPolygons.push(...value.polygons);
          }
        }
      });

      // 이전 마커 위치와 비교하여 진행 방향 계산
      const markersWithHeading = allMarkers.map((marker) => {
        const prevMarker = prevMarkersRef.current.find((pm) => pm.id === marker.id);

        if (prevMarker && (prevMarker.lat !== marker.lat || prevMarker.lng !== marker.lng)) {
          // 이동했으면 방향 계산
          const heading = calculateHeading(prevMarker.lat, prevMarker.lng, marker.lat, marker.lng);
          return {
            ...marker,
            heading,
            prevLat: prevMarker.lat,
            prevLng: prevMarker.lng,
          };
        }

        // 이동하지 않았거나 이전 데이터가 없으면 기존 heading 유지 (또는 0)
        return {
          ...marker,
          heading: marker.heading || prevMarker?.heading || 0,
        };
      });

      prevMarkersRef.current = markersWithHeading; // 다음 비교를 위해 현재 위치 저장 (useRef 사용)
      setMarkers(markersWithHeading);
      setPolygons(allPolygons);
      setLastRefreshTime(new Date());
    } catch (err: any) {
      setError(err.message);
    } finally {
      setLoading(false);
    }
  }, [dataSources, calculateHeading]); // prevMarkersRef는 의존성에 포함하지 않음 (useRef이므로)

  // 수동 새로고침 핸들러
  const handleManualRefresh = useCallback(() => {
    loadMultipleDataSources();
  }, [loadMultipleDataSources]);

  // REST API 데이터 로딩
  const loadRestApiData = async (
    source: ChartDataSource,
  ): Promise<{ markers: MarkerData[]; polygons: PolygonData[] }> => {
    if (!source.endpoint) {
      throw new Error("API endpoint가 없습니다.");
    }

    // 쿼리 파라미터 구성
    const queryParams: Record<string, string> = {};
    if (source.queryParams) {
      source.queryParams.forEach((param) => {
        if (param.key && param.value) {
          queryParams[param.key] = param.value;
        }
      });
    }

    // 헤더 구성
    const headers: Record<string, string> = {};
    if (source.headers) {
      source.headers.forEach((header) => {
        if (header.key && header.value) {
          headers[header.key] = header.value;
        }
      });
    }

    // Request Body 파싱
    let requestBody: any = undefined;
    if (source.body) {
      try {
        requestBody = JSON.parse(source.body);
      } catch {
        // JSON 파싱 실패시 문자열 그대로 사용
        requestBody = source.body;
      }
    }

    // 백엔드 프록시를 통해 API 호출
    const response = await fetch(getApiUrl("/api/dashboards/fetch-external-api"), {
      method: "POST",
      headers: {
        "Content-Type": "application/json",
      },
      credentials: "include",
      body: JSON.stringify({
        url: source.endpoint,
        method: source.method || "GET",
        headers,
        queryParams,
        body: requestBody,
        externalConnectionId: source.externalConnectionId,
      }),
    });

    if (!response.ok) {
      throw new Error(`API 호출 실패: ${response.status}`);
    }

    const result = await response.json();

    if (!result.success) {
      throw new Error(result.message || "API 호출 실패");
    }

    // 데이터 추출 및 파싱
    let data = result.data;

    // 텍스트 형식 데이터 체크 (기상청 API 등)
    if (data && typeof data === "object" && data.text && typeof data.text === "string") {
      const parsedData = parseTextData(data.text);
      if (parsedData.length > 0) {
        // 컬럼 매핑 적용
        const mappedData = applyColumnMapping(parsedData, source.columnMapping);
        const result = convertToMapData(mappedData, source.name || source.id || "API", source.mapDisplayType, source);
        return result;
      }
    }

    // JSON Path로 데이터 추출
    if (source.jsonPath) {
      const pathParts = source.jsonPath.split(".");
      for (const part of pathParts) {
        data = data?.[part];
      }
    }

    // 데이터가 null/undefined면 빈 결과 반환
    if (data === null || data === undefined) {
      return { markers: [], polygons: [] };
    }

    const rows = Array.isArray(data) ? data : [data];

    // 컬럼 매핑 적용
    const mappedRows = applyColumnMapping(rows, source.columnMapping);

    // 마커와 폴리곤으로 변환 (mapDisplayType + dataSource 전달)
    const mapData = convertToMapData(mappedRows, source.name || source.id || "API", source.mapDisplayType, source);

    // ✅ REST API 데이터를 vehicle_location_history에 자동 저장 (경로 보기용)
    // - 모든 REST API 차량 위치 데이터는 자동으로 저장됨
    if (mapData.markers.length > 0) {
      try {
        const authToken = typeof window !== "undefined" ? localStorage.getItem("authToken") || "" : "";
        
        // 마커 데이터를 vehicle_location_history에 저장
        for (const marker of mapData.markers) {
          // user_id 추출 (마커 description에서 파싱)
          let userId = "";
          let vehicleId: number | undefined = undefined;
          let vehicleName = "";
          
          if (marker.description) {
            try {
              const parsed = JSON.parse(marker.description);
              // 다양한 필드명 지원 (plate_no 우선 - 차량 번호판으로 경로 구분)
              userId = parsed.plate_no || parsed.plateNo || parsed.car_number || parsed.carNumber ||
                       parsed.user_id || parsed.userId || parsed.driver_id || parsed.driverId || 
                       parsed.car_no || parsed.carNo || parsed.vehicle_no || parsed.vehicleNo ||
                       parsed.id || parsed.code || "";
              vehicleId = parsed.vehicle_id || parsed.vehicleId || parsed.car_id || parsed.carId;
              vehicleName = parsed.plate_no || parsed.plateNo || parsed.car_name || parsed.carName || 
                           parsed.vehicle_name || parsed.vehicleName || parsed.name || parsed.title || "";
            } catch {
              // 파싱 실패 시 무시
            }
          }
          
          // user_id가 없으면 마커 이름이나 ID를 사용
          if (!userId) {
            userId = marker.name || marker.id || `marker_${Date.now()}`;
          }
          
          // vehicle_location_history에 저장
          await fetch(getApiUrl("/api/dynamic-form/location-history"), {
            method: "POST",
            headers: {
              "Content-Type": "application/json",
              Authorization: `Bearer ${authToken}`,
            },
            credentials: "include",
            body: JSON.stringify({
              latitude: marker.lat,
              longitude: marker.lng,
              userId: userId,
              vehicleId: vehicleId,
              tripStatus: "api_tracking", // REST API에서 가져온 데이터 표시
              departureName: source.name || "REST API",
              destinationName: vehicleName || marker.name,
            }),
          });
          
          console.log("📍 [saveToHistory] 저장 완료:", { userId, lat: marker.lat, lng: marker.lng });
        }
      } catch (saveError) {
        console.error("❌ [saveToHistory] 저장 실패:", saveError);
        // 저장 실패해도 마커 표시는 계속
      }
    }

    return mapData;
  };

  // Database 데이터 로딩
  const loadDatabaseData = async (
    source: ChartDataSource,
  ): Promise<{ markers: MarkerData[]; polygons: PolygonData[] }> => {
    if (!source.query) {
      throw new Error("SQL 쿼리가 없습니다.");
    }

    let rows: any[] = [];

    if (source.connectionType === "external" && source.externalConnectionId) {
      // 외부 DB
      const { ExternalDbConnectionAPI } = await import("@/lib/api/externalDbConnection");
      const externalResult = await ExternalDbConnectionAPI.executeQuery(
        parseInt(source.externalConnectionId),
        source.query,
      );

      if (!externalResult.success || !externalResult.data) {
        throw new Error(externalResult.message || "외부 DB 쿼리 실행 실패");
      }

      const resultData = externalResult.data as unknown as {
        rows: Record<string, unknown>[];
      };

      rows = resultData.rows;
    } else {
      // 현재 DB
      const { dashboardApi } = await import("@/lib/api/dashboard");
      const result = await dashboardApi.executeQuery(source.query);

      rows = result.rows;
    }

    // 컬럼 매핑 적용
    const mappedRows = applyColumnMapping(rows, source.columnMapping);

    // 마커와 폴리곤으로 변환 (mapDisplayType + dataSource 전달)
    return convertToMapData(mappedRows, source.name || source.id || "Database", source.mapDisplayType, source);
  };

  // XML 데이터 파싱 (UTIC API 등)
  const parseXmlData = (xmlText: string): any[] => {
    try {
      const parser = new DOMParser();
      const xmlDoc = parser.parseFromString(xmlText, "text/xml");

      const records = xmlDoc.getElementsByTagName("record");
      const results: any[] = [];

      for (let i = 0; i < records.length; i++) {
        const record = records[i];
        const obj: any = {};

        for (let j = 0; j < record.children.length; j++) {
          const child = record.children[j];
          obj[child.tagName] = child.textContent || "";
        }

        results.push(obj);
      }

      return results;
    } catch (error) {
      return [];
    }
  };

  // 텍스트 데이터 파싱 (CSV, 기상청 형식 등)
  const parseTextData = (text: string): any[] => {
    try {
      // XML 형식 감지
      if (text.trim().startsWith("<?xml") || text.trim().startsWith("<result>")) {
        return parseXmlData(text);
      }

      const lines = text.split("\n").filter((line) => {
        const trimmed = line.trim();
        return trimmed && !trimmed.startsWith("#") && !trimmed.startsWith("=") && !trimmed.startsWith("---");
      });

      if (lines.length === 0) return [];

      // CSV 형식으로 파싱
      const result: any[] = [];

      for (let i = 0; i < lines.length; i++) {
        const line = lines[i];
        const values = line.split(",").map((v) => v.trim().replace(/,=$/g, ""));

        // 기상특보 형식: 지역코드, 지역명, 하위코드, 하위지역명, 발표시각, 특보종류, 등급, 발표상태, 설명
        if (values.length >= 4) {
          const obj: any = {
            code: values[0] || "", // 지역 코드 (예: L1070000)
            region: values[1] || "", // 지역명 (예: 경상북도)
            subCode: values[2] || "", // 하위 코드 (예: L1071600)
            subRegion: values[3] || "", // 하위 지역명 (예: 영주시)
            tmFc: values[4] || "", // 발표시각
            type: values[5] || "", // 특보종류 (강풍, 호우 등)
            level: values[6] || "", // 등급 (주의, 경보)
            status: values[7] || "", // 발표상태
            description: values.slice(8).join(", ").trim() || "",
          };

          // 지역 이름 설정 (하위 지역명 우선, 없으면 상위 지역명)
          obj.name = obj.subRegion || obj.region || obj.code;

          result.push(obj);
        }
      }

      return result;
    } catch (error) {
      return [];
    }
  };

  // 데이터를 마커와 폴리곤으로 변환
  const convertToMapData = (
    rows: any[],
    sourceName: string,
    mapDisplayType?: "auto" | "marker" | "polygon",
    dataSource?: ChartDataSource,
  ): { markers: MarkerData[]; polygons: PolygonData[] } => {
    if (rows.length === 0) return { markers: [], polygons: [] };

    const markers: MarkerData[] = [];
    const polygons: PolygonData[] = [];

    rows.forEach((row, index) => {
      // null/undefined 체크
      if (!row) {
        return;
      }

      // 텍스트 데이터 체크 (기상청 API 등)
      if (row && typeof row === "object" && row.text && typeof row.text === "string") {
        const parsedData = parseTextData(row.text);

        // 파싱된 데이터를 재귀적으로 변환 (색상 정보 전달)
        const result = convertToMapData(parsedData, sourceName, mapDisplayType, dataSource);
        markers.push(...result.markers);
        polygons.push(...result.polygons);
        return; // 이 행은 처리 완료
      }

      // 폴리곤 데이터 체크 (coordinates 필드가 배열인 경우 또는 강제 polygon 모드)
      if (row.coordinates && Array.isArray(row.coordinates) && row.coordinates.length > 0) {
        // coordinates가 [lat, lng] 배열의 배열인지 확인
        const firstCoord = row.coordinates[0];
        if (Array.isArray(firstCoord) && firstCoord.length === 2) {
          polygons.push({
            id: `${sourceName}-polygon-${index}-${row.code || row.id || Date.now()}`, // 고유 ID 생성
            name: row.name || row.title || `영역 ${index + 1}`,
            coordinates: row.coordinates as [number, number][],
            status: row.status || row.level,
            description: JSON.stringify(row, null, 2), // 항상 전체 row 객체를 JSON으로 저장
            source: sourceName,
            color: dataSource?.polygonColor || getColorByStatus(row.status || row.level),
          });
          return; // 폴리곤으로 처리했으므로 마커로는 추가하지 않음
        }
      }

      // 지역명으로 해상 구역 확인 (auto 또는 polygon 모드일 때만)
      const regionName = row.name || row.area || row.region || row.location || row.subRegion;
      if (regionName && MARITIME_ZONES[regionName] && mapDisplayType !== "marker") {
        polygons.push({
          id: `${sourceName}-polygon-${index}-${row.code || row.id || Date.now()}`, // 고유 ID 생성
          name: regionName,
          coordinates: MARITIME_ZONES[regionName] as [number, number][],
          status: row.status || row.level,
          description: JSON.stringify(row, null, 2), // 항상 전체 row 객체를 JSON으로 저장
          source: sourceName,
          color: dataSource?.polygonColor || getColorByStatus(row.status || row.level),
        });
        return; // 폴리곤으로 처리했으므로 마커로는 추가하지 않음
      }

      // 마커 데이터 처리 (위도/경도가 있는 경우)
      let lat = row.lat || row.latitude || row.y || row.locationDataY;
      let lng = row.lng || row.longitude || row.x || row.locationDataX;

      // 위도/경도가 없으면 지역 코드/지역명으로 변환 시도
      if (
        (lat === undefined || lng === undefined) &&
        (row.code || row.areaCode || row.regionCode || row.tmFc || row.stnId)
      ) {
        const regionCode = row.code || row.areaCode || row.regionCode || row.tmFc || row.stnId;
        const coords = getCoordinatesByRegionCode(regionCode);
        if (coords) {
          lat = coords.lat;
          lng = coords.lng;
        }
      }

      // 지역명으로도 시도
      if ((lat === undefined || lng === undefined) && (row.name || row.area || row.region || row.location)) {
        const regionName = row.name || row.area || row.region || row.location;
        const coords = getCoordinatesByRegionName(regionName);
        if (coords) {
          lat = coords.lat;
          lng = coords.lng;
        }
      }

      // mapDisplayType이 "polygon"이면 무조건 폴리곤으로 처리
      if (mapDisplayType === "polygon") {
        const regionName = row.name || row.subRegion || row.region || row.area;
        if (regionName) {
          polygons.push({
            id: `${sourceName}-polygon-${index}-${row.code || row.id || Date.now()}`, // 고유 ID 생성
            name: regionName,
            coordinates: [], // GeoJSON에서 좌표를 가져올 것
            status: row.status || row.level,
            description: JSON.stringify(row, null, 2), // 항상 전체 row 객체를 JSON으로 저장
            source: sourceName,
            color: dataSource?.polygonColor || getColorByStatus(row.status || row.level),
          });
        }
        return; // 폴리곤으로 처리했으므로 마커로는 추가하지 않음
      }

      // 위도/경도가 있고 polygon 모드가 아니면 마커로 처리
      if (lat !== undefined && lng !== undefined && (mapDisplayType as string) !== "polygon") {
        markers.push({
          // 진행 방향(heading) 계산을 위해 ID는 새로고침마다 바뀌지 않도록 고정값 사용
          // 중복 방지를 위해 sourceName과 index를 조합하여 고유 ID 생성
          id: `${sourceName}-${row.id || row.code || "marker"}-${index}`,
          lat: Number(lat),
          lng: Number(lng),
          latitude: Number(lat),
          longitude: Number(lng),
          name: row.name || row.title || row.area || row.region || `위치 ${index + 1}`,
          status: row.status || row.level,
          description: JSON.stringify(row, null, 2), // 항상 전체 row 객체를 JSON으로 저장
          source: sourceName,
          color: row.marker_color || row.color || dataSource?.markerColor || "#3b82f6", // 쿼리 색상 > 설정 색상 > 기본 파랑
        });
      } else {
        // 위도/경도가 없는 육지 지역 → 폴리곤으로 추가 (GeoJSON 매칭용)
        const regionName = row.name || row.subRegion || row.region || row.area;
        if (regionName) {
          polygons.push({
            id: `${sourceName}-polygon-${index}-${row.code || row.id || Date.now()}`, // 고유 ID 생성
            name: regionName,
            coordinates: [], // GeoJSON에서 좌표를 가져올 것
            status: row.status || row.level,
            description: JSON.stringify(row, null, 2), // 항상 전체 row 객체를 JSON으로 저장
            source: sourceName,
            color: dataSource?.polygonColor || getColorByStatus(row.status || row.level),
          });
        }
      }
    });

    return { markers, polygons };
  };

  // 상태에 따른 색상 반환
  const getColorByStatus = (status?: string): string => {
    if (!status) return "#3b82f6"; // 기본 파란색

    const statusLower = status.toLowerCase();
    if (statusLower.includes("경보") || statusLower.includes("위험")) return "#ef4444"; // 빨강
    if (statusLower.includes("주의")) return "#f59e0b"; // 주황
    if (statusLower.includes("정상")) return "#10b981"; // 초록

    return "#3b82f6"; // 기본 파란색
  };

  // 지역 코드를 위도/경도로 변환
  const getCoordinatesByRegionCode = (code: string): { lat: number; lng: number } | null => {
    // 기상청 지역 코드 매핑 (예시)
    const regionCodeMap: Record<string, { lat: number; lng: number }> = {
      // 서울/경기
      "11": { lat: 37.5665, lng: 126.978 }, // 서울
      "41": { lat: 37.4138, lng: 127.5183 }, // 경기

      // 강원
      "42": { lat: 37.8228, lng: 128.1555 }, // 강원

      // 충청
      "43": { lat: 36.6357, lng: 127.4913 }, // 충북
      "44": { lat: 36.5184, lng: 126.8 }, // 충남

      // 전라
      "45": { lat: 35.7175, lng: 127.153 }, // 전북
      "46": { lat: 34.8679, lng: 126.991 }, // 전남

      // 경상
      "47": { lat: 36.4919, lng: 128.8889 }, // 경북
      "48": { lat: 35.4606, lng: 128.2132 }, // 경남

      // 제주
      "50": { lat: 33.4996, lng: 126.5312 }, // 제주

      // 광역시
      "26": { lat: 35.1796, lng: 129.0756 }, // 부산
      "27": { lat: 35.8714, lng: 128.6014 }, // 대구
      "28": { lat: 35.1595, lng: 126.8526 }, // 광주
      "29": { lat: 36.3504, lng: 127.3845 }, // 대전
      "30": { lat: 35.5384, lng: 129.3114 }, // 울산
      "31": { lat: 36.8, lng: 127.7 }, // 세종
    };

    return regionCodeMap[code] || null;
  };

  // 해상 구역 폴리곤 좌표 (기상청 특보 구역 기준)
  const MARITIME_ZONES: Record<string, Array<[number, number]>> = {
    // 서해 해역
    "인천·경기북부앞바다": [
      [37.8, 125.8],
      [37.8, 126.5],
      [37.3, 126.5],
      [37.3, 125.8],
    ],
    "인천·경기남부앞바다": [
      [37.3, 125.7],
      [37.3, 126.4],
      [36.8, 126.4],
      [36.8, 125.7],
    ],
    충남북부앞바다: [
      [36.8, 125.6],
      [36.8, 126.3],
      [36.3, 126.3],
      [36.3, 125.6],
    ],
    충남남부앞바다: [
      [36.3, 125.5],
      [36.3, 126.2],
      [35.8, 126.2],
      [35.8, 125.5],
    ],
    전북북부앞바다: [
      [35.8, 125.4],
      [35.8, 126.1],
      [35.3, 126.1],
      [35.3, 125.4],
    ],
    전북남부앞바다: [
      [35.3, 125.3],
      [35.3, 126.0],
      [34.8, 126.0],
      [34.8, 125.3],
    ],
    전남북부서해앞바다: [
      [35.5, 125.2],
      [35.5, 125.9],
      [35.0, 125.9],
      [35.0, 125.2],
    ],
    전남중부서해앞바다: [
      [35.0, 125.1],
      [35.0, 125.8],
      [34.5, 125.8],
      [34.5, 125.1],
    ],
    전남남부서해앞바다: [
      [34.5, 125.0],
      [34.5, 125.7],
      [34.0, 125.7],
      [34.0, 125.0],
    ],
    서해중부안쪽먼바다: [
      [37.5, 124.5],
      [37.5, 126.0],
      [36.0, 126.0],
      [36.0, 124.5],
    ],
    서해중부바깥먼바다: [
      [37.5, 123.5],
      [37.5, 125.0],
      [36.0, 125.0],
      [36.0, 123.5],
    ],
    서해남부북쪽안쪽먼바다: [
      [36.0, 124.5],
      [36.0, 126.0],
      [35.0, 126.0],
      [35.0, 124.5],
    ],
    서해남부북쪽바깥먼바다: [
      [36.0, 123.5],
      [36.0, 125.0],
      [35.0, 125.0],
      [35.0, 123.5],
    ],
    서해남부남쪽안쪽먼바다: [
      [35.0, 124.0],
      [35.0, 125.5],
      [34.0, 125.5],
      [34.0, 124.0],
    ],
    서해남부남쪽바깥먼바다: [
      [35.0, 123.0],
      [35.0, 124.5],
      [33.5, 124.5],
      [33.5, 123.0],
    ],
    // 제주도 해역
    제주도남부앞바다: [
      [33.25, 126.0],
      [33.25, 126.85],
      [33.0, 126.85],
      [33.0, 126.0],
    ],
    제주도남쪽바깥먼바다: [
      [33.15, 125.7],
      [33.15, 127.3],
      [32.5, 127.3],
      [32.5, 125.7],
    ],
    제주도동부앞바다: [
      [33.4, 126.7],
      [33.4, 127.25],
      [33.05, 127.25],
      [33.05, 126.7],
    ],
    제주도남동쪽안쪽먼바다: [
      [33.3, 126.85],
      [33.3, 127.95],
      [32.65, 127.95],
      [32.65, 126.85],
    ],
    제주도남서쪽안쪽먼바다: [
      [33.3, 125.35],
      [33.3, 126.45],
      [32.7, 126.45],
      [32.7, 125.35],
    ],
    // 남해 해역
    남해동부앞바다: [
      [34.65, 128.3],
      [34.65, 129.65],
      [33.95, 129.65],
      [33.95, 128.3],
    ],
    남해동부안쪽먼바다: [
      [34.25, 127.95],
      [34.25, 129.75],
      [33.45, 129.75],
      [33.45, 127.95],
    ],
    남해동부바깥먼바다: [
      [33.65, 127.95],
      [33.65, 130.35],
      [32.45, 130.35],
      [32.45, 127.95],
    ],
    // 동해 해역
    경북북부앞바다: [
      [36.65, 129.2],
      [36.65, 130.1],
      [35.95, 130.1],
      [35.95, 129.2],
    ],
    경북남부앞바다: [
      [36.15, 129.1],
      [36.15, 129.95],
      [35.45, 129.95],
      [35.45, 129.1],
    ],
    동해남부남쪽안쪽먼바다: [
      [35.65, 129.35],
      [35.65, 130.65],
      [34.95, 130.65],
      [34.95, 129.35],
    ],
    동해남부남쪽바깥먼바다: [
      [35.25, 129.45],
      [35.25, 131.15],
      [34.15, 131.15],
      [34.15, 129.45],
    ],
    동해남부북쪽안쪽먼바다: [
      [36.6, 129.65],
      [36.6, 130.95],
      [35.85, 130.95],
      [35.85, 129.65],
    ],
    동해남부북쪽바깥먼바다: [
      [36.65, 130.35],
      [36.65, 132.15],
      [35.85, 132.15],
      [35.85, 130.35],
    ],
    // 강원 해역
    강원북부앞바다: [
      [38.15, 128.4],
      [38.15, 129.55],
      [37.45, 129.55],
      [37.45, 128.4],
    ],
    강원중부앞바다: [
      [37.65, 128.7],
      [37.65, 129.6],
      [36.95, 129.6],
      [36.95, 128.7],
    ],
    강원남부앞바다: [
      [37.15, 128.9],
      [37.15, 129.85],
      [36.45, 129.85],
      [36.45, 128.9],
    ],
    동해중부안쪽먼바다: [
      [38.55, 129.35],
      [38.55, 131.15],
      [37.25, 131.15],
      [37.25, 129.35],
    ],
    동해중부바깥먼바다: [
      [38.6, 130.35],
      [38.6, 132.55],
      [37.65, 132.55],
      [37.65, 130.35],
    ],
    // 울릉도·독도
    "울릉도.독도": [
      [37.7, 130.7],
      [37.7, 132.0],
      [37.4, 132.0],
      [37.4, 130.7],
    ],
  };

  // 지역명을 위도/경도로 변환
  const getCoordinatesByRegionName = (name: string): { lat: number; lng: number } | null => {
    // 먼저 해상 구역인지 확인
    if (MARITIME_ZONES[name]) {
      // 폴리곤의 중심점 계산
      const coords = MARITIME_ZONES[name];
      const centerLat = coords.reduce((sum, c) => sum + c[0], 0) / coords.length;
      const centerLng = coords.reduce((sum, c) => sum + c[1], 0) / coords.length;
      return { lat: centerLat, lng: centerLng };
    }

    const regionNameMap: Record<string, { lat: number; lng: number }> = {
      // 서울/경기
      서울: { lat: 37.5665, lng: 126.978 },
      서울특별시: { lat: 37.5665, lng: 126.978 },
      경기: { lat: 37.4138, lng: 127.5183 },
      경기도: { lat: 37.4138, lng: 127.5183 },
      인천: { lat: 37.4563, lng: 126.7052 },
      인천광역시: { lat: 37.4563, lng: 126.7052 },

      // 강원
      강원: { lat: 37.8228, lng: 128.1555 },
      강원도: { lat: 37.8228, lng: 128.1555 },
      강원특별자치도: { lat: 37.8228, lng: 128.1555 },

      // 충청
      충북: { lat: 36.6357, lng: 127.4913 },
      충청북도: { lat: 36.6357, lng: 127.4913 },
      충남: { lat: 36.5184, lng: 126.8 },
      충청남도: { lat: 36.5184, lng: 126.8 },
      대전: { lat: 36.3504, lng: 127.3845 },
      대전광역시: { lat: 36.3504, lng: 127.3845 },
      세종: { lat: 36.8, lng: 127.7 },
      세종특별자치시: { lat: 36.8, lng: 127.7 },

      // 전라
      전북: { lat: 35.7175, lng: 127.153 },
      전북특별자치도: { lat: 35.7175, lng: 127.153 },
      전라북도: { lat: 35.7175, lng: 127.153 },
      전남: { lat: 34.8679, lng: 126.991 },
      전라남도: { lat: 34.8679, lng: 126.991 },
      광주: { lat: 35.1595, lng: 126.8526 },
      광주광역시: { lat: 35.1595, lng: 126.8526 },

      // 경상
      경북: { lat: 36.4919, lng: 128.8889 },
      경상북도: { lat: 36.4919, lng: 128.8889 },
      포항: { lat: 36.019, lng: 129.3435 },
      포항시: { lat: 36.019, lng: 129.3435 },
      경주: { lat: 35.8562, lng: 129.2247 },
      경주시: { lat: 35.8562, lng: 129.2247 },
      안동: { lat: 36.5684, lng: 128.7294 },
      안동시: { lat: 36.5684, lng: 128.7294 },
      영주: { lat: 36.8056, lng: 128.6239 },
      영주시: { lat: 36.8056, lng: 128.6239 },
      경남: { lat: 35.4606, lng: 128.2132 },
      경상남도: { lat: 35.4606, lng: 128.2132 },
      창원: { lat: 35.228, lng: 128.6811 },
      창원시: { lat: 35.228, lng: 128.6811 },
      진주: { lat: 35.18, lng: 128.1076 },
      진주시: { lat: 35.18, lng: 128.1076 },
      부산: { lat: 35.1796, lng: 129.0756 },
      부산광역시: { lat: 35.1796, lng: 129.0756 },
      대구: { lat: 35.8714, lng: 128.6014 },
      대구광역시: { lat: 35.8714, lng: 128.6014 },
      울산: { lat: 35.5384, lng: 129.3114 },
      울산광역시: { lat: 35.5384, lng: 129.3114 },

      // 제주
      제주: { lat: 33.4996, lng: 126.5312 },
      제주도: { lat: 33.4996, lng: 126.5312 },
      제주특별자치도: { lat: 33.4996, lng: 126.5312 },

      // 울릉도/독도
      울릉도: { lat: 37.4845, lng: 130.9057 },
      "울릉도.독도": { lat: 37.4845, lng: 130.9057 },
      독도: { lat: 37.2433, lng: 131.8642 },
    };

    // 정확한 매칭
    if (regionNameMap[name]) {
      return regionNameMap[name];
    }

    // 부분 매칭 (예: "서울시 강남구" → "서울")
    for (const [key, value] of Object.entries(regionNameMap)) {
      if (name.includes(key)) {
        return value;
      }
    }

    return null;
  };

  // 데이터를 마커로 변환 (하위 호환성 - 현재 미사용)
  // eslint-disable-next-line @typescript-eslint/no-unused-vars
  const convertToMarkers = (rows: any[]): MarkerData[] => {
    if (rows.length === 0) return [];

    // 위도/경도 컬럼 찾기
    const firstRow = rows[0];
    const columns = Object.keys(firstRow);

    const latColumn = columns.find((col) => /^(lat|latitude|위도|y)$/i.test(col));
    const lngColumn = columns.find((col) => /^(lng|lon|longitude|경도|x)$/i.test(col));
    const nameColumn = columns.find((col) => /^(name|title|이름|명칭|location)$/i.test(col));

    if (!latColumn || !lngColumn) {
      return [];
    }

    return rows
      .map((row, index) => {
        const lat = parseFloat(row[latColumn]);
        const lng = parseFloat(row[lngColumn]);

        if (isNaN(lat) || isNaN(lng)) {
          return null;
        }

        const marker: MarkerData = {
          id: row.id || `marker-${index}`,
          lat,
          lng,
          latitude: lat,
          longitude: lng,
          name: row[nameColumn || "name"] || `위치 ${index + 1}`,
          status: row.status,
          description: JSON.stringify(row, null, 2),
        };
        return marker;
      })
      .filter((marker): marker is MarkerData => marker !== null);
  };

  // GeoJSON 데이터 로드
  useEffect(() => {
    const loadGeoJsonData = async () => {
      try {
        const response = await fetch("/geojson/korea-municipalities.json");
        const data = await response.json();
        setGeoJsonData(data);
      } catch (err) {
        // GeoJSON 로드 실패 처리
      }
    };
    loadGeoJsonData();
  }, []);

  // 초기 로드 및 자동 새로고침 (마커 데이터만 polling)
  useEffect(() => {
    if (!dataSources || dataSources.length === 0) {
      setMarkers([]);
      setPolygons([]);
      return;
    }

    // 즉시 첫 로드 (마커 데이터)
    loadMultipleDataSources();

    // 위젯 레벨의 새로고침 간격 사용 (초)
    const refreshInterval = element?.chartConfig?.refreshInterval ?? 5;

    // 0이면 자동 새로고침 비활성화
    if (refreshInterval === 0) {
      return;
    }

    const intervalId = setInterval(() => {
      loadMultipleDataSources();
    }, refreshInterval * 1000);

    return () => {
      clearInterval(intervalId);
    };
    // eslint-disable-next-line react-hooks/exhaustive-deps
  }, [dataSources, element?.chartConfig?.refreshInterval]);

  // 타일맵 URL (VWorld 한국 지도)
  const tileMapUrl = `https://api.vworld.kr/req/wmts/1.0.0/${VWORLD_API_KEY}/Base/{z}/{y}/{x}.png`;

  // 지도 중심점 계산
  const center: [number, number] =
    markers.length > 0
      ? [
          markers.reduce((sum, m) => sum + m.lat, 0) / markers.length,
          markers.reduce((sum, m) => sum + m.lng, 0) / markers.length,
        ]
      : [36.5, 127.5]; // 한국 중심

  return (
    <div className="bg-background flex h-full w-full flex-col">
      {/* 헤더 */}
      <div className="flex items-center justify-between border-b p-4">
        <div>
          <h3 className="text-lg font-semibold">{element?.customTitle || "지도"}</h3>
          <p className="text-muted-foreground text-xs">
            {dataSources?.length || 0}개 데이터 소스 연결됨
            {lastRefreshTime && (
              <span className="ml-2">• 마지막 업데이트: {lastRefreshTime.toLocaleTimeString("ko-KR")}</span>
            )}
          </p>
        </div>
        <div className="flex items-center gap-2">
          {/* 이동경로 날짜 선택 */}
          {selectedUserId && (
            <div className="flex items-center gap-1 rounded border bg-blue-50 px-2 py-1">
              <span className="text-xs text-blue-600">🛣️</span>
              <input
                type="date"
                value={routeDate}
                onChange={(e) => {
                  setRouteDate(e.target.value);
                  if (selectedUserId) {
                    loadRoute(selectedUserId, e.target.value);
                  }
                }}
                className="h-6 rounded border-none bg-transparent px-1 text-xs text-blue-600 focus:outline-none"
              />
              <span className="text-xs text-blue-600">({routePoints.length}개)</span>
              <button onClick={clearRoute} className="ml-1 text-xs text-blue-400 hover:text-blue-600">
                ✕
              </button>
            </div>
          )}
          <Button
            variant="outline"
            size="sm"
            onClick={handleManualRefresh}
            disabled={loading}
            className="h-8 gap-2 text-xs"
          >
            <RefreshCw className={`h-3 w-3 ${loading ? "animate-spin" : ""}`} />
            새로고침
          </Button>
          {loading && <Loader2 className="h-4 w-4 animate-spin" />}
        </div>
      </div>

      {/* 지도 */}
      <div className="relative flex-1">
        {error ? (
          <div className="flex h-full items-center justify-center">
            <p className="text-destructive text-sm">{error}</p>
          </div>
        ) : (
          <MapContainer
            key={`map-widget-${element.id}`}
            center={center}
            zoom={element.chartConfig?.initialZoom ?? 8}
            minZoom={element.chartConfig?.minZoom ?? 8}
            maxZoom={element.chartConfig?.maxZoom ?? 18}
            scrollWheelZoom
            doubleClickZoom
            touchZoom
            zoomControl
            style={{ width: "100%", height: "100%" }}
            className="z-0"
          >
            <TileLayer
              url={tileMapUrl}
              attribution="&copy; VWorld"
              minZoom={element.chartConfig?.minZoom ?? 8}
              maxZoom={element.chartConfig?.maxZoom ?? 18}
            />

            {/* 폴리곤 렌더링 */}
            {/* GeoJSON 렌더링 (육지 지역 경계선) */}
            {geoJsonData && polygons.length > 0 ? (
              <GeoJSON
                key={JSON.stringify(polygons.map((p) => p.id))} // 폴리곤 변경 시 재렌더링
                data={geoJsonData}
                style={(feature: any) => {
                  const ctpName = feature?.properties?.CTP_KOR_NM; // 시/도명 (예: 경상북도)
                  const sigName = feature?.properties?.SIG_KOR_NM; // 시/군/구명 (예: 군위군)

                  // 폴리곤 매칭 (시/군/구명 우선, 없으면 시/도명)
                  const matchingPolygon = polygons.find((p) => {
                    if (!p.name) return false;

                    // 정확한 매칭
                    if (p.name === sigName) {
                      return true;
                    }
                    if (p.name === ctpName) {
                      return true;
                    }

                    // 부분 매칭 (GeoJSON 지역명에 폴리곤 이름이 포함되는지)
                    if (sigName && sigName.includes(p.name)) {
                      return true;
                    }
                    if (ctpName && ctpName.includes(p.name)) {
                      return true;
                    }

                    // 역방향 매칭 (폴리곤 이름에 GeoJSON 지역명이 포함되는지)
                    if (sigName && p.name.includes(sigName)) {
                      return true;
                    }
                    if (ctpName && p.name.includes(ctpName)) {
                      return true;
                    }

                    return false;
                  });

                  if (matchingPolygon) {
                    return {
                      fillColor: matchingPolygon.color || "#3b82f6",
                      fillOpacity: 0.3,
                      color: matchingPolygon.color || "#3b82f6",
                      weight: 2,
                    };
                  }

                  return {
                    fillOpacity: 0,
                    opacity: 0,
                  };
                }}
                onEachFeature={(feature: any, layer: any) => {
                  const ctpName = feature?.properties?.CTP_KOR_NM;
                  const sigName = feature?.properties?.SIG_KOR_NM;

                  const matchingPolygon = polygons.find((p) => {
                    if (!p.name) return false;
                    if (p.name === sigName || p.name === ctpName) return true;
                    if (sigName && sigName.includes(p.name)) return true;
                    if (ctpName && ctpName.includes(p.name)) return true;
                    if (sigName && p.name.includes(sigName)) return true;
                    if (ctpName && p.name.includes(ctpName)) return true;
                    return false;
                  });

                  if (matchingPolygon) {
                    // 폴리곤의 데이터 소스 찾기
                    const polygonDataSource = dataSources?.find((ds) => ds.name === matchingPolygon.source);
                    const popupFields = polygonDataSource?.popupFields;

                    let popupContent = "";

                    // popupFields가 설정되어 있으면 설정된 필드만 표시
                    if (popupFields && popupFields.length > 0 && matchingPolygon.description) {
                      try {
                        const parsed = JSON.parse(matchingPolygon.description);
                        popupContent = `
                          <div class="min-w-[200px]">
                            ${matchingPolygon.source ? `<div class="mb-2 border-b pb-2"><div class="text-gray-500 text-xs">📡 ${matchingPolygon.source}</div></div>` : ""}
                            <div class="bg-gray-100 rounded p-2">
                              <div class="text-gray-900 mb-1 text-xs font-semibold">상세 정보</div>
                              <div class="space-y-2">
                                ${popupFields
                                  .map((field) => {
                                    const value = parsed[field.fieldName];
                                    if (value === undefined || value === null) return "";
                                    return `<div class="text-xs"><span class="text-gray-600 font-medium">${field.label}:</span> <span class="text-gray-900">${value}</span></div>`;
                                  })
                                  .join("")}
                              </div>
                            </div>
                          </div>
                        `;
                      } catch (error) {
                        // JSON 파싱 실패 시 기본 표시
                        popupContent = `
                          <div class="min-w-[200px]">
                            <div class="mb-2 font-semibold">${matchingPolygon.name}</div>
                            ${matchingPolygon.source ? `<div class="mb-1 text-xs text-muted-foreground">출처: ${matchingPolygon.source}</div>` : ""}
                            ${matchingPolygon.status ? `<div class="mb-1 text-xs">상태: ${matchingPolygon.status}</div>` : ""}
                            ${matchingPolygon.description ? `<div class="mt-2 max-h-[200px] overflow-auto text-xs"><pre class="whitespace-pre-wrap">${matchingPolygon.description}</pre></div>` : ""}
                          </div>
                        `;
                      }
                    } else {
                      // popupFields가 없으면 전체 데이터 표시
                      popupContent = `
                        <div class="min-w-[200px]">
                          <div class="mb-2 font-semibold">${matchingPolygon.name}</div>
                          ${matchingPolygon.source ? `<div class="mb-1 text-xs text-muted-foreground">출처: ${matchingPolygon.source}</div>` : ""}
                          ${matchingPolygon.status ? `<div class="mb-1 text-xs">상태: ${matchingPolygon.status}</div>` : ""}
                          ${matchingPolygon.description ? `<div class="mt-2 max-h-[200px] overflow-auto text-xs"><pre class="whitespace-pre-wrap">${matchingPolygon.description}</pre></div>` : ""}
                        </div>
                      `;
                    }

                    layer.bindPopup(popupContent);
                  }
                }}
              />
            ) : null}

            {/* 폴리곤 렌더링 (해상 구역만) */}
            {polygons
              .filter((p) => MARITIME_ZONES[p.name])
              .map((polygon) => {
                // 폴리곤의 데이터 소스 찾기
                const polygonDataSource = dataSources?.find((ds) => ds.name === polygon.source);
                const popupFields = polygonDataSource?.popupFields;

                return (
                  <Polygon
                    key={polygon.id}
                    positions={polygon.coordinates}
                    pathOptions={{
                      color: polygon.color || "#3b82f6",
                      fillColor: polygon.color || "#3b82f6",
                      fillOpacity: 0.3,
                      weight: 2,
                    }}
                  >
                    <Popup>
                      <div className="min-w-[200px]">
                        {/* popupFields가 설정되어 있으면 설정된 필드만 표시 */}
                        {popupFields && popupFields.length > 0 && polygon.description ? (
                          (() => {
                            try {
                              const parsed = JSON.parse(polygon.description);
                              return (
                                <>
                                  {polygon.source && (
                                    <div className="mb-2 border-b pb-2">
                                      <div className="text-muted-foreground text-xs">📡 {polygon.source}</div>
                                    </div>
                                  )}
                                  <div className="bg-muted rounded p-2">
                                    <div className="text-foreground mb-1 text-xs font-semibold">상세 정보</div>
                                    <div className="space-y-2">
                                      {popupFields.map((field, idx) => {
                                        const value = parsed[field.fieldName];
                                        if (value === undefined || value === null) return null;
                                        return (
                                          <div key={idx} className="text-xs">
                                            <span className="text-muted-foreground font-medium">{field.label}:</span>{" "}
                                            <span className="text-foreground">{String(value)}</span>
                                          </div>
                                        );
                                      })}
                                    </div>
                                  </div>
                                </>
                              );
                            } catch (error) {
                              // JSON 파싱 실패 시 기본 표시
                              return (
                                <>
                                  <div className="mb-2 font-semibold">{polygon.name}</div>
                                  {polygon.source && (
                                    <div className="text-muted-foreground mb-1 text-xs">출처: {polygon.source}</div>
                                  )}
                                  {polygon.status && <div className="mb-1 text-xs">상태: {polygon.status}</div>}
                                  {polygon.description && (
                                    <div className="mt-2 max-h-[200px] overflow-auto text-xs">
                                      <pre className="whitespace-pre-wrap">{polygon.description}</pre>
                                    </div>
                                  )}
                                </>
                              );
                            }
                          })()
                        ) : (
                          // popupFields가 없으면 전체 데이터 표시
                          <>
                            <div className="mb-2 font-semibold">{polygon.name}</div>
                            {polygon.source && (
                              <div className="text-muted-foreground mb-1 text-xs">출처: {polygon.source}</div>
                            )}
                            {polygon.status && <div className="mb-1 text-xs">상태: {polygon.status}</div>}
                            {polygon.description && (
                              <div className="mt-2 max-h-[200px] overflow-auto text-xs">
                                <pre className="whitespace-pre-wrap">{polygon.description}</pre>
                              </div>
                            )}
                          </>
                        )}
                      </div>
                    </Popup>
                  </Polygon>
                );
              })}

            {/* 마커 렌더링 */}
            {markers.map((marker) => {
              // 마커의 소스에 해당하는 데이터 소스 찾기
              const sourceDataSource = dataSources?.find((ds) => ds.name === marker.source) || dataSources?.[0];
              const markerType = sourceDataSource?.markerType || "circle";

              let markerIcon: any;
              if (typeof window !== "undefined") {
                const L = require("leaflet");
                // heading이 없거나 0일 때 기본값 90(동쪽/오른쪽)으로 설정하여 처음에 오른쪽을 보게 함
                const heading = marker.heading || 90;

                if (markerType === "arrow") {
                  // 화살표 마커
                  markerIcon = L.divIcon({
                    className: "custom-arrow-marker",
                    html: `
                      <div style="
                        width: 40px;
                        height: 40px;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        transform: translate(-50%, -50%) rotate(${heading}deg);
                        filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3));
                      ">
                        <svg width="40" height="40" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
                          <!-- 이등변 삼각형 화살표 (뾰족한 방향 표시) -->
                          <polygon 
                            points="20,5 28,30 12,30" 
                            fill="${marker.color || "#3b82f6"}" 
                            stroke="white" 
                            stroke-width="2"
                          />
                        </svg>
                      </div>
                    `,
                    iconSize: [40, 40],
                    iconAnchor: [20, 20],
                  });
                } else if (markerType === "truck") {
                  // 트럭 마커
                  // 트럭 아이콘이 오른쪽(90도)을 보고 있으므로, 북쪽(0도)으로 가려면 -90도 회전 필요
                  const rotation = heading - 90;

                  // 회전 각도가 90~270도 범위면 차량이 뒤집어짐 (바퀴가 위로)
                  // 이 경우 scaleY(-1)로 상하 반전하여 바퀴가 아래로 오도록 함
                  const normalizedRotation = ((rotation % 360) + 360) % 360;
                  const isFlipped = normalizedRotation > 90 && normalizedRotation < 270;
                  const transformStyle = isFlipped
                    ? `translate(-50%, -50%) rotate(${rotation}deg) scaleY(-1)`
                    : `translate(-50%, -50%) rotate(${rotation}deg)`;

                  markerIcon = L.divIcon({
                    className: "custom-truck-marker",
                    html: `
                      <div style="
                        width: 48px;
                        height: 48px;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        transform: ${transformStyle};
                        filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3));
                      ">
                        <svg width="48" height="48" viewBox="0 0 40 40" xmlns="http://www.w3.org/2000/svg">
                          <g>
                            <!-- 트럭 적재함 -->
                            <rect 
                              x="10" 
                              y="12" 
                              width="12" 
                              height="10" 
                              fill="${marker.color || "#3b82f6"}" 
                              stroke="white" 
                              stroke-width="1.5"
                              rx="1"
                            />
                            <!-- 트럭 운전석 -->
                            <path 
                              d="M 22 14 L 22 22 L 28 22 L 28 18 L 26 14 Z" 
                              fill="${marker.color || "#3b82f6"}" 
                              stroke="white" 
                              stroke-width="1.5"
                            />
                            <!-- 운전석 창문 -->
                            <rect 
                              x="23" 
                              y="15" 
                              width="3" 
                              height="4" 
                              fill="white"
                              opacity="0.8"
                            />
                            <!-- 앞 바퀴 -->
                            <circle 
                              cx="25" 
                              cy="23" 
                              r="2.5" 
                              fill="#333" 
                              stroke="white" 
                              stroke-width="1"
                            />
                            <!-- 뒷 바퀴 -->
                            <circle 
                              cx="14" 
                              cy="23" 
                              r="2.5" 
                              fill="#333" 
                              stroke="white" 
                              stroke-width="1"
                            />
                          </g>
                        </svg>
                      </div>
                    `,
                    iconSize: [48, 48],
                    iconAnchor: [24, 24],
                  });
                } else {
                  // 동그라미 마커 (기본)
                  markerIcon = L.divIcon({
                    className: "custom-circle-marker",
                    html: `
                      <div style="
                        width: 32px;
                        height: 32px;
                        display: flex;
                        align-items: center;
                        justify-content: center;
                        transform: translate(-50%, -50%);
                        filter: drop-shadow(0 2px 4px rgba(0,0,0,0.3));
                      ">
                        <svg width="32" height="32" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
                          <!-- 외부 원 -->
                          <circle 
                            cx="16" 
                            cy="16" 
                            r="14" 
                            fill="${marker.color || "#3b82f6"}" 
                            stroke="white" 
                            stroke-width="2"
                          />
                          <!-- 내부 점 -->
                          <circle 
                            cx="16" 
                            cy="16" 
                            r="6" 
                            fill="white"
                          />
                        </svg>
                      </div>
                    `,
                    iconSize: [32, 32],
                    iconAnchor: [16, 16],
                  });
                }
              }

              return (
                <Marker key={marker.id} position={[marker.lat, marker.lng]} icon={markerIcon}>
                  <Popup maxWidth={350}>
                    <div className="max-w-[350px] min-w-[250px]" dir="ltr">
                      {/* 데이터 소스명만 표시 */}
                      {marker.source && (
                        <div className="mb-2 border-b pb-2">
                          <div className="text-muted-foreground text-xs">📡 {marker.source}</div>
                        </div>
                      )}

                      {/* 상세 정보 */}
                      <div className="space-y-2">
                        {marker.description &&
                          (() => {
                            // 마커의 소스에 해당하는 데이터 소스 찾기
                            const sourceDataSource = dataSources?.find((ds) => ds.name === marker.source);
                            const popupFields = sourceDataSource?.popupFields;

                            // popupFields가 설정되어 있으면 설정된 필드만 표시
                            if (popupFields && popupFields.length > 0) {
                              try {
                                const parsed = JSON.parse(marker.description);
                                return (
                                  <div className="bg-muted rounded p-2">
                                    <div className="text-foreground mb-1 text-xs font-semibold">상세 정보</div>
                                    <div className="space-y-2">
                                      {popupFields.map((field, idx) => {
                                        const value = parsed[field.fieldName];
                                        if (value === undefined || value === null) return null;

                                        // 포맷팅 적용
                                        let formattedValue = value;
                                        if (field.format === "date" && value) {
                                          formattedValue = new Date(value).toLocaleDateString("ko-KR");
                                        } else if (field.format === "datetime" && value) {
                                          formattedValue = new Date(value).toLocaleString("ko-KR");
                                        } else if (field.format === "number" && typeof value === "number") {
                                          formattedValue = value.toLocaleString();
                                        } else if (
                                          field.format === "url" &&
                                          typeof value === "string" &&
                                          value.startsWith("http")
                                        ) {
                                          return (
                                            <div key={idx} className="text-xs">
                                              <span className="text-muted-foreground font-medium">{field.label}:</span>{" "}
                                              <a
                                                href={value}
                                                target="_blank"
                                                rel="noopener noreferrer"
                                                className="text-primary hover:underline"
                                              >
                                                링크 열기
                                              </a>
                                            </div>
                                          );
                                        }

                                        return (
                                          <div key={idx} className="text-xs">
                                            <span className="text-muted-foreground font-medium">{field.label}:</span>{" "}
                                            <span className="text-foreground">{String(formattedValue)}</span>
                                          </div>
                                        );
                                      })}
                                    </div>
                                  </div>
                                );
                              } catch (error) {
                                return (
                                  <div className="bg-muted rounded p-2">
                                    <div className="text-foreground mb-1 text-xs font-semibold">상세 정보</div>
                                    <div className="text-muted-foreground text-xs">{marker.description}</div>
                                  </div>
                                );
                              }
                            }

                            // popupFields가 없으면 전체 데이터 표시 (기본 동작)
                            try {
                              const parsed = JSON.parse(marker.description);
                              return (
                                <div className="bg-muted rounded p-2">
                                  <div className="text-foreground mb-1 text-xs font-semibold">상세 정보</div>
                                  <div className="space-y-2">
                                    {Object.entries(parsed).map(([key, value], idx) => {
                                      if (value === undefined || value === null) return null;

                                      // 좌표 필드는 제외 (하단에 별도 표시)
                                      if (["lat", "lng", "latitude", "longitude", "x", "y"].includes(key)) return null;

                                      return (
                                        <div key={idx} className="text-xs">
                                          <span className="text-muted-foreground font-medium">{key}:</span>{" "}
                                          <span className="text-foreground">{String(value)}</span>
                                        </div>
                                      );
                                    })}
                                  </div>
                                </div>
                              );
                            } catch (error) {
                              return (
                                <div className="bg-muted rounded p-2">
                                  <div className="text-foreground mb-1 text-xs font-semibold">상세 정보</div>
                                  <div className="text-muted-foreground text-xs">{marker.description}</div>
                                </div>
                              );
                            }
                          })()}

                        {/* 좌표 */}
                        <div className="text-muted-foreground border-t pt-2 text-[10px]">
                          {marker.lat.toFixed(6)}, {marker.lng.toFixed(6)}
                        </div>

                        {/* 이동경로 버튼 */}
                        {(() => {
                          try {
                            const parsed = JSON.parse(marker.description || "{}");
                            // 다양한 필드명 지원 (plate_no 우선)
                            const visibleUserId = parsed.plate_no || parsed.plateNo || parsed.car_number || parsed.carNumber ||
                                           parsed.user_id || parsed.userId || parsed.driver_id || parsed.driverId ||
                                           parsed.car_no || parsed.carNo || parsed.vehicle_no || parsed.vehicleNo ||
                                           parsed.id || parsed.code || marker.name;
                            if (visibleUserId) {
                              return (
                                <div className="mt-2 border-t pt-2">
                                  <button
                                    onClick={() => loadRoute(visibleUserId)}
                                    disabled={routeLoading}
                                    className="w-full rounded bg-blue-500 px-2 py-1 text-xs text-white hover:bg-blue-600 disabled:opacity-50"
                                  >
                                    {routeLoading && selectedUserId === visibleUserId ? "로딩 중..." : "🛣️ 이동경로 보기"}
                                  </button>
                                </div>
                              );
                            }
                            return null;
                          } catch {
                            return null;
                          }
                        })()}
                      </div>
                    </div>
                  </Popup>
                </Marker>
              );
            })}

            {/* 이동경로 Polyline */}
            {routePoints.length > 1 && (
              <Polyline
                positions={routePoints.map((p) => [p.lat, p.lng] as [number, number])}
                pathOptions={{
                  color: "#3b82f6",
                  weight: 4,
                  opacity: 0.8,
                  dashArray: "10, 5",
                }}
              />
            )}
          </MapContainer>
        )}
      </div>

      {/* 하단 정보 */}
      {(markers.length > 0 || polygons.length > 0) && (
        <div className="text-muted-foreground border-t p-2 text-xs">
          {markers.length > 0 && `마커 ${markers.length}개`}
          {markers.length > 0 && polygons.length > 0 && " · "}
          {polygons.length > 0 && `영역 ${polygons.length}개`}
        </div>
      )}
    </div>
  );
}
