"use client"

/**
 * 通用 Widget 边界组件（shared 层）。
 *
 * 组合三个能力：
 * 1. Error Boundary — 隔离故障域，单个 Widget 抛错不影响其他区块
 * 2. Suspense — 流式渲染时显示骨架屏，避免白屏等待
 * 3. Skeleton — 与 Widget 尺寸匹配的占位
 *
 * 用法：
 * ```tsx
 * <WidgetBoundary title="成绩趋势">
 *   <GradeTrendChart data={data} />
 * </WidgetBoundary>
 * ```
 */

import { Component, Suspense, type ReactNode } from "react"
import { AlertCircle } from "lucide-react"
import { useTranslations } from "next-intl"
import { Button } from "@/shared/components/ui/button"
import { Skeleton } from "@/shared/components/ui/skeleton"

interface WidgetBoundaryProps {
  children: ReactNode
  /** Widget 标题（用于错误提示和 aria-label） */
  title?: string
  /** 骨架屏高度（默认 200px） */
  skeletonHeight?: number
  /** 自定义错误描述 */
  fallbackDescription?: string
  /** 重试按钮文案 */
  retryLabel?: string
}

interface WidgetBoundaryState {
  hasError: boolean
}

interface WidgetErrorBoundaryProps {
  title: string
  fallbackDescription: string
  retryLabel: string
  loadFailedMessage: string
  retryAriaLabel: string
  children: ReactNode
}

class WidgetErrorBoundary extends Component<
  WidgetErrorBoundaryProps,
  WidgetBoundaryState
> {
  constructor(props: WidgetErrorBoundaryProps) {
    super(props)
    this.state = { hasError: false }
  }

  static getDerivedStateFromError(): WidgetBoundaryState {
    return { hasError: true }
  }

  handleReset = (): void => {
    this.setState({ hasError: false })
  }

  render(): ReactNode {
    if (this.state.hasError) {
      return (
        <div
          role="alert"
          aria-live="assertive"
          className="flex h-full min-h-[200px] flex-col items-center justify-center gap-3 rounded-lg border border-destructive/30 bg-destructive/5 p-6 text-center"
        >
          <AlertCircle className="h-8 w-8 text-destructive" aria-hidden="true" />
          <div className="space-y-1">
            <p className="text-sm font-medium text-foreground">
              {this.props.loadFailedMessage}
            </p>
            <p className="text-xs text-muted-foreground">
              {this.props.fallbackDescription}
            </p>
          </div>
          <Button
            size="sm"
            variant="outline"
            onClick={this.handleReset}
            aria-label={this.props.retryAriaLabel}
          >
            {this.props.retryLabel}
          </Button>
        </div>
      )
    }

    return this.props.children
  }
}

function WidgetSkeleton({
  height,
  loadingAriaLabel,
}: {
  height: number
  loadingAriaLabel: string
}): ReactNode {
  return (
    <div
      role="status"
      aria-label={loadingAriaLabel}
      aria-live="polite"
      className="space-y-3 p-4"
      style={{ minHeight: height }}
    >
      <Skeleton className="h-6 w-1/3" />
      <Skeleton className="h-4 w-full" />
      <Skeleton className="h-4 w-2/3" />
      <Skeleton className="h-32 w-full" />
    </div>
  )
}

export function WidgetBoundary({
  children,
  title,
  skeletonHeight = 200,
  fallbackDescription,
  retryLabel,
}: WidgetBoundaryProps): ReactNode {
  const t = useTranslations("common")
  const effectiveTitle = title ?? t("widget.block")
  const effectiveFallbackDescription = fallbackDescription ?? t("widget.defaultFallback")
  const effectiveRetryLabel = retryLabel ?? t("widget.retry")
  const loadFailedMessage = t("widget.loadFailed", { title: effectiveTitle })
  const retryAriaLabel = t("widget.retryAriaLabel", { title: effectiveTitle })
  const loadingAriaLabel = t("widget.loadingAriaLabel", { title: effectiveTitle })

  return (
    <WidgetErrorBoundary
      title={effectiveTitle}
      fallbackDescription={effectiveFallbackDescription}
      retryLabel={effectiveRetryLabel}
      loadFailedMessage={loadFailedMessage}
      retryAriaLabel={retryAriaLabel}
    >
      <Suspense
        fallback={
          <WidgetSkeleton height={skeletonHeight} loadingAriaLabel={loadingAriaLabel} />
        }
      >
        {children}
      </Suspense>
    </WidgetErrorBoundary>
  )
}
