"use client";

import React, { useState, useEffect, useCallback, useMemo } from "react";
import { DashboardElement, QueryResult } from "@/components/admin/dashboard/types";
import { ChartRenderer } from "@/components/admin/dashboard/charts/ChartRenderer";
import { DashboardProvider } from "@/contexts/DashboardContext";
import { RESOLUTIONS, Resolution } from "@/components/admin/dashboard/ResolutionSelector";
import { Button } from "@/components/ui/button";
import { Download } from "lucide-react";
import {
  DropdownMenu,
  DropdownMenuContent,
  DropdownMenuItem,
  DropdownMenuTrigger,
} from "@/components/ui/dropdown-menu";
import dynamic from "next/dynamic";

// 위젯 동적 import - 모든 위젯
// const MapSummaryWidget = dynamic(() => import("./widgets/MapSummaryWidget"), { ssr: false });
// const MapTestWidget = dynamic(() => import("./widgets/MapTestWidget"), { ssr: false });
const MapTestWidgetV2 = dynamic(() => import("./widgets/MapTestWidgetV2"), { ssr: false });
const ChartTestWidget = dynamic(() => import("./widgets/ChartTestWidget"), { ssr: false });
const ListTestWidget = dynamic(
  () => import("./widgets/ListTestWidget").then((mod) => ({ default: mod.ListTestWidget })),
  { ssr: false },
);
const CustomMetricTestWidget = dynamic(() => import("./widgets/CustomMetricTestWidget"), { ssr: false });
const RiskAlertTestWidget = dynamic(() => import("./widgets/RiskAlertTestWidget"), { ssr: false });
const StatusSummaryWidget = dynamic(() => import("./widgets/StatusSummaryWidget"), { ssr: false });
// const RiskAlertWidget = dynamic(() => import("./widgets/RiskAlertWidget"), { ssr: false });
const WeatherWidget = dynamic(() => import("./widgets/WeatherWidget"), { ssr: false });
const WeatherMapWidget = dynamic(() => import("./widgets/WeatherMapWidget"), { ssr: false });
const ExchangeWidget = dynamic(() => import("./widgets/ExchangeWidget"), { ssr: false });
const VehicleStatusWidget = dynamic(() => import("./widgets/VehicleStatusWidget"), { ssr: false });
const VehicleListWidget = dynamic(() => import("./widgets/VehicleListWidget"), { ssr: false });
const VehicleMapOnlyWidget = dynamic(() => import("./widgets/VehicleMapOnlyWidget"), { ssr: false });
const CargoListWidget = dynamic(() => import("./widgets/CargoListWidget"), { ssr: false });
const CustomerIssuesWidget = dynamic(() => import("./widgets/CustomerIssuesWidget"), { ssr: false });
const DeliveryStatusWidget = dynamic(() => import("./widgets/DeliveryStatusWidget"), { ssr: false });
const DeliveryStatusSummaryWidget = dynamic(() => import("./widgets/DeliveryStatusSummaryWidget"), { ssr: false });
const DeliveryTodayStatsWidget = dynamic(() => import("./widgets/DeliveryTodayStatsWidget"), { ssr: false });
const TaskWidget = dynamic(() => import("./widgets/TaskWidget"), { ssr: false });
const DocumentWidget = dynamic(() => import("./widgets/DocumentWidget"), { ssr: false });
const BookingAlertWidget = dynamic(() => import("./widgets/BookingAlertWidget"), { ssr: false });
const CalculatorWidget = dynamic(() => import("./widgets/CalculatorWidget"), { ssr: false });
const CalendarWidget = dynamic(
  () => import("@/components/admin/dashboard/widgets/CalendarWidget").then((mod) => ({ default: mod.CalendarWidget })),
  { ssr: false },
);
const ClockWidget = dynamic(
  () => import("@/components/admin/dashboard/widgets/ClockWidget").then((mod) => ({ default: mod.ClockWidget })),
  { ssr: false },
);
// const ListWidget = dynamic(
//   () => import("@/components/admin/dashboard/widgets/ListWidget").then((mod) => ({ default: mod.ListWidget })),
//   { ssr: false },
// );

const YardManagement3DWidget = dynamic(() => import("@/components/admin/dashboard/widgets/YardManagement3DWidget"), {
  ssr: false,
});

const WorkHistoryWidget = dynamic(() => import("./widgets/WorkHistoryWidget"), {
  ssr: false,
});

const CustomStatsWidget = dynamic(() => import("./widgets/CustomStatsWidget"), {
  ssr: false,
});

// const CustomMetricWidget = dynamic(() => import("./widgets/CustomMetricWidget"), {
//   ssr: false,
// });

/**
 * 위젯 렌더링 함수 - DashboardSidebar의 모든 subtype 처리
 * ViewerElement에서 사용하기 위해 컴포넌트 외부에 정의
 */
function renderWidget(element: DashboardElement) {
  switch (element.subtype) {
    // 차트는 ChartRenderer에서 처리됨 (이 함수 호출 안됨)

    // === 위젯 종류 ===
    case "exchange":
      return <ExchangeWidget element={element} />;
    case "weather":
      return <WeatherWidget element={element} />;
    case "weather-map":
      return <WeatherMapWidget element={element} />;
    case "calculator":
      return <CalculatorWidget element={element} />;
    case "clock":
      return <ClockWidget element={element} />;
    // case "map-summary":
    //   return <MapSummaryWidget element={element} />;
    // case "map-test":
    //   return <MapTestWidget element={element} />;
    case "map-summary-v2":
      return <MapTestWidgetV2 element={element} />;
    case "chart":
      return <ChartTestWidget element={element} />;
    case "list-v2":
      return <ListTestWidget element={element} />;
    case "custom-metric-v2":
      return <CustomMetricTestWidget element={element} />;
    case "risk-alert-v2":
      return <RiskAlertTestWidget element={element} />;
    // case "risk-alert":
    //   return <RiskAlertWidget element={element} />;
    case "calendar":
      return <CalendarWidget element={element} />;
    case "status-summary":
      return <StatusSummaryWidget element={element} />;
    // case "custom-metric":
    //   return <CustomMetricWidget element={element} />;

    // === 운영/작업 지원 ===
    case "todo":
    case "maintenance":
      return <TaskWidget element={element} />;
    case "booking-alert":
      return <BookingAlertWidget element={element} />;
    case "document":
      return <DocumentWidget element={element} />;
    // case "list":
    //   return <ListWidget element={element} />;

    case "yard-management-3d":
      // console.log("🏗️ 야드관리 위젯 렌더링:", {
      //   elementId: element.id,
      //   yardConfig: element.yardConfig,
      //   yardConfigType: typeof element.yardConfig,
      //   hasLayoutId: !!element.yardConfig?.layoutId,
      //   layoutId: element.yardConfig?.layoutId,
      //   layoutName: element.yardConfig?.layoutName,
      // });
      return <YardManagement3DWidget isEditMode={false} config={element.yardConfig} />;

    case "work-history":
      return <WorkHistoryWidget element={element} />;

    case "transport-stats":
      // console.log("📊 [DashboardViewer] CustomStatsWidget 렌더링:", {
      //   elementId: element.id,
      //   hasDataSource: !!element.dataSource,
      //   query: element.dataSource?.query?.substring(0, 50) + "...",
      //   dataSourceType: element.dataSource?.type,
      // });
      return <CustomStatsWidget element={element} />;

    // === 차량 관련 (추가 위젯) ===
    case "vehicle-status":
      return <VehicleStatusWidget element={element} />;
    case "vehicle-list":
      return <VehicleListWidget element={element} />;
    case "vehicle-map":
      return <VehicleMapOnlyWidget element={element} />;

    // === 배송 관련 (추가 위젯) ===
    case "delivery-status":
      return <DeliveryStatusWidget element={element} />;
    case "delivery-status-summary":
      return <DeliveryStatusSummaryWidget element={element} />;
    case "delivery-today-stats":
      return <DeliveryTodayStatsWidget element={element} />;
    case "cargo-list":
      return <CargoListWidget element={element} />;
    case "customer-issues":
      return <CustomerIssuesWidget element={element} />;

    // === 기본 fallback ===
    default:
      return (
        <div className="flex h-full w-full items-center justify-center bg-muted/30 p-4">
          <div className="text-center">
            <div className="mb-2 text-3xl">❓</div>
            <div className="text-sm text-muted-foreground">알 수 없는 위젯 타입: {element.subtype}</div>
          </div>
        </div>
      );
  }
}

interface DashboardViewerProps {
  elements: DashboardElement[];
  dashboardId?: string;
  refreshInterval?: number; // 전체 대시보드 새로고침 간격 (ms)
  backgroundColor?: string; // 배경색
  resolution?: string; // 대시보드 해상도
  dashboardTitle?: string; // 대시보드 제목 (다운로드 파일명용)
}

/**
 * 대시보드 뷰어 컴포넌트
 * - 저장된 대시보드를 읽기 전용으로 표시
 * - 실시간 데이터 업데이트
 * - 편집 화면과 동일한 레이아웃 (중앙 정렬, 고정 크기)
 */
export function DashboardViewer({
  elements,
  refreshInterval,
  backgroundColor = "#f9fafb",
  resolution = "fhd",
  dashboardTitle,
}: DashboardViewerProps) {
  const [elementData, setElementData] = useState<Record<string, QueryResult>>({});
  const [loadingElements, setLoadingElements] = useState<Set<string>>(new Set());

  // 대시보드 다운로드
  // 헬퍼 함수: dataUrl로 다운로드 처리
  const handleDownloadWithDataUrl = async (
    dataUrl: string,
    format: "png" | "pdf",
    canvasWidth: number,
    canvasHeight: number,
  ) => {
    if (format === "png") {
      console.log("💾 PNG 다운로드 시작...");
      const link = document.createElement("a");
      const _dvd = new Date();
      const filename = `${dashboardTitle || "dashboard"}_${_dvd.getFullYear()}-${String(_dvd.getMonth() + 1).padStart(2, "0")}-${String(_dvd.getDate()).padStart(2, "0")}.png`;
      link.download = filename;
      link.href = dataUrl;
      document.body.appendChild(link);
      link.click();
      document.body.removeChild(link);
      console.log("✅ PNG 다운로드 완료:", filename);
    } else {
      console.log("📄 PDF 생성 중...");
      const jsPDF = (await import("jspdf")).default;

      // dataUrl에서 이미지 크기 계산
      const img = new Image();
      img.src = dataUrl;
      await new Promise((resolve) => {
        img.onload = resolve;
      });

      console.log("📐 이미지 실제 크기:", { width: img.width, height: img.height });
      console.log("📐 캔버스 계산 크기:", { width: canvasWidth, height: canvasHeight });

      // PDF 크기 계산 (A4 기준)
      const imgWidth = 210; // A4 width in mm
      const actualHeight = canvasHeight;
      const actualWidth = canvasWidth;
      const imgHeight = (actualHeight * imgWidth) / actualWidth;

      console.log("📄 PDF 크기:", { width: imgWidth, height: imgHeight });

      const pdf = new jsPDF({
        orientation: imgHeight > imgWidth ? "portrait" : "landscape",
        unit: "mm",
        format: [imgWidth, imgHeight],
      });

      pdf.addImage(dataUrl, "PNG", 0, 0, imgWidth, imgHeight);
      const _dvp = new Date();
      const filename = `${dashboardTitle || "dashboard"}_${_dvp.getFullYear()}-${String(_dvp.getMonth() + 1).padStart(2, "0")}-${String(_dvp.getDate()).padStart(2, "0")}.pdf`;
      pdf.save(filename);
      console.log("✅ PDF 다운로드 완료:", filename);
    }
  };

  const handleDownload = useCallback(
    async (format: "png" | "pdf") => {
      try {
        console.log("🔍 다운로드 시작:", format);

        const canvas = document.querySelector(".dashboard-viewer-canvas") as HTMLElement;
        console.log("🔍 캔버스 찾기:", canvas);

        if (!canvas) {
          alert("대시보드를 찾을 수 없습니다. 페이지를 새로고침 후 다시 시도해주세요.");
          return;
        }

        console.log("📸 html-to-image 로딩 중...");
        // html-to-image 동적 import
        const { toPng } = await import("html-to-image");

        console.log("📸 캔버스 캡처 중...");

        // 3D/WebGL 렌더링 완료 대기
        console.log("⏳ 3D 렌더링 완료 대기 중...");
        await new Promise((resolve) => setTimeout(resolve, 1000));

        // WebGL 캔버스를 이미지로 변환 (Three.js 캔버스 보존)
        console.log("🎨 WebGL 캔버스 처리 중...");
        const webglCanvases = canvas.querySelectorAll("canvas");
        const webglImages: { canvas: HTMLCanvasElement; dataUrl: string; rect: DOMRect }[] = [];

        webglCanvases.forEach((webglCanvas) => {
          try {
            const rect = webglCanvas.getBoundingClientRect();
            const dataUrl = webglCanvas.toDataURL("image/png");
            webglImages.push({ canvas: webglCanvas, dataUrl, rect });
            console.log("✅ WebGL 캔버스 캡처:", {
              width: rect.width,
              height: rect.height,
              left: rect.left,
              top: rect.top,
              bottom: rect.bottom,
            });
          } catch (error) {
            console.warn("⚠️ WebGL 캔버스 캡처 실패:", error);
          }
        });

        // 캔버스의 실제 크기와 위치 가져오기
        const rect = canvas.getBoundingClientRect();
        const canvasWidth = canvas.scrollWidth;

        // 실제 콘텐츠의 최하단 위치 계산
        // 뷰어 모드에서는 모든 자식 요소를 확인
        const children = canvas.querySelectorAll("*");
        let maxBottom = 0;
        children.forEach((child) => {
          // canvas 자신이나 너무 작은 요소는 제외
          if (child === canvas || child.clientHeight < 10) {
            return;
          }
          const childRect = child.getBoundingClientRect();
          const relativeBottom = childRect.bottom - rect.top;
          if (relativeBottom > maxBottom) {
            maxBottom = relativeBottom;
          }
        });

        // 실제 콘텐츠 높이 + 여유 공간 (50px)
        // maxBottom이 0이면 기본 캔버스 높이 사용
        const canvasHeight = maxBottom > 50 ? maxBottom + 50 : Math.max(canvas.scrollHeight, rect.height);

        console.log("📐 캔버스 정보:", {
          rect: { x: rect.x, y: rect.y, left: rect.left, top: rect.top, width: rect.width, height: rect.height },
          scroll: { width: canvasWidth, height: canvas.scrollHeight },
          calculated: { width: canvasWidth, height: canvasHeight },
          maxBottom: maxBottom,
          webglCount: webglImages.length,
        });

        // html-to-image로 캔버스 캡처 (WebGL 제외)
        const dataUrl = await toPng(canvas, {
          backgroundColor: backgroundColor || "#ffffff",
          width: canvasWidth,
          height: canvasHeight,
          pixelRatio: 2, // 고해상도
          cacheBust: true,
          skipFonts: false,
          preferredFontFormat: "woff2",
          filter: (node: Node) => {
            // WebGL 캔버스는 제외 (나중에 수동으로 합성)
            if (node instanceof HTMLCanvasElement) {
              return false;
            }
            return true;
          },
        });

        // WebGL 캔버스를 이미지 위에 합성
        if (webglImages.length > 0) {
          console.log("🖼️ WebGL 이미지 합성 중...");
          const img = new Image();
          img.src = dataUrl;
          await new Promise((resolve) => {
            img.onload = resolve;
          });

          // 새 캔버스에 합성
          const compositeCanvas = document.createElement("canvas");
          compositeCanvas.width = img.width;
          compositeCanvas.height = img.height;
          const ctx = compositeCanvas.getContext("2d");

          if (ctx) {
            // 기본 이미지 그리기
            ctx.drawImage(img, 0, 0);

            // WebGL 이미지들을 위치에 맞게 그리기
            for (const { dataUrl: webglDataUrl, rect: webglRect } of webglImages) {
              const webglImg = new Image();
              webglImg.src = webglDataUrl;
              await new Promise((resolve) => {
                webglImg.onload = resolve;
              });

              // 상대 위치 계산 (pixelRatio 2 고려)
              const relativeX = (webglRect.left - rect.left) * 2;
              const relativeY = (webglRect.top - rect.top) * 2;
              const width = webglRect.width * 2;
              const height = webglRect.height * 2;

              ctx.drawImage(webglImg, relativeX, relativeY, width, height);
              console.log("✅ WebGL 이미지 합성 완료:", { x: relativeX, y: relativeY, width, height });
            }

            // 합성된 이미지를 dataUrl로 변환
            const compositeDataUrl = compositeCanvas.toDataURL("image/png");
            console.log("✅ 최종 합성 완료");

            // 합성된 이미지로 다운로드
            return await handleDownloadWithDataUrl(compositeDataUrl, format, canvasWidth, canvasHeight);
          }
        }

        console.log("✅ 캡처 완료 (WebGL 없음)");

        // WebGL이 없는 경우 기본 다운로드
        await handleDownloadWithDataUrl(dataUrl, format, canvasWidth, canvasHeight);
      } catch (error) {
        console.error("❌ 다운로드 실패:", error);
        alert(`다운로드에 실패했습니다.\n\n에러: ${error instanceof Error ? error.message : String(error)}`);
      }
    },
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [backgroundColor, dashboardTitle, handleDownloadWithDataUrl],
  );

  // 캔버스 설정 계산
  const canvasConfig = useMemo(() => {
    return RESOLUTIONS[resolution as Resolution] || RESOLUTIONS.fhd;
  }, [resolution]);

  // 캔버스 높이 동적 계산
  const canvasHeight = useMemo(() => {
    if (elements.length === 0) {
      return canvasConfig.height;
    }
    const maxBottomY = Math.max(...elements.map((el) => el.position.y + el.size.height));
    return Math.max(canvasConfig.height, maxBottomY + 100);
  }, [elements, canvasConfig.height]);

  // 개별 요소 데이터 로딩
  const loadElementData = useCallback(async (element: DashboardElement) => {
    if (!element.dataSource?.query || element.type !== "chart") {
      return;
    }

    setLoadingElements((prev) => new Set([...prev, element.id]));

    try {
      let result;

      // 외부 DB vs 현재 DB 분기
      if (element.dataSource.connectionType === "external" && element.dataSource.externalConnectionId) {
        // 외부 DB
        const { ExternalDbConnectionAPI } = await import("@/lib/api/externalDbConnection");
        const externalResult = await ExternalDbConnectionAPI.executeQuery(
          parseInt(element.dataSource.externalConnectionId),
          element.dataSource.query,
        );

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

        const data: QueryResult = {
          columns: externalResult.data?.[0] ? Object.keys(externalResult.data[0]) : [],
          rows: externalResult.data || [],
          totalRows: externalResult.data?.length || 0,
          executionTime: 0,
        };

        setElementData((prev) => ({
          ...prev,
          [element.id]: data,
        }));
      } else {
        // 현재 DB
        const { dashboardApi } = await import("@/lib/api/dashboard");
        result = await dashboardApi.executeQuery(element.dataSource.query);

        const data: QueryResult = {
          columns: result.columns || [],
          rows: result.rows || [],
          totalRows: result.rowCount || 0,
          executionTime: 0,
        };

        setElementData((prev) => ({
          ...prev,
          [element.id]: data,
        }));
      }
    } catch {
      // 에러 발생 시 무시 (차트는 빈 상태로 표시됨)
    } finally {
      setLoadingElements((prev) => {
        const newSet = new Set(prev);
        newSet.delete(element.id);
        return newSet;
      });
    }
  }, []);

  // 모든 요소 데이터 로딩
  const loadAllData = useCallback(async () => {
    const chartElements = elements.filter((el) => el.type === "chart" && el.dataSource?.query);

    // 병렬로 모든 차트 데이터 로딩
    await Promise.all(chartElements.map((element) => loadElementData(element)));
  }, [elements, loadElementData]);

  // 초기 데이터 로딩
  useEffect(() => {
    loadAllData();
  }, [loadAllData]);

  // 전체 새로고침 간격 설정
  useEffect(() => {
    if (!refreshInterval || refreshInterval === 0) {
      return;
    }

    const interval = setInterval(loadAllData, refreshInterval);
    return () => clearInterval(interval);
  }, [refreshInterval, loadAllData]);

  // 요소를 자연스러운 읽기 순서로 정렬 (왼쪽→오른쪽, 위→아래) - 태블릿 이하에서 세로 정렬 시 사용
  const sortedElements = useMemo(() => {
    return [...elements].sort((a, b) => {
      // Y 좌표 차이가 50px 이상이면 Y 우선 (같은 행으로 간주 안함)
      const yDiff = a.position.y - b.position.y;
      if (Math.abs(yDiff) > 50) {
        return yDiff;
      }
      // 같은 행이면 X 좌표로 정렬
      return a.position.x - b.position.x;
    });
  }, [elements]);

  // 요소가 없는 경우
  if (elements.length === 0) {
    return (
      <div className="bg-muted flex h-full items-center justify-center">
        <div className="text-center">
          <div className="mb-4 text-6xl">📊</div>
          <div className="text-foreground mb-2 text-xl font-medium">표시할 요소가 없습니다</div>
          <div className="text-muted-foreground text-sm">대시보드 편집기에서 차트나 위젯을 추가해보세요</div>
        </div>
      </div>
    );
  }

  return (
    <DashboardProvider>
      {/* 데스크톱: 디자이너에서 설정한 위치 그대로 렌더링 (화면에 맞춰 비율 유지) */}
      <div className="bg-muted hidden min-h-screen py-8 lg:block" style={{ backgroundColor }}>
        <div className="mx-auto px-4" style={{ width: "100%", maxWidth: "none" }}>
          {/* 다운로드 버튼 - 비활성화 */}
          {/* <div className="mb-4 flex justify-end">
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <Button variant="outline" size="sm" className="gap-2">
                  <Download className="h-4 w-4" />
                  다운로드
                </Button>
              </DropdownMenuTrigger>
              <DropdownMenuContent align="end">
                <DropdownMenuItem onClick={() => handleDownload("png")}>PNG 이미지로 저장</DropdownMenuItem>
                <DropdownMenuItem onClick={() => handleDownload("pdf")}>PDF 문서로 저장</DropdownMenuItem>
              </DropdownMenuContent>
            </DropdownMenu>
          </div> */}

          <div
            className="dashboard-viewer-canvas relative rounded-lg"
            style={{
              width: "100%",
              minHeight: `${canvasConfig.height}px`,
              height: `${canvasHeight}px`,
              backgroundColor: backgroundColor,
            }}
          >
            {sortedElements.map((element) => (
              <ViewerElement
                key={element.id}
                element={element}
                data={elementData[element.id]}
                isLoading={loadingElements.has(element.id)}
                onRefresh={() => loadElementData(element)}
                isMobile={false}
                canvasWidth={canvasConfig.width}
              />
            ))}
          </div>
        </div>
      </div>

      {/* 태블릿 이하: 반응형 세로 정렬 */}
      <div className="bg-muted block min-h-screen p-4 lg:hidden" style={{ backgroundColor }}>
        <div className="mx-auto max-w-3xl space-y-4">
          {/* 다운로드 버튼 - 비활성화 */}
          {/* <div className="flex justify-end">
            <DropdownMenu>
              <DropdownMenuTrigger asChild>
                <Button variant="outline" size="sm" className="gap-2">
                  <Download className="h-4 w-4" />
                  다운로드
                </Button>
              </DropdownMenuTrigger>
              <DropdownMenuContent align="end">
                <DropdownMenuItem onClick={() => handleDownload("png")}>PNG 이미지로 저장</DropdownMenuItem>
                <DropdownMenuItem onClick={() => handleDownload("pdf")}>PDF 문서로 저장</DropdownMenuItem>
              </DropdownMenuContent>
            </DropdownMenu>
          </div> */}

          <div className="dashboard-viewer-canvas">
            {sortedElements.map((element) => (
              <ViewerElement
                key={element.id}
                element={element}
                data={elementData[element.id]}
                isLoading={loadingElements.has(element.id)}
                onRefresh={() => loadElementData(element)}
                isMobile={true}
              />
            ))}
          </div>
        </div>
      </div>
    </DashboardProvider>
  );
}

interface ViewerElementProps {
  element: DashboardElement;
  data?: QueryResult;
  isLoading: boolean;
  onRefresh: () => void;
  isMobile: boolean;
  canvasWidth?: number;
}

/**
 * 개별 뷰어 요소 컴포넌트
 * - 데스크톱(lg 이상): absolute positioning으로 디자이너에서 설정한 위치 그대로 렌더링 (너비는 화면 비율에 따라 조정)
 * - 태블릿 이하: 세로 스택 카드 레이아웃
 */
function ViewerElement({ element, data, isLoading, onRefresh, isMobile, canvasWidth = 1920 }: ViewerElementProps) {
  const [isMounted, setIsMounted] = useState(false);

  // 마운트 확인 (Leaflet 지도 초기화 문제 해결)
  useEffect(() => {
    setIsMounted(true);
  }, []);

  if (isMobile) {
    // 태블릿 이하: 세로 스택 카드 스타일
    return (
      <div
        className="border-border bg-background relative overflow-hidden rounded-lg border shadow-sm"
        style={{ minHeight: "300px" }}
      >
        {element.showHeader !== false && (
          <div className="flex items-center justify-between px-2 py-1">
            {/* map-summary-v2는 customTitle이 없으면 제목 숨김 */}
            {element.subtype === "map-summary-v2" && !element.customTitle ? null : (
              <h3 className="text-foreground text-xs font-semibold">{element.customTitle || element.title}</h3>
            )}
          </div>
        )}
        <div className={element.showHeader !== false ? "p-2" : "p-2"} style={{ minHeight: "250px" }}>
          {!isMounted ? (
            <div className="flex h-full w-full items-center justify-center">
              <div className="border-primary h-6 w-6 animate-spin rounded-full border-2 border-t-transparent" />
            </div>
          ) : element.type === "chart" ? (
            <ChartRenderer element={element} data={data} width={undefined} height={250} />
          ) : (
            renderWidget(element)
          )}
        </div>
        {isLoading && (
          <div className="bg-opacity-75 bg-background absolute inset-0 flex items-center justify-center">
            <div className="text-center">
              <div className="border-primary mx-auto mb-2 h-6 w-6 animate-spin rounded-full border-2 border-t-transparent" />
              <div className="text-foreground text-sm">업데이트 중...</div>
            </div>
          </div>
        )}
      </div>
    );
  }

  // 데스크톱: 디자이너에서 설정한 위치 그대로 absolute positioning
  // 단, 너비는 화면 크기에 따라 비율로 조정
  const widthPercentage = (element.size.width / canvasWidth) * 100;
  const leftPercentage = (element.position.x / canvasWidth) * 100;

  return (
    <div
      className="border-border bg-background absolute overflow-hidden rounded-lg border shadow-sm"
      style={{
        left: `${leftPercentage}%`,
        top: element.position.y,
        width: `${widthPercentage}%`,
        height: element.size.height,
      }}
    >
      {element.showHeader !== false && (
        <div className="flex items-center justify-between px-2 py-1">
          {/* map-summary-v2는 customTitle이 없으면 제목 숨김 */}
          {element.subtype === "map-summary-v2" && !element.customTitle ? null : (
            <h3 className="text-foreground text-xs font-semibold">{element.customTitle || element.title}</h3>
          )}
        </div>
      )}
      <div className={element.showHeader !== false ? "h-[calc(100%-32px)] w-full" : "h-full w-full"}>
        {!isMounted ? (
          <div className="flex h-full w-full items-center justify-center">
            <div className="border-primary h-6 w-6 animate-spin rounded-full border-2 border-t-transparent" />
          </div>
        ) : element.type === "chart" ? (
          <ChartRenderer
            element={element}
            data={data}
            width={undefined}
            height={element.showHeader !== false ? element.size.height - 32 : element.size.height}
          />
        ) : (
          renderWidget(element)
        )}
      </div>
      {isLoading && (
        <div className="bg-opacity-75 bg-background absolute inset-0 flex items-center justify-center">
          <div className="text-center">
            <div className="border-primary mx-auto mb-2 h-6 w-6 animate-spin rounded-full border-2 border-t-transparent" />
            <div className="text-foreground text-sm">업데이트 중...</div>
          </div>
        </div>
      )}
    </div>
  );
}
