/**
 * @module useLayoutIO
 * @description 리포트 레이아웃의 서버 저장/로드 및 템플릿 적용을 담당한다.
 *
 * - loadLayout: 서버에서 리포트 상세 정보와 레이아웃을 불러온다.
 *   - 기존 단일 페이지 구조를 다중 페이지 구조로 자동 마이그레이션한다.
 *   - 레이아웃이 없으면 A4 portrait 기본 페이지를 생성한다.
 *
 * - saveLayout: 현재 레이아웃과 쿼리, 메뉴 연결 정보를 서버에 저장한다.
 *   - reportId가 "new"이면 먼저 리포트를 생성하고 URL을 교체한다.
 *
 * - saveLayoutWithMenus: 메뉴 연결 정보를 갱신한 뒤 레이아웃을 저장한다.
 *
 * - applyTemplate: DB에서 템플릿을 조회하여 현재 페이지에 적용한다.
 *   컴포넌트와 쿼리 ID를 재생성하여 충돌을 방지한다.
 */

import { useCallback } from "react";
import { v4 as uuidv4 } from "uuid";
import { reportApi } from "@/lib/api/reportApi";
import type { ReportDetail, ReportLayout, ReportLayoutConfig, ReportPage, ComponentConfig } from "@/types/report";
import type { ReportQuery } from "./types";
import type { ToastFunction, SetComponentsFn } from "./internalTypes";

/** A4 기본 페이지 설정 */
const DEFAULT_PAGE: Omit<ReportPage, "page_id"> = {
  page_name: "페이지 1",
  page_order: 0,
  width: 210,
  height: 297,
  orientation: "portrait",
  margins: { top: 10, bottom: 10, left: 10, right: 10 },
  background_color: "#ffffff",
  components: [],
};

/** A4 기본 페이지 객체를 새 ID와 함께 생성한다. */
function createDefaultPage(overrides: Partial<ReportPage> = {}): ReportPage {
  return { ...DEFAULT_PAGE, page_id: uuidv4(), ...overrides };
}

export interface LayoutIOActions {
  isLoading: boolean;
  isSaving: boolean;
  loadLayout: () => Promise<void>;
  saveLayout: () => Promise<void>;
  saveLayoutWithMenus: (menuObjids: number[]) => Promise<void>;
  applyTemplate: (templateId: string) => Promise<void>;
}

interface LayoutIODeps {
  reportId: string;
  layoutConfig: ReportLayoutConfig;
  queries: ReportQuery[];
  menuObjids: number[];
  setMenuObjids: React.Dispatch<React.SetStateAction<number[]>>;
  setIsLoading: React.Dispatch<React.SetStateAction<boolean>>;
  setIsSaving: React.Dispatch<React.SetStateAction<boolean>>;
  setReportDetail: React.Dispatch<React.SetStateAction<ReportDetail | null>>;
  setLayout: React.Dispatch<React.SetStateAction<ReportLayout | null>>;
  setLayoutConfig: React.Dispatch<React.SetStateAction<ReportLayoutConfig>>;
  setCurrentPageId: React.Dispatch<React.SetStateAction<string | null>>;
  setQueries: (queries: ReportQuery[]) => void;
  /** 현재 페이지의 컴포넌트를 업데이트하는 헬퍼 */
  setComponents: SetComponentsFn;
  /** applyTemplate에서 현재 페이지 ID를 안정적으로 읽기 위한 ref */
  currentPageIdRef: React.MutableRefObject<string | null>;
  toast: ToastFunction;
}

/** 공통 저장 페이로드를 빌드하는 헬퍼 */
function buildSavePayload(
  layoutConfig: ReportLayoutConfig,
  queries: ReportQuery[],
  menuObjids: number[],
) {
  return {
    layoutConfig,
    queries: queries.map((q) => ({
      ...q,
      externalConnectionId: q.externalConnectionId || undefined,
    })),
    menuObjids,
  };
}

/** 백엔드 쿼리 응답을 ReportQuery 형태로 변환한다 (snake_case → camelCase). */
function mapBackendQuery(q: Record<string, unknown>): ReportQuery {
  return {
    id: q.query_id as string,
    name: q.query_name as string,
    type: q.query_type as "MASTER" | "DETAIL",
    sqlQuery: q.sql_query as string,
    parameters: Array.isArray(q.parameters) ? (q.parameters as string[]) : [],
    externalConnectionId: (q.external_connection_id as number | null) || undefined,
  };
}

export function useLayoutIO({
  reportId,
  layoutConfig,
  queries,
  menuObjids,
  setMenuObjids,
  setIsLoading,
  setIsSaving,
  setReportDetail,
  setLayout,
  setLayoutConfig,
  setCurrentPageId,
  setQueries,
  setComponents,
  currentPageIdRef,
  toast,
}: LayoutIODeps): Omit<LayoutIOActions, "isLoading" | "isSaving"> {
  /**
   * 서버에서 리포트 상세 정보와 레이아웃을 불러온다.
   * reportId가 "new"이면 기본 페이지만 생성한다.
   */
  const loadLayout = useCallback(async () => {
    setIsLoading(true);
    try {
      if (reportId === "new") {
        setLayoutConfig({ pages: [createDefaultPage()] });
        setCurrentPageId((prev) => prev ?? uuidv4());
        return;
      }

      // 리포트 상세 조회
      const detailResponse = await reportApi.getReportById(reportId);
      if (detailResponse.success && detailResponse.data) {
        setReportDetail(detailResponse.data);

        if (detailResponse.data.queries?.length > 0) {
          setQueries((detailResponse.data.queries as unknown as Record<string, unknown>[]).map(mapBackendQuery));
        }

        setMenuObjids(detailResponse.data.menuObjids ?? []);
      }

      // 레이아웃 조회
      try {
        const layoutResponse = await reportApi.getLayout(reportId);
        if (layoutResponse.success && layoutResponse.data) {
          const layoutData = layoutResponse.data;
          setLayout(layoutData);

          // 다중 페이지 구조 감지
          const storedConfig = layoutData.components;
          const topLevelPages = layoutData.pages;
          const nestedPages =
            storedConfig && typeof storedConfig === "object" && !Array.isArray(storedConfig)
              ? (storedConfig as Record<string, unknown>).pages
              : null;

          const pages =
            Array.isArray(topLevelPages) && topLevelPages.length > 0
              ? topLevelPages
              : Array.isArray(nestedPages) && (nestedPages as unknown[]).length > 0
              ? (nestedPages as ReportPage[])
              : null;

          if (pages) {
            // 다중 페이지 구조 로드
            const watermark =
              (layoutData as unknown as Record<string, unknown>).watermark ||
              (storedConfig as unknown as Record<string, unknown>)?.watermark;
            setLayoutConfig({ pages, watermark: watermark as ReportLayoutConfig["watermark"] });
            setCurrentPageId((pages[0] as ReportPage).page_id);
          } else {
            // 기존 단일 페이지 구조 → 다중 페이지로 자동 마이그레이션
            const oldComponents = Array.isArray(layoutData.components)
              ? (layoutData.components as ComponentConfig[])
              : [];

            if (oldComponents.length > 0) {
              const migratedPage = createDefaultPage({
                width: layoutData.canvas_width || 210,
                height: layoutData.canvas_height || 297,
                orientation: (layoutData.page_orientation as "portrait" | "landscape") || "portrait",
                margins: {
                  top: layoutData.margin_top || 20,
                  bottom: layoutData.margin_bottom || 20,
                  left: layoutData.margin_left || 20,
                  right: layoutData.margin_right || 20,
                },
                components: oldComponents,
              });
              setLayoutConfig({ pages: [migratedPage] });
              setCurrentPageId(migratedPage.page_id);
            } else {
              const defaultPage = createDefaultPage();
              setLayoutConfig({ pages: [defaultPage] });
              setCurrentPageId(defaultPage.page_id);
            }
          }
        }
      } catch {
        // 레이아웃이 없으면 기본 페이지 생성
        const defaultPage = createDefaultPage();
        setLayoutConfig({ pages: [defaultPage] });
        setCurrentPageId(defaultPage.page_id);
      }
    } catch (error) {
      toast({
        title: "오류",
        description: error instanceof Error ? error.message : "리포트를 불러오는데 실패했습니다.",
        variant: "destructive",
      });
    } finally {
      setIsLoading(false);
    }
  }, [reportId, setIsLoading, setLayoutConfig, setCurrentPageId, setReportDetail, setQueries, setMenuObjids, setLayout, toast]);

  /**
   * 현재 레이아웃, 쿼리, 메뉴 연결 정보를 서버에 저장한다.
   * reportId가 "new"이면 먼저 리포트를 생성하고 URL을 업데이트한다.
   */
  const saveLayout = useCallback(async () => {
    setIsSaving(true);
    try {
      let actualReportId = reportId;

      if (reportId === "new") {
        const createResponse = await reportApi.createReport({
          reportNameKor: "새 리포트",
          reportType: "BASIC",
          description: "새로 생성된 리포트입니다.",
        });
        if (!createResponse.success || !createResponse.data) throw new Error("리포트 생성에 실패했습니다.");
        actualReportId = createResponse.data.reportId;
        window.history.replaceState({}, "", `/admin/report/designer/${actualReportId}`);
      }

      await reportApi.saveLayout(actualReportId, buildSavePayload(layoutConfig, queries, menuObjids));
      toast({ title: "성공", description: reportId === "new" ? "리포트가 생성되었습니다." : "레이아웃이 저장되었습니다." });

      if (reportId === "new") await loadLayout();
    } catch (error) {
      toast({
        title: "오류",
        description: error instanceof Error ? error.message : "저장에 실패했습니다.",
        variant: "destructive",
      });
    } finally {
      setIsSaving(false);
    }
  }, [reportId, layoutConfig, queries, menuObjids, setIsSaving, loadLayout, toast]);

  /**
   * 메뉴 연결 정보를 갱신한 뒤 레이아웃을 저장한다.
   * 상태 업데이트와 API 호출을 함께 수행한다.
   */
  const saveLayoutWithMenus = useCallback(
    async (selectedMenuObjids: number[]) => {
      setMenuObjids(selectedMenuObjids);
      setIsSaving(true);
      try {
        let actualReportId = reportId;

        if (reportId === "new") {
          const createResponse = await reportApi.createReport({
            reportNameKor: "새 리포트",
            reportType: "BASIC",
            description: "새로 생성된 리포트입니다.",
          });
          if (!createResponse.success || !createResponse.data) throw new Error("리포트 생성에 실패했습니다.");
          actualReportId = createResponse.data.reportId;
          window.history.replaceState({}, "", `/admin/report/designer/${actualReportId}`);
        }

        await reportApi.saveLayout(actualReportId, buildSavePayload(layoutConfig, queries, selectedMenuObjids));
        toast({ title: "성공", description: reportId === "new" ? "리포트가 생성되었습니다." : "레이아웃이 저장되었습니다." });

        if (reportId === "new") await loadLayout();
      } catch (error) {
        toast({
          title: "오류",
          description: error instanceof Error ? error.message : "저장에 실패했습니다.",
          variant: "destructive",
        });
      } finally {
        setIsSaving(false);
      }
    },
    [reportId, layoutConfig, queries, setMenuObjids, setIsSaving, loadLayout, toast],
  );

  /**
   * DB에서 템플릿을 조회하여 현재 페이지에 적용한다.
   * 컴포넌트와 쿼리의 ID를 재생성하고, queryId 매핑을 통해 컴포넌트-쿼리 연결을 유지한다.
   * 템플릿에 pageSettings가 포함된 경우 현재 페이지 설정도 업데이트한다.
   */
  const applyTemplate = useCallback(
    async (templateId: string) => {
      try {
        if (!confirm("현재 레이아웃을 덮어씁니다. 계속하시겠습니까?")) return;

        const response = await reportApi.getTemplates();
        if (!response.success || !response.data) throw new Error("템플릿 목록을 불러올 수 없습니다.");

        const allTemplates = [...(response.data.system ?? []), ...(response.data.custom ?? [])];
        const template = allTemplates.find((t: { template_id: string }) => t.template_id === templateId);
        if (!template) throw new Error("템플릿을 찾을 수 없습니다.");

        // layout_config 파싱
        let parsedLayout: { components?: ComponentConfig[]; pageSettings?: Record<string, unknown> } = {};
        try {
          parsedLayout = template.layout_config
            ? typeof template.layout_config === "string"
              ? JSON.parse(template.layout_config)
              : template.layout_config
            : {};
        } catch {
          parsedLayout = { components: [] };
        }

        // default_queries 파싱
        let defaultQueries: Record<string, unknown>[] = [];
        try {
          if (template.default_queries) {
            const raw = typeof template.default_queries === "string"
              ? JSON.parse(template.default_queries)
              : template.default_queries;
            defaultQueries = Array.isArray(raw) ? raw : [];
          }
        } catch {
          defaultQueries = [];
        }

        // 쿼리 ID 재생성 및 원본→신규 매핑 생성
        const queryIdMap = new Map<string, string>();
        const newQueries: ReportQuery[] = defaultQueries
          .filter((q): q is Record<string, unknown> => typeof q === "object" && q !== null)
          .map((q) => {
            const oldId = (q.id as string) || (q.name as string) || "";
            const newId = `query-${Date.now()}-${Math.random()}`;
            if (oldId) queryIdMap.set(oldId, newId);
            return {
              id: newId,
              name: (q.name as string) ?? "",
              type: ((q.type as string) || "MASTER") as "MASTER" | "DETAIL",
              sqlQuery: (q.sqlQuery as string) ?? "",
              parameters: Array.isArray(q.parameters) ? (q.parameters as string[]) : [],
              externalConnectionId: (q.externalConnectionId as number | null) ?? null,
            };
          });

        // 컴포넌트 ID 재생성 + queryId 매핑
        const newComponents: ComponentConfig[] = (parsedLayout.components ?? []).map((comp) => ({
          ...comp,
          id: `comp-${Date.now()}-${Math.random()}`,
          queryId: comp.queryId ? (queryIdMap.get(comp.queryId) ?? comp.queryId) : comp.queryId,
        }));

        // 페이지 설정 적용 (템플릿에 pageSettings가 있는 경우)
        const pageSettings = parsedLayout.pageSettings;
        if (pageSettings) {
          setLayoutConfig((prev) => {
            const pageId = currentPageIdRef.current;
            if (!pageId) return prev;
            return {
              ...prev,
              pages: prev.pages.map((p) =>
                p.page_id === pageId
                  ? {
                      ...p,
                      width: (pageSettings.width as number) ?? p.width,
                      height: (pageSettings.height as number) ?? p.height,
                      orientation: (pageSettings.orientation as "portrait" | "landscape") ?? p.orientation,
                      margins: (pageSettings.margins as ReportPage["margins"]) ?? p.margins,
                    }
                  : p,
              ),
            };
          });
        }

        setComponents(newComponents);
        setQueries(newQueries);

        const description =
          newComponents.length === 0
            ? "템플릿이 적용되었습니다. (빈 템플릿)"
            : `템플릿이 적용되었습니다. (컴포넌트 ${newComponents.length}개, 쿼리 ${newQueries.length}개)`;

        toast({ title: "성공", description });
      } catch (error) {
        toast({
          title: "오류",
          description: error instanceof Error ? error.message : "템플릿 적용에 실패했습니다.",
          variant: "destructive",
        });
      }
    },
    // currentPageIdRef는 ref이므로 deps에 포함하지 않음
    // eslint-disable-next-line react-hooks/exhaustive-deps
    [setLayoutConfig, setComponents, setQueries, toast],
  );

  return { loadLayout, saveLayout, saveLayoutWithMenus, applyTemplate };
}
