"use client";

/**
 * V2 ErrorBoundary - 컴포넌트별 에러 격리
 *
 * 특징:
 * - 각 컴포넌트의 에러가 다른 컴포넌트에 영향을 주지 않음
 * - 폴백 UI 제공
 * - 재시도 기능
 * - 에러 로깅 및 이벤트 발행
 */

import React, { Component, ErrorInfo, ReactNode } from "react";
import { v2EventBus, V2_EVENTS } from "../events";
import { AlertCircle, RefreshCw } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert";

interface V2ErrorBoundaryProps {
  /** 자식 컴포넌트 */
  children: ReactNode;
  /** 컴포넌트 ID (에러 추적용) */
  componentId: string;
  /** 컴포넌트 타입 (에러 추적용) */
  componentType: string;
  /** 사용자 정의 폴백 UI */
  fallback?: ReactNode | ((error: Error, retry: () => void) => ReactNode);
  /** 폴백 UI 표시 방식 */
  fallbackStyle?: "minimal" | "compact" | "full";
  /** 에러 발생 시 콜백 */
  onError?: (error: Error, errorInfo: ErrorInfo) => void;
  /** 복구 가능 여부 */
  recoverable?: boolean;
  /** 자동 재시도 횟수 (0이면 자동 재시도 안 함) */
  autoRetryCount?: number;
  /** 자동 재시도 간격 (ms) */
  autoRetryDelay?: number;
}

interface V2ErrorBoundaryState {
  hasError: boolean;
  error: Error | null;
  errorInfo: ErrorInfo | null;
  retryCount: number;
}

export class V2ErrorBoundary extends Component<
  V2ErrorBoundaryProps,
  V2ErrorBoundaryState
> {
  private retryTimeoutId: ReturnType<typeof setTimeout> | null = null;

  static defaultProps = {
    fallbackStyle: "compact" as const,
    recoverable: true,
    autoRetryCount: 0,
    autoRetryDelay: 3000,
  };

  constructor(props: V2ErrorBoundaryProps) {
    super(props);
    this.state = {
      hasError: false,
      error: null,
      errorInfo: null,
      retryCount: 0,
    };
  }

  static getDerivedStateFromError(error: Error): Partial<V2ErrorBoundaryState> {
    return { hasError: true, error };
  }

  componentDidCatch(error: Error, errorInfo: ErrorInfo): void {
    const { componentId, componentType, onError, autoRetryCount = 0 } = this.props;
    const { retryCount } = this.state;

    // 상태 업데이트
    this.setState({ errorInfo });

    // 에러 로깅
    console.error(
      `[V2ErrorBoundary] 컴포넌트 에러 - ${componentType}(${componentId}):`,
      error
    );
    console.error("Component Stack:", errorInfo.componentStack);

    // 에러 이벤트 발행
    v2EventBus.emitSync(V2_EVENTS.COMPONENT_ERROR, {
      componentId,
      componentType,
      error,
      recoverable: this.props.recoverable ?? true,
    });

    // 사용자 정의 에러 핸들러 호출
    onError?.(error, errorInfo);

    // 자동 재시도
    if (autoRetryCount > 0 && retryCount < autoRetryCount) {
      this.scheduleAutoRetry();
    }
  }

  componentWillUnmount(): void {
    if (this.retryTimeoutId) {
      clearTimeout(this.retryTimeoutId);
    }
  }

  private scheduleAutoRetry = (): void => {
    const { autoRetryDelay = 3000 } = this.props;

    this.retryTimeoutId = setTimeout(() => {
      this.handleRetry();
    }, autoRetryDelay);
  };

  private handleRetry = (): void => {
    const { componentId, componentType } = this.props;

    // 복구 이벤트 발행
    v2EventBus.emitSync(V2_EVENTS.COMPONENT_RECOVER, {
      componentId,
      componentType,
    });

    this.setState((prev) => ({
      hasError: false,
      error: null,
      errorInfo: null,
      retryCount: prev.retryCount + 1,
    }));
  };

  private renderMinimalFallback(): ReactNode {
    const { recoverable = true } = this.props;

    return (
      <div className="flex items-center justify-center p-2 text-xs text-destructive">
        <AlertCircle className="mr-1 h-3 w-3" />
        <span>오류 발생</span>
        {recoverable && (
          <button
            onClick={this.handleRetry}
            className="ml-2 underline hover:no-underline"
          >
            재시도
          </button>
        )}
      </div>
    );
  }

  private renderCompactFallback(): ReactNode {
    const { componentType, recoverable = true } = this.props;
    const { error } = this.state;

    return (
      <div className="rounded-md border border-destructive/50 bg-destructive/5 p-3">
        <div className="flex items-center gap-2">
          <AlertCircle className="h-4 w-4 text-destructive" />
          <span className="text-sm font-medium text-destructive">
            {componentType} 로드 실패
          </span>
        </div>
        {error && (
          <p className="mt-1 text-xs text-muted-foreground">
            {error.message.substring(0, 100)}
            {error.message.length > 100 ? "..." : ""}
          </p>
        )}
        {recoverable && (
          <Button
            variant="outline"
            size="sm"
            onClick={this.handleRetry}
            className="mt-2"
          >
            <RefreshCw className="mr-1 h-3 w-3" />
            재시도
          </Button>
        )}
      </div>
    );
  }

  private renderFullFallback(): ReactNode {
    const { componentId, componentType, recoverable = true } = this.props;
    const { error, errorInfo, retryCount } = this.state;

    return (
      <Alert variant="destructive" className="my-2">
        <AlertCircle className="h-4 w-4" />
        <AlertTitle>
          {componentType} 컴포넌트 오류
        </AlertTitle>
        <AlertDescription>
          <div className="mt-2 space-y-2">
            <p className="text-sm">
              <strong>컴포넌트 ID:</strong> {componentId}
            </p>
            {error && (
              <p className="text-sm">
                <strong>에러 메시지:</strong> {error.message}
              </p>
            )}
            {retryCount > 0 && (
              <p className="text-sm text-muted-foreground">
                재시도 횟수: {retryCount}회
              </p>
            )}
            {process.env.NODE_ENV === "development" && errorInfo && (
              <details className="mt-2">
                <summary className="cursor-pointer text-xs">
                  스택 트레이스 보기
                </summary>
                <pre className="mt-1 max-h-32 overflow-auto whitespace-pre-wrap rounded bg-muted p-2 text-xs">
                  {errorInfo.componentStack}
                </pre>
              </details>
            )}
            {recoverable && (
              <Button
                variant="outline"
                size="sm"
                onClick={this.handleRetry}
                className="mt-2"
              >
                <RefreshCw className="mr-1 h-3 w-3" />
                다시 시도
              </Button>
            )}
          </div>
        </AlertDescription>
      </Alert>
    );
  }

  render(): ReactNode {
    const { children, fallback, fallbackStyle = "compact" } = this.props;
    const { hasError, error } = this.state;

    if (!hasError) {
      return children;
    }

    // 사용자 정의 폴백
    if (fallback) {
      if (typeof fallback === "function") {
        return fallback(error!, this.handleRetry);
      }
      return fallback;
    }

    // 기본 폴백 스타일별 렌더링
    switch (fallbackStyle) {
      case "minimal":
        return this.renderMinimalFallback();
      case "full":
        return this.renderFullFallback();
      case "compact":
      default:
        return this.renderCompactFallback();
    }
  }
}

// ============================================================================
// 함수형 래퍼 (HOC)
// ============================================================================

interface WithV2ErrorBoundaryOptions {
  componentType: string;
  fallbackStyle?: "minimal" | "compact" | "full";
  recoverable?: boolean;
}

/**
 * V2 에러 바운더리 HOC
 *
 * @example
 * ```typescript
 * const SafeComponent = withV2ErrorBoundary(MyComponent, {
 *   componentType: "MyComponent",
 *   fallbackStyle: "compact",
 * });
 * ```
 */
export function withV2ErrorBoundary<P extends object>(
  WrappedComponent: React.ComponentType<P>,
  options: WithV2ErrorBoundaryOptions
): React.FC<P & { componentId?: string }> {
  const { componentType, fallbackStyle, recoverable } = options;

  const WithErrorBoundary: React.FC<P & { componentId?: string }> = (props) => {
    const componentId =
      props.componentId ?? `${componentType}_${Date.now()}`;

    return (
      <V2ErrorBoundary
        componentId={componentId}
        componentType={componentType}
        fallbackStyle={fallbackStyle}
        recoverable={recoverable}
      >
        <WrappedComponent {...props} />
      </V2ErrorBoundary>
    );
  };

  WithErrorBoundary.displayName = `WithV2ErrorBoundary(${componentType})`;

  return WithErrorBoundary;
}

// ============================================================================
// 훅 기반 에러 리포팅 (ErrorBoundary 외부에서 에러 보고용)
// ============================================================================

/**
 * V2 에러 리포팅 훅
 *
 * ErrorBoundary가 잡지 못하는 비동기 에러 등을 보고할 때 사용
 *
 * @example
 * ```typescript
 * const reportError = useV2ErrorReporter("my-component", "MyComponent");
 *
 * const handleClick = async () => {
 *   try {
 *     await someAsyncOperation();
 *   } catch (error) {
 *     reportError(error as Error);
 *   }
 * };
 * ```
 */
export function useV2ErrorReporter(
  componentId: string,
  componentType: string
): (error: Error, recoverable?: boolean) => void {
  return React.useCallback(
    (error: Error, recoverable = true) => {
      console.error(
        `[V2ErrorReporter] ${componentType}(${componentId}):`,
        error
      );

      v2EventBus.emitSync(V2_EVENTS.COMPONENT_ERROR, {
        componentId,
        componentType,
        error,
        recoverable,
      });
    },
    [componentId, componentType]
  );
}

