import { useEffect, type ReactNode, type CSSProperties } from "react"

/**
 * 通用骨架屏系统
 *
 * 使用方法：
 *   1. 在页面文件顶部声明骨架规格（"声明区"）：
 *        const skeleton: SkeletonSpec[] = [
 *          { type: 'header', title: 220, subtitle: 300 },
 *          { type: 'grid', cols: 4, count: 4, item: { type: 'stat-card' } },
 *        ]
 *   2. 使用 <PageSkeleton loading={...} spec={skeleton}> 包裹页面内容。
 *   3. CSS 会在首次使用时自动注入，无需手动引入样式。
 */

type Size = number | string

export type SkeletonSpec =
  | { type: "rect"; w?: Size; h?: Size; rounded?: Size; className?: string }
  | { type: "circle"; size?: Size }
  | { type: "text"; lines?: number; lastWidth?: Size; lineHeight?: Size }
  | { type: "header"; title?: Size; subtitle?: Size }
  | { type: "stat-card" }
  | { type: "action-card" }
  | { type: "list-item"; lines?: number }
  | { type: "card"; h?: Size; padded?: boolean; children?: SkeletonSpec[] }
  | { type: "banner"; h?: Size }
  | { type: "table"; rows?: number; cols?: number }
  | {
      type: "grid"
      cols: number
      smCols?: number
      count: number
      gap?: number
      item: SkeletonSpec
    }
  | { type: "stack"; gap?: number; children: SkeletonSpec[] }
  | { type: "row"; gap?: number; children: SkeletonSpec[]; align?: "start" | "center" }
  | { type: "split"; ratio?: "2:1" | "1:1" | "1:2"; gap?: number; children: SkeletonSpec[] }

const STYLE_ID = "qycw-skeleton-styles"

const SKELETON_CSS = `
@keyframes qycw-skel-shimmer {
  0% { background-position: 200% 0; }
  100% { background-position: -200% 0; }
}
@keyframes qycw-skel-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes qycw-page-enter {
  from { opacity: 0; transform: translateY(10px); }
  to { opacity: 1; transform: translateY(0); }
}
@keyframes qycw-content-fade {
  from { opacity: 0; transform: translateY(4px); }
  to { opacity: 1; transform: translateY(0); }
}
.qycw-skel {
  display: block;
  background: linear-gradient(90deg, #f1f5f9 0%, #f8fafc 50%, #f1f5f9 100%);
  background-size: 200% 100%;
  animation: qycw-skel-shimmer 1.6s ease-in-out infinite;
  border-radius: 8px;
}
.qycw-skel-round { border-radius: 9999px; }
.qycw-skel-rect { border-radius: 6px; }
.qycw-skel-card {
  background: #ffffff;
  border: 1px solid #f1f5f9;
  border-radius: 14px;
  padding: 20px;
}
.qycw-skel-wrap { animation: qycw-skel-fade-in 0.25s ease-out; }
.qycw-page-enter { animation: qycw-page-enter 0.5s cubic-bezier(0.16, 1, 0.3, 1) both; }
.qycw-content-fade { animation: qycw-content-fade 0.4s cubic-bezier(0.16, 1, 0.3, 1) both; }
@media (prefers-reduced-motion: reduce) {
  .qycw-page-enter, .qycw-content-fade, .qycw-skel-wrap, .qycw-skel { animation: none !important; }
}
`

let cssInjected = false
function ensureCss() {
  if (cssInjected) return
  if (typeof document === "undefined") return
  if (document.getElementById(STYLE_ID)) {
    cssInjected = true
    return
  }
  const style = document.createElement("style")
  style.id = STYLE_ID
  style.textContent = SKELETON_CSS
  document.head.appendChild(style)
  cssInjected = true
}

// 模块加载即注入，保证 qycw-page-enter 等在首帧就生效
ensureCss()

const sizeToCss = (v?: Size): string | undefined =>
  v === undefined ? undefined : typeof v === "number" ? `${v}px` : v

function bar(style: CSSProperties, key?: string | number, extra = "qycw-skel-rect") {
  return <span key={key} className={`qycw-skel ${extra}`} style={style} />
}

function renderNode(spec: SkeletonSpec, key?: string | number): ReactNode {
  switch (spec.type) {
    case "rect":
      return bar(
        {
          width: sizeToCss(spec.w) ?? "100%",
          height: sizeToCss(spec.h) ?? "16px",
          borderRadius: sizeToCss(spec.rounded),
        },
        key,
        `qycw-skel-rect ${spec.className || ""}`,
      )

    case "circle": {
      const s = sizeToCss(spec.size) ?? "40px"
      return bar({ width: s, height: s }, key, "qycw-skel-round")
    }

    case "text": {
      const lines = spec.lines ?? 3
      const lh = sizeToCss(spec.lineHeight) ?? "12px"
      return (
        <span key={key} style={{ display: "flex", flexDirection: "column", gap: "8px" }}>
          {Array.from({ length: lines }).map((_, i) => {
            const isLast = i === lines - 1
            const w = isLast ? sizeToCss(spec.lastWidth) ?? "60%" : "100%"
            return bar({ width: w, height: lh }, i)
          })}
        </span>
      )
    }

    case "header":
      return (
        <span key={key} style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
          {bar({ width: sizeToCss(spec.title) ?? "220px", height: "26px" }, "t")}
          {bar({ width: sizeToCss(spec.subtitle) ?? "300px", height: "12px" }, "s")}
        </span>
      )

    case "stat-card":
      return (
        <div key={key} className="qycw-skel-card">
          <div style={{ display: "flex", justifyContent: "space-between", alignItems: "center" }}>
            <span style={{ display: "flex", flexDirection: "column", gap: "10px" }}>
              {bar({ width: "64px", height: "12px" })}
              {bar({ width: "96px", height: "26px" })}
            </span>
            {bar({ width: "44px", height: "44px", borderRadius: "12px" })}
          </div>
        </div>
      )

    case "action-card":
      return (
        <div
          key={key}
          className="qycw-skel-card"
          style={{ display: "flex", flexDirection: "column", alignItems: "center", gap: "12px" }}
        >
          {bar({ width: "48px", height: "48px", borderRadius: "12px" })}
          {bar({ width: "60%", height: "12px" })}
          {bar({ width: "80%", height: "10px" })}
        </div>
      )

    case "list-item":
      return (
        <div
          key={key}
          className="qycw-skel-card"
          style={{ display: "flex", alignItems: "center", gap: "12px" }}
        >
          {bar({ width: "40px", height: "40px" }, "a", "qycw-skel-round")}
          <span style={{ flex: 1, display: "flex", flexDirection: "column", gap: "8px" }}>
            {bar({ width: "40%", height: "12px" })}
            {Array.from({ length: (spec.lines ?? 1) }).map((_, i) =>
              bar({ width: i === 0 ? "75%" : "55%", height: "10px" }, i),
            )}
          </span>
        </div>
      )

    case "card":
      return (
        <div
          key={key}
          className={spec.padded === false ? "" : "qycw-skel-card"}
          style={{
            minHeight: sizeToCss(spec.h),
            display: "flex",
            flexDirection: "column",
            gap: "12px",
          }}
        >
          {spec.children
            ? spec.children.map((c, i) => renderNode(c, i))
            : bar({ width: "100%", height: sizeToCss(spec.h) ?? "120px" })}
        </div>
      )

    case "banner":
      return bar(
        { width: "100%", height: sizeToCss(spec.h) ?? "96px", borderRadius: "16px" },
        key,
      )

    case "table": {
      const rows = spec.rows ?? 5
      const cols = spec.cols ?? 4
      return (
        <div key={key} className="qycw-skel-card" style={{ padding: 0, overflow: "hidden" }}>
          <div
            style={{
              padding: "16px 20px",
              borderBottom: "1px solid #f1f5f9",
              display: "grid",
              gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
              gap: "16px",
            }}
          >
            {Array.from({ length: cols }).map((_, i) => bar({ width: "70%", height: "12px" }, i))}
          </div>
          {Array.from({ length: rows }).map((_, r) => (
            <div
              key={r}
              style={{
                padding: "14px 20px",
                borderBottom: r === rows - 1 ? "none" : "1px solid #f8fafc",
                display: "grid",
                gridTemplateColumns: `repeat(${cols}, minmax(0, 1fr))`,
                gap: "16px",
              }}
            >
              {Array.from({ length: cols }).map((_, c) =>
                bar({ width: c === 0 ? "60%" : "85%", height: "12px" }, c),
              )}
            </div>
          ))}
        </div>
      )
    }

    case "grid": {
      const gap = `${spec.gap ?? 16}px`
      return (
        <div
          key={key}
          style={{
            display: "grid",
            gap,
            gridTemplateColumns: `repeat(${spec.cols}, minmax(0, 1fr))`,
          }}
        >
          {Array.from({ length: spec.count }).map((_, i) => (
            <div key={i}>{renderNode(spec.item, i)}</div>
          ))}
        </div>
      )
    }

    case "stack":
      return (
        <div
          key={key}
          style={{ display: "flex", flexDirection: "column", gap: `${spec.gap ?? 16}px` }}
        >
          {spec.children.map((c, i) => renderNode(c, i))}
        </div>
      )

    case "row":
      return (
        <div
          key={key}
          style={{
            display: "flex",
            gap: `${spec.gap ?? 16}px`,
            alignItems: spec.align === "center" ? "center" : "flex-start",
          }}
        >
          {spec.children.map((c, i) => (
            <div key={i} style={{ flex: 1, minWidth: 0 }}>
              {renderNode(c, i)}
            </div>
          ))}
        </div>
      )

    case "split": {
      const ratio = spec.ratio ?? "2:1"
      const [a, b] = ratio.split(":").map(Number)
      return (
        <div
          key={key}
          style={{
            display: "grid",
            gap: `${spec.gap ?? 24}px`,
            gridTemplateColumns: `${a}fr ${b}fr`,
          }}
        >
          {spec.children.slice(0, 2).map((c, i) => (
            <div key={i} style={{ minWidth: 0 }}>
              {renderNode(c, i)}
            </div>
          ))}
        </div>
      )
    }

    default:
      return null
  }
}

interface PageSkeletonProps {
  loading: boolean
  spec: SkeletonSpec | SkeletonSpec[]
  gap?: number
  children: ReactNode
}

export function PageSkeleton({ loading, spec, gap = 32, children }: PageSkeletonProps) {
  useEffect(() => {
    ensureCss()
  }, [])

  if (!loading) return <>{children}</>

  const specs = Array.isArray(spec) ? spec : [spec]
  return (
    <div
      className="qycw-skel-wrap"
      style={{ display: "flex", flexDirection: "column", gap: `${gap}px` }}
      aria-busy="true"
      aria-live="polite"
    >
      {specs.map((s, i) => renderNode(s, i))}
    </div>
  )
}

export function SkeletonBlock({ spec }: { spec: SkeletonSpec | SkeletonSpec[] }) {
  useEffect(() => {
    ensureCss()
  }, [])
  const specs = Array.isArray(spec) ? spec : [spec]
  return <>{specs.map((s, i) => renderNode(s, i))}</>
}
