"use client";

import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from "@/components/ui/dialog";
import { Button } from "@/components/ui/button";
import { FileDown, FileText, Loader2 } from "lucide-react";
import { ComponentConfig, ReportDetail, ReportMaster, ReportPage, ReportQuery, WatermarkConfig } from "@/types/report";
import { reportApi } from "@/lib/api/reportApi";
import { useCallback, useEffect, useMemo, useRef, useState } from "react";
import { useToast } from "@/hooks/use-toast";
import { getFullImageUrl } from "@/lib/api/client";
import {
  TextRenderer,
  TableRenderer,
  ImageRenderer,
  DividerRenderer,
  SignatureRenderer,
  StampRenderer,
  PageNumberRenderer,
  CardRenderer,
  CalculationRenderer,
  BarcodeCanvasRenderer,
  CheckboxRenderer,
} from "./designer/renderers";
import { MM_TO_PX } from "@/lib/report/constants";

interface QueryResult {
  queryId: string;
  fields: string[];
  rows: Record<string, unknown>[];
}

interface ReportListPreviewModalProps {
  report: ReportMaster | null;
  onClose: () => void;
  /** 컨텍스트에서 자동 주입할 쿼리 파라미터 (formData 기반) */
  contextParams?: Record<string, unknown>;
}

export function ReportListPreviewModal({ report, onClose, contextParams }: ReportListPreviewModalProps) {
  const [detail, setDetail] = useState<ReportDetail | null>(null);
  const [queryResults, setQueryResults] = useState<QueryResult[]>([]);
  const [isLoading, setIsLoading] = useState(false);
  const [isExporting, setIsExporting] = useState(false);
  const previewRef = useRef<HTMLDivElement>(null);
  const { toast } = useToast();

  const getQueryResult = useCallback(
    (queryId: string): QueryResult | null => {
      return queryResults.find((r) => r.queryId === queryId) || null;
    },
    [queryResults],
  );

  useEffect(() => {
    if (!report) {
      setDetail(null);
      setQueryResults([]);
      return;
    }

    let cancelled = false;
    setIsLoading(true);

    (async () => {
      try {
        const res = await reportApi.getReportById(report.report_id);
        if (cancelled || !res.success) return;

        setDetail(res.data);

        // 쿼리 자동 실행
        const queries: ReportQuery[] = res.data.queries ?? [];
        if (queries.length === 0) return;

        // contextParams를 $1, $2 ... 형식으로 매핑 (휴리스틱)
        const contextEntries = Object.values(contextParams ?? {});
        const buildParams = (parameters: string[]): Record<string, unknown> => {
          const result: Record<string, unknown> = {};
          parameters.forEach((param, idx) => {
            result[param] = contextEntries[idx] ?? contextParams?.[param] ?? null;
          });
          return result;
        };

        const results: QueryResult[] = [];
        for (const q of queries) {
          try {
            const params = buildParams(q.parameters ?? []);
            const execRes = await reportApi.executeQuery(
              report.report_id,
              q.query_id,
              params,
              q.sql_query,
              q.external_connection_id,
            );
            if (execRes.success && execRes.data) {
              results.push({
                queryId: q.query_id,
                fields: execRes.data.fields,
                rows: execRes.data.rows,
              });
            }
          } catch {
            // 개별 쿼리 실패는 무시
          }
        }

        if (!cancelled) setQueryResults(results);
      } catch {
        if (!cancelled) {
          toast({ title: "오류", description: "리포트를 불러올 수 없습니다.", variant: "destructive" });
        }
      } finally {
        if (!cancelled) setIsLoading(false);
      }
    })();

    return () => {
      cancelled = true;
    };
  }, [report?.report_id, contextParams]);

  const { pages, watermark } = useMemo(() => {
    const empty = { pages: [] as ReportPage[], watermark: undefined as WatermarkConfig | undefined };
    if (!detail?.layout) return empty;

    const layout = detail.layout as unknown as Record<string, unknown>;

    let config: Record<string, unknown> | null = null;

    let raw: unknown = layout.components;

    while (typeof raw === "string") {
      try {
        raw = JSON.parse(raw);
      } catch {
        break;
      }
    }

    if (raw && typeof raw === "object" && !Array.isArray(raw)) {
      config = raw as Record<string, unknown>;
    }

    if (!config && Array.isArray(layout.pages)) {
      config = layout;
    }

    if (!config) return empty;

    const foundPages = Array.isArray(config.pages) ? (config.pages as ReportPage[]) : [];
    const foundWatermark = config.watermark as WatermarkConfig | undefined;

    return { pages: foundPages, watermark: foundWatermark };
  }, [detail?.layout]);

  const handleDownloadPDF = async () => {
    if (!previewRef.current || pages.length === 0) return;
    setIsExporting(true);
    try {
      const [{ jsPDF }, html2canvas] = await Promise.all([
        import("jspdf"),
        import("html2canvas").then((m) => m.default),
      ]);

      const pageEls = previewRef.current.querySelectorAll<HTMLElement>("[data-list-preview-page]");
      if (pageEls.length === 0) return;

      const firstPage = pages[0];
      const doc = new jsPDF({
        orientation: firstPage.orientation === "landscape" ? "l" : "p",
        unit: "mm",
        format: [firstPage.width, firstPage.height],
      });

      for (let i = 0; i < pageEls.length; i++) {
        const canvas = await html2canvas(pageEls[i], {
          scale: 2,
          useCORS: true,
          allowTaint: true,
          backgroundColor: "#ffffff",
        });

        if (i > 0) {
          const p = pages[i] ?? firstPage;
          doc.addPage([p.width, p.height], p.orientation === "landscape" ? "l" : "p");
        }

        const p = pages[i] ?? firstPage;
        doc.addImage(canvas.toDataURL("image/jpeg", 0.92), "JPEG", 0, 0, p.width, p.height);
      }

      doc.save(`${report?.report_name_kor ?? "report"}.pdf`);
    } catch {
      toast({ title: "오류", description: "PDF 다운로드에 실패했습니다.", variant: "destructive" });
    } finally {
      setIsExporting(false);
    }
  };

  const containerRef = useRef<HTMLDivElement>(null);
  const [scale, setScale] = useState(1);

  useEffect(() => {
    if (!containerRef.current || pages.length === 0) return;

    const calculateScale = () => {
      const container = containerRef.current;
      if (!container) return;

      const firstPage = pages[0];
      const pageWidthPx = firstPage.width * MM_TO_PX;
      const pageHeightPx = firstPage.height * MM_TO_PX;

      const availableWidth = container.clientWidth - 48;
      const availableHeight = container.clientHeight - 48;

      const scaleX = availableWidth / pageWidthPx;
      const scaleY = availableHeight / pageHeightPx;
      setScale(Math.min(scaleX, scaleY, 1));
    };

    const observer = new ResizeObserver(calculateScale);
    observer.observe(containerRef.current);
    calculateScale();

    return () => observer.disconnect();
  }, [pages]);

  return (
    <Dialog open={!!report} onOpenChange={(open) => !open && onClose()}>
      <DialogContent
        className="flex flex-col gap-0 p-0"
        style={{ height: "85vh", width: "calc(85vh / 1.414)", maxWidth: "95vw" }}
      >
        <DialogHeader className="shrink-0 border-b px-7 py-5">
          <DialogTitle className="flex items-center gap-2.5 text-lg">
            <FileText className="h-6 w-6 text-blue-600" />
            {report?.report_name_kor ?? "리포트 미리보기"}
          </DialogTitle>
        </DialogHeader>

        <div ref={containerRef} className="min-h-0 flex-1 overflow-auto bg-gray-100">
          {isLoading ? (
            <div className="flex h-72 items-center justify-center">
              <Loader2 className="h-10 w-10 animate-spin text-gray-400" />
            </div>
          ) : pages.length === 0 ? (
            <div className="flex h-72 flex-col items-center justify-center gap-3 text-gray-400">
              <FileText className="h-14 w-14 opacity-30" />
              <p className="text-base">{detail ? "저장된 레이아웃이 없습니다." : "데이터를 불러오는 중..."}</p>
            </div>
          ) : (
            <div ref={previewRef} className="flex flex-col items-center p-6" style={{ gap: `${24 * scale}px` }}>
              {[...pages]
                .sort((a, b) => a.page_order - b.page_order)
                .map((page, pageIndex) => (
                  <div
                    key={page.page_id}
                    style={{
                      width: `${Math.ceil(page.width * MM_TO_PX * scale) + 1}px`,
                      height: `${Math.ceil(page.height * MM_TO_PX * scale) + 1}px`,
                      flexShrink: 0,
                      overflow: "hidden",
                    }}
                  >
                    <div
                      style={{
                        transform: `scale(${scale})`,
                        transformOrigin: "top left",
                        width: `${page.width * MM_TO_PX}px`,
                        height: `${page.height * MM_TO_PX}px`,
                      }}
                    >
                      <PagePreview
                        page={page}
                        pageIndex={pageIndex}
                        totalPages={pages.length}
                        pages={pages}
                        watermark={watermark}
                        getQueryResult={getQueryResult}
                      />
                    </div>
                  </div>
                ))}
            </div>
          )}
        </div>

        <DialogFooter className="shrink-0 border-t bg-white px-7 py-5">
          <Button variant="outline" onClick={onClose} className="text-base">
            닫기
          </Button>
          <Button
            onClick={handleDownloadPDF}
            disabled={isExporting || isLoading || pages.length === 0}
            className="gap-2 bg-blue-600 text-base text-white hover:bg-blue-700"
          >
            {isExporting ? <Loader2 className="h-5 w-5 animate-spin" /> : <FileDown className="h-5 w-5" />}
            PDF 다운로드
          </Button>
        </DialogFooter>
      </DialogContent>
    </Dialog>
  );
}

function WatermarkLayer({
  watermark,
  pageWidth,
  pageHeight,
}: {
  watermark: WatermarkConfig;
  pageWidth: number;
  pageHeight: number;
}) {
  const baseStyle: React.CSSProperties = {
    position: "absolute",
    top: 0,
    left: 0,
    width: "100%",
    height: "100%",
    pointerEvents: "none",
    overflow: "hidden",
    zIndex: 0,
  };

  const rotation = watermark.rotation ?? -45;

  const textOrImage =
    watermark.type === "text" ? (
      <span
        style={{
          fontSize: `${watermark.fontSize || 48}px`,
          color: watermark.fontColor || "#cccccc",
          fontWeight: "bold",
          userSelect: "none",
          whiteSpace: "nowrap",
        }}
      >
        {watermark.text || "WATERMARK"}
      </span>
    ) : watermark.imageUrl ? (
      // eslint-disable-next-line @next/next/no-img-element
      <img
        src={watermark.imageUrl.startsWith("data:") ? watermark.imageUrl : getFullImageUrl(watermark.imageUrl)}
        alt=""
        style={{ maxWidth: "50%", maxHeight: "50%", objectFit: "contain" }}
      />
    ) : null;

  if (watermark.style === "diagonal") {
    return (
      <div style={baseStyle}>
        <div
          style={{
            position: "absolute",
            top: "50%",
            left: "50%",
            transform: `translate(-50%, -50%) rotate(${rotation}deg)`,
            opacity: watermark.opacity,
            display: "flex",
            alignItems: "center",
            justifyContent: "center",
          }}
        >
          {textOrImage}
        </div>
      </div>
    );
  }

  if (watermark.style === "center") {
    return (
      <div style={baseStyle}>
        <div
          style={{
            position: "absolute",
            top: "50%",
            left: "50%",
            transform: "translate(-50%, -50%)",
            opacity: watermark.opacity,
            display: "flex",
            alignItems: "center",
            justifyContent: "center",
          }}
        >
          {textOrImage}
        </div>
      </div>
    );
  }

  if (watermark.style === "tile") {
    const tileSize = watermark.type === "text" ? (watermark.fontSize || 48) * 4 : 150;
    const cols = Math.ceil((pageWidth * MM_TO_PX) / tileSize) + 2;
    const rows = Math.ceil((pageHeight * MM_TO_PX) / tileSize) + 2;

    return (
      <div style={baseStyle}>
        <div
          style={{
            position: "absolute",
            top: "-50%",
            left: "-50%",
            width: "200%",
            height: "200%",
            display: "flex",
            flexWrap: "wrap",
            alignContent: "flex-start",
            transform: `rotate(${rotation}deg)`,
            opacity: watermark.opacity,
          }}
        >
          {Array.from({ length: rows * cols }).map((_, i) => (
            <div
              key={i}
              style={{
                width: `${tileSize}px`,
                height: `${tileSize}px`,
                display: "flex",
                alignItems: "center",
                justifyContent: "center",
              }}
            >
              {textOrImage}
            </div>
          ))}
        </div>
      </div>
    );
  }

  return null;
}

function PagePreview({
  page,
  pageIndex,
  totalPages,
  pages,
  watermark,
  getQueryResult,
}: {
  page: ReportPage;
  pageIndex: number;
  totalPages: number;
  pages: ReportPage[];
  watermark?: WatermarkConfig;
  getQueryResult: (queryId: string) => QueryResult | null;
}) {
  return (
    <div
      data-list-preview-page={page.page_id}
      className="relative shadow-md"
      style={{
        width: `${page.width * MM_TO_PX}px`,
        height: `${page.height * MM_TO_PX}px`,
        backgroundColor: page.background_color || "#ffffff",
        flexShrink: 0,
        overflow: "visible",
      }}
    >
      {watermark?.enabled && <WatermarkLayer watermark={watermark} pageWidth={page.width} pageHeight={page.height} />}

      {(page.components ?? [])
        .sort((a, b) => (a.zIndex ?? 0) - (b.zIndex ?? 0))
        .map((comp) => (
          <ComponentRenderer
            key={comp.id}
            comp={comp}
            pageIndex={pageIndex}
            totalPages={totalPages}
            pages={pages}
            getQueryResult={getQueryResult}
          />
        ))}
    </div>
  );
}

function BarcodePreviewRenderer({
  component,
  getQueryResult,
}: {
  component: ComponentConfig;
  getQueryResult: (queryId: string) => QueryResult | null;
}) {
  return <BarcodeCanvasRenderer component={component} getQueryResult={getQueryResult} />;
}

function ComponentRenderer({
  comp,
  pageIndex,
  totalPages,
  pages,
  getQueryResult,
}: {
  comp: ComponentConfig;
  pageIndex: number;
  totalPages: number;
  pages: ReportPage[];
  getQueryResult: (queryId: string) => QueryResult | null;
}) {
  const isDivider = comp.type === "divider";

  const baseStyle: React.CSSProperties = {
    position: "absolute",
    left: `${comp.x}px`,
    top: `${comp.y}px`,
    width: `${comp.width}px`,
    height: `${comp.height}px`,
    boxSizing: "border-box",
    overflow: "hidden",
    zIndex: comp.zIndex ?? 0,
    backgroundColor: comp.backgroundColor || "transparent",
    ...(comp.borderWidth
      ? { borderWidth: `${comp.borderWidth}px`, borderColor: comp.borderColor || "#000", borderStyle: "solid" }
      : {}),
    ...(comp.borderRadius ? { borderRadius: `${comp.borderRadius}px` } : {}),
    padding: isDivider
      ? 0
      : comp.padding != null
        ? typeof comp.padding === "number"
          ? `${comp.padding}px`
          : comp.padding
        : "8px",
  };

  const getComponentValue = (c: ComponentConfig): string => {
    if (c.queryId && c.fieldName) {
      const qr = getQueryResult(c.queryId);
      if (qr?.rows?.length) {
        const val = qr.rows[0][c.fieldName];
        if (val != null) return String(val);
      }
      return `{${c.fieldName}}`;
    }
    return c.defaultValue || "";
  };

  const displayValue = getComponentValue(comp);

  const sortedPages = [...pages].sort((a, b) => a.page_order - b.page_order);
  const currentPageId = sortedPages[pageIndex]?.page_id ?? null;
  const layoutConfig = { pages: sortedPages.map((p) => ({ page_id: p.page_id, page_order: p.page_order })) };

  return (
    <div style={baseStyle}>
      {(comp.type === "text" || comp.type === "label") && (
        <TextRenderer component={comp} displayValue={displayValue} getQueryResult={getQueryResult} />
      )}

      {comp.type === "table" && (
        <TableRenderer component={comp} getQueryResult={getQueryResult} />
      )}

      {comp.type === "image" && <ImageRenderer component={comp} />}

      {comp.type === "divider" && <DividerRenderer component={comp} />}

      {comp.type === "signature" && <SignatureRenderer component={comp} />}

      {comp.type === "stamp" && <StampRenderer component={comp} />}

      {comp.type === "pageNumber" && (
        <PageNumberRenderer component={comp} currentPageId={currentPageId} layoutConfig={layoutConfig} />
      )}

      {comp.type === "card" && (
        <CardRenderer component={comp} getQueryResult={getQueryResult} />
      )}

      {comp.type === "calculation" && (
        <CalculationRenderer component={comp} getQueryResult={getQueryResult} />
      )}

      {comp.type === "barcode" && (
        <BarcodeCanvasRenderer component={comp} getQueryResult={getQueryResult} />
      )}

      {comp.type === "checkbox" && (
        <CheckboxRenderer component={comp} getQueryResult={getQueryResult} />
      )}
    </div>
  );
}
