/**
 * 임베드된 화면 컴포넌트
 * 다른 화면 안에 임베드되어 표시되는 화면
 */

"use client";

import React, { forwardRef, useImperativeHandle, useState, useEffect, useRef, useCallback } from "react";
import type {
  ScreenEmbedding,
  DataReceiver,
  DataReceivable,
  EmbeddedScreenHandle,
  DataReceiveMode,
} from "@/types/screen-embedding";
import type { ComponentData } from "@/types/screen";
import { logger } from "@/lib/utils/logger";
import { applyMappingRules, filterDataByCondition } from "@/lib/utils/dataMapping";
import { DynamicComponentRenderer } from "@/lib/registry/DynamicComponentRenderer";
import { screenApi } from "@/lib/api/screen";
import { useSplitPanelContext, SplitPanelPosition } from "@/contexts/SplitPanelContext";
import { ScreenContextProvider } from "@/contexts/ScreenContext";
import { useAuth } from "@/hooks/useAuth";

interface EmbeddedScreenProps {
  embedding: ScreenEmbedding;
  onSelectionChanged?: (selectedRows: any[]) => void;
  position?: SplitPanelPosition; // 분할 패널 내 위치 (left/right)
  initialFormData?: Record<string, any>; // 🆕 수정 모드에서 전달되는 초기 데이터
  groupedData?: Record<string, any>[]; // 🆕 그룹 데이터 (수정 모드에서 원본 데이터 추적용)
}

/**
 * 임베드된 화면 컴포넌트
 */
export const EmbeddedScreen = forwardRef<EmbeddedScreenHandle, EmbeddedScreenProps>(
  ({ embedding, onSelectionChanged, position, initialFormData, groupedData }, ref) => {
    const [layout, setLayout] = useState<ComponentData[]>([]);
    const [selectedRows, setSelectedRows] = useState<any[]>([]);
    const [loading, setLoading] = useState(true);
    const [error, setError] = useState<string | null>(null);
    const [screenInfo, setScreenInfo] = useState<any>(null);
    const [formData, setFormData] = useState<Record<string, any>>(initialFormData || {}); // 🆕 초기 데이터로 시작
    const [formDataVersion, setFormDataVersion] = useState(0); // 🆕 폼 데이터 버전 (강제 리렌더링용)

    // 컴포넌트 참조 맵
    const componentRefs = useRef<Map<string, DataReceivable>>(new Map());

    // 분할 패널 컨텍스트 (분할 패널 내부에 있을 때만 사용)
    const splitPanelContext = useSplitPanelContext();

    // 🆕 selectedLeftData 참조 안정화 (실제 값이 바뀔 때만 업데이트)
    const selectedLeftData = splitPanelContext?.selectedLeftData;
    const prevSelectedLeftDataRef = useRef<string>("");

    // 🆕 사용자 정보 가져오기 (저장 액션에 필요)
    const { userId, userName, companyCode } = useAuth();

    // 컴포넌트들의 실제 영역 계산 (가로폭 맞춤을 위해)
    const contentBounds = React.useMemo(() => {
      if (layout.length === 0) return { width: 0, height: 0 };

      let maxRight = 0;
      let maxBottom = 0;

      layout.forEach((component) => {
        const { position: compPosition = { x: 0, y: 0 }, size = { width: 200, height: 40 } } = component;
        const right = (compPosition.x || 0) + (size.width || 200);
        const bottom = (compPosition.y || 0) + (size.height || 40);

        if (right > maxRight) maxRight = right;
        if (bottom > maxBottom) maxBottom = bottom;
      });

      return { width: maxRight, height: maxBottom };
    }, [layout]);

    // 필드 값 변경 핸들러
    const handleFieldChange = useCallback((fieldName: string, value: any) => {
      setFormData((prev) => ({
        ...prev,
        [fieldName]: value,
      }));
    }, []);

    // 화면 데이터 로드
    useEffect(() => {
      loadScreenData();
    }, [embedding.childScreenId]);

    // initialFormData 변경 시 formData 업데이트 (수정 모드)
    useEffect(() => {
      if (initialFormData && Object.keys(initialFormData).length > 0) {
        setFormData(initialFormData);
      }
    }, [initialFormData]);

    // 🆕 분할 패널에서 좌측 선택 데이터가 변경되면 우측 화면의 formData에 자동 반영
    // 🆕 좌측 선택 데이터가 변경되면 우측 formData를 업데이트
    useEffect(() => {
      // 우측 화면인 경우에만 적용
      if (position !== "right" || !splitPanelContext) {
        return;
      }

      // 자동 데이터 전달이 비활성화된 경우 스킵
      if (splitPanelContext.disableAutoDataTransfer) {
        return;
      }

      // 🆕 값 비교로 실제 변경 여부 확인 (불필요한 리렌더링 방지)
      const currentDataStr = JSON.stringify(selectedLeftData || {});
      if (prevSelectedLeftDataRef.current === currentDataStr) {
        return; // 실제 값이 같으면 스킵
      }
      prevSelectedLeftDataRef.current = currentDataStr;

      // 🆕 현재 화면의 모든 컴포넌트에서 columnName 수집
      const allColumnNames = layout.filter((comp) => comp.columnName).map((comp) => comp.columnName as string);

      // 🆕 모든 필드를 빈 값으로 초기화한 후, selectedLeftData로 덮어쓰기
      const initializedFormData: Record<string, any> = {};

      // 먼저 모든 컬럼을 빈 문자열로 초기화
      allColumnNames.forEach((colName) => {
        initializedFormData[colName] = "";
      });

      // selectedLeftData가 있으면 해당 값으로 덮어쓰기
      if (selectedLeftData && Object.keys(selectedLeftData).length > 0) {
        Object.keys(selectedLeftData).forEach((key) => {
          // null/undefined는 빈 문자열로, 나머지는 그대로
          initializedFormData[key] = selectedLeftData[key] ?? "";
        });
      }

      setFormData(initializedFormData);
      setFormDataVersion((v) => v + 1); // 🆕 버전 증가로 컴포넌트 강제 리렌더링
    }, [position, splitPanelContext, selectedLeftData, layout]);

    // 선택 변경 이벤트 전파
    useEffect(() => {
      onSelectionChanged?.(selectedRows);
    }, [selectedRows, onSelectionChanged]);

    /**
     * 화면 레이아웃 로드
     */
    const loadScreenData = async () => {
      try {
        setLoading(true);
        setError(null);

        // 화면 정보 로드 (screenApi.getScreen은 직접 ScreenDefinition 객체를 반환)
        const screenData = await screenApi.getScreen(embedding.childScreenId);
        if (screenData) {
          setScreenInfo(screenData);
        } else {
          console.warn("⚠️ [EmbeddedScreen] 화면 정보 로드 실패:", {
            screenId: embedding.childScreenId,
          });
        }

        // 화면 레이아웃 로드 (별도 API)
        const layoutData = await screenApi.getLayout(embedding.childScreenId);

        logger.info("📦 화면 레이아웃 로드 완료", {
          screenId: embedding.childScreenId,
          mode: embedding.mode,
          hasLayoutData: !!layoutData,
          componentsCount: layoutData?.components?.length || 0,
          position,
        });

        if (layoutData && layoutData.components && Array.isArray(layoutData.components)) {
          setLayout(layoutData.components);

          logger.info("✅ 임베드 화면 컴포넌트 설정 완료", {
            screenId: embedding.childScreenId,
            componentsCount: layoutData.components.length,
          });
        } else {
          logger.warn("⚠️ 화면에 컴포넌트가 없습니다", {
            screenId: embedding.childScreenId,
            layoutData,
          });
          setLayout([]);
        }
      } catch (err: any) {
        logger.error("화면 레이아웃 로드 실패", err);
        setError(err.message || "화면을 불러올 수 없습니다.");
      } finally {
        setLoading(false);
      }
    };

    /**
     * 컴포넌트 등록
     */
    const registerComponent = useCallback((id: string, component: DataReceivable) => {
      componentRefs.current.set(id, component);

      logger.debug("컴포넌트 등록", {
        componentId: id,
        componentType: component.componentType,
      });
    }, []);

    /**
     * 컴포넌트 등록 해제
     */
    const unregisterComponent = useCallback((id: string) => {
      componentRefs.current.delete(id);

      logger.debug("컴포넌트 등록 해제", {
        componentId: id,
      });
    }, []);

    /**
     * 선택된 행 업데이트
     */
    const handleSelectionChange = useCallback((rows: any[]) => {
      setSelectedRows(rows);
    }, []);

    // 외부에서 호출 가능한 메서드
    useImperativeHandle(ref, () => ({
      /**
       * 선택된 행 가져오기
       */
      getSelectedRows: () => {
        return selectedRows;
      },

      /**
       * 선택 초기화
       */
      clearSelection: () => {
        setSelectedRows([]);
      },

      /**
       * 데이터 수신
       */
      receiveData: async (data: any[], receivers: DataReceiver[]) => {
        logger.info("데이터 수신 시작", {
          dataCount: data.length,
          receiversCount: receivers.length,
        });

        const errors: Array<{ componentId: string; error: string }> = [];

        // 각 데이터 수신자에게 데이터 전달
        for (const receiver of receivers) {
          try {
            const component = componentRefs.current.get(receiver.targetComponentId);

            if (!component) {
              const errorMsg = `컴포넌트를 찾을 수 없습니다: ${receiver.targetComponentId}`;
              logger.warn(errorMsg);
              errors.push({
                componentId: receiver.targetComponentId,
                error: errorMsg,
              });
              continue;
            }

            // 1. 조건 필터링
            let filteredData = data;
            if (receiver.condition) {
              filteredData = filterDataByCondition(data, receiver.condition);

              logger.debug("조건 필터링 적용", {
                componentId: receiver.targetComponentId,
                originalCount: data.length,
                filteredCount: filteredData.length,
              });
            }

            // 2. 매핑 규칙 적용
            const mappedData = applyMappingRules(filteredData, receiver.mappingRules);

            logger.debug("매핑 규칙 적용", {
              componentId: receiver.targetComponentId,
              mappingRulesCount: receiver.mappingRules.length,
            });

            // 3. 검증
            if (receiver.validation) {
              if (receiver.validation.required && mappedData.length === 0) {
                throw new Error("필수 데이터가 없습니다.");
              }

              if (receiver.validation.minRows && mappedData.length < receiver.validation.minRows) {
                throw new Error(`최소 ${receiver.validation.minRows}개의 데이터가 필요합니다.`);
              }

              if (receiver.validation.maxRows && mappedData.length > receiver.validation.maxRows) {
                throw new Error(`최대 ${receiver.validation.maxRows}개까지만 허용됩니다.`);
              }
            }

            // 4. 데이터 전달
            await component.receiveData(mappedData, receiver.mode);

            logger.info("데이터 전달 성공", {
              componentId: receiver.targetComponentId,
              componentType: receiver.targetComponentType,
              mode: receiver.mode,
              dataCount: mappedData.length,
            });
          } catch (err: any) {
            logger.error("데이터 전달 실패", {
              componentId: receiver.targetComponentId,
              error: err.message,
            });

            errors.push({
              componentId: receiver.targetComponentId,
              error: err.message,
            });
          }
        }

        if (errors.length > 0) {
          throw new Error(`일부 컴포넌트에 데이터 전달 실패: ${errors.map((e) => e.componentId).join(", ")}`);
        }
      },

      /**
       * 현재 데이터 가져오기
       */
      getData: () => {
        const allData: Record<string, any> = {};

        componentRefs.current.forEach((component, id) => {
          allData[id] = component.getData();
        });

        return allData;
      },
    }));

    // 로딩 상태
    if (loading) {
      return (
        <div className="flex h-full items-center justify-center">
          <div className="flex flex-col items-center gap-4">
            <div className="border-primary h-8 w-8 animate-spin rounded-full border-4 border-t-transparent" />
            <p className="text-muted-foreground text-sm">화면을 불러오는 중...</p>
          </div>
        </div>
      );
    }

    // 에러 상태
    if (error) {
      return (
        <div className="flex h-full items-center justify-center">
          <div className="flex flex-col items-center gap-4 text-center">
            <div className="bg-destructive/10 flex h-12 w-12 items-center justify-center rounded-full">
              <svg className="text-destructive h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
                <path
                  strokeLinecap="round"
                  strokeLinejoin="round"
                  strokeWidth={2}
                  d="M12 9v2m0 4h.01m-6.938 4h13.856c1.54 0 2.502-1.667 1.732-3L13.732 4c-.77-1.333-2.694-1.333-3.464 0L3.34 16c-.77 1.333.192 3 1.732 3z"
                />
              </svg>
            </div>
            <div>
              <p className="text-sm font-medium">화면을 불러올 수 없습니다</p>
              <p className="text-muted-foreground mt-1 text-xs">{error}</p>
            </div>
            <button onClick={loadScreenData} className="text-primary text-sm hover:underline">
              다시 시도
            </button>
          </div>
        </div>
      );
    }

    // 화면 렌더링 - 절대 위치 기반 레이아웃 (원본 화면과 동일하게)
    // position을 ScreenContextProvider에 전달하여 중첩된 화면에서도 위치를 알 수 있게 함
    return (
      <ScreenContextProvider
        screenId={embedding.childScreenId}
        tableName={screenInfo?.tableName}
        splitPanelPosition={position}
      >
        <div className="relative h-full w-full overflow-auto p-4">
          {layout.length === 0 ? (
            <div className="flex h-full items-center justify-center">
              <p className="text-muted-foreground text-sm">화면에 컴포넌트가 없습니다.</p>
            </div>
          ) : (
            <div
              className="relative w-full"
              style={{
                minHeight: contentBounds.height + 20, // 여유 공간 추가
              }}
            >
              {layout.map((component) => {
                const { position: compPosition = { x: 0, y: 0, z: 1 }, size = { width: 200, height: 40 } } = component;

                // 컴포넌트가 컨테이너 너비를 초과하지 않도록 너비 조정
                // 부모 컨테이너의 100%를 기준으로 계산
                const componentStyle: React.CSSProperties = {
                  left: compPosition.x || 0,
                  top: compPosition.y || 0,
                  width: size.width || 200,
                  height: size.height || 40,
                  zIndex: compPosition.z || 1,
                  // 컴포넌트가 오른쪽 경계를 넘어가면 너비 조정
                  maxWidth: `calc(100% - ${compPosition.x || 0}px)`,
                };

                // 🆕 formDataVersion을 key에서 제거하여 불필요한 remount 방지
                // universal-form-modal 같은 컴포넌트가 채번 후 unmount되는 문제 해결
                return (
                  <div key={component.id} className="absolute" style={componentStyle}>
                    <DynamicComponentRenderer
                      component={component}
                      isInteractive={true}
                      screenId={embedding.childScreenId}
                      tableName={screenInfo?.tableName}
                      formData={formData}
                      onFormDataChange={handleFieldChange}
                      onSelectionChange={embedding.mode === "select" ? handleSelectionChange : undefined}
                      userId={userId}
                      userName={userName}
                      companyCode={companyCode}
                      groupedData={groupedData}
                      initialData={initialFormData}
                    />
                  </div>
                );
              })}
            </div>
          )}
        </div>
      </ScreenContextProvider>
    );
  },
);

EmbeddedScreen.displayName = "EmbeddedScreen";
