import { useCallback, useEffect, useLayoutEffect, useRef, useState, type ReactNode } from "react";
import { createPortal } from "react-dom";
import { AppIcon } from "./PixelIcons";
import type { AIFocus } from "../lib/ai";
import { useAI } from "../context/AIContext";

export interface TipContext {
  percentile?: number;
  level?: string;
  tone?: string;
  rangeMin?: number;
  rangeMax?: number;
  rangeYears?: number;
  summary?: string;
  number?: string;
  history?: string;
  affects?: string;
}

export interface Tip {
  what?: string;
  how?: string;
  impact?: string;
  context?: string | TipContext;
  source?: string;
  freq?: string;
}

export function formatTipContext(context?: Tip["context"]): string | null {
  if (!context) return null;
  if (typeof context === "string") return context;
  return context.summary || context.history || context.number || null;
}

/** 避免 API 回傳巢狀物件時 React 直接渲染崩潰 */
export function formatDisplayText(node: ReactNode): ReactNode {
  if (node == null || typeof node === "boolean") return node;
  if (typeof node === "string" || typeof node === "number") return node;
  if (Array.isArray(node)) return node.map(formatDisplayText) as ReactNode;
  if (typeof node === "object") {
    const text = formatTipContext(node as TipContext);
    if (text) return text;
  }
  return "—";
}

export interface ExplainRow {
  dt: string;
  dd: string;
}

// 「?」說明：滑鼠懸浮（或 focus）即浮出，不佔版面、不需點擊。
// 用 position:fixed + 視窗座標夾住，浮層方向永遠落在螢幕內、不會超出邊界。
// 預設顯示 tip 的「這是什麼/怎麼看/影響什麼」；也可傳 rows 自訂條列。
export function Explain({
  tip,
  label,
  rows,
  align = "left",
  trigger,
  wide,
}: {
  tip?: Tip | null;
  label?: string;
  rows?: ExplainRow[];
  align?: "left" | "right";
  /** 自訂觸發元素（懸浮即浮出說明）；預設為 ? 按鈕 */
  trigger?: ReactNode;
  wide?: boolean;
}) {
  const [open, setOpen] = useState(false);
  const [pos, setPos] = useState<{ left: number; top: number; maxH?: number }>({ left: -9999, top: -9999 });
  const wrapRef = useRef<HTMLSpanElement>(null);
  const tipRef = useRef<HTMLDivElement>(null);
  const closeTimer = useRef<ReturnType<typeof setTimeout> | null>(null);

  const keepOpen = useCallback(() => {
    if (closeTimer.current) {
      clearTimeout(closeTimer.current);
      closeTimer.current = null;
    }
    setOpen(true);
  }, []);

  const scheduleClose = useCallback(() => {
    if (closeTimer.current) clearTimeout(closeTimer.current);
    closeTimer.current = setTimeout(() => setOpen(false), 140);
  }, []);

  useEffect(() => () => {
    if (closeTimer.current) clearTimeout(closeTimer.current);
  }, []);

  const place = useCallback(() => {
    const btn = wrapRef.current?.querySelector(".explain-trigger, .qbtn") as HTMLElement | null;
    const el = tipRef.current;
    if (!btn || !el) return;
    const m = 8;
    const vw = window.innerWidth;
    const vh = window.innerHeight;
    const r = btn.getBoundingClientRect();
    const width = Math.min(wide ? 360 : 300, vw - m * 2);
    el.style.width = width + "px";
    const h = el.offsetHeight;
    // 水平：依 align 起始，再夾在 [m, vw-width-m] 內
    let left = align === "right" ? r.right - width : r.left;
    left = Math.max(m, Math.min(left, vw - width - m));
    // 垂直：預設在下方；下方空間不足則翻到上方；都不夠就限制高度可捲動
    let top = r.bottom + 8;
    let maxH: number | undefined;
    if (top + h > vh - m) {
      const above = r.top - 8 - h;
      if (above >= m) top = above;
      else maxH = vh - m - top;
    }
    setPos({ left, top, maxH });
  }, [align, wide]);

  useLayoutEffect(() => {
    if (open) place();
  }, [open, place]);

  useEffect(() => {
    if (!open) return;
    const f = () => place();
    window.addEventListener("scroll", f, true);
    window.addEventListener("resize", f);
    return () => {
      window.removeEventListener("scroll", f, true);
      window.removeEventListener("resize", f);
    };
  }, [open, place]);

  const hasRows = !!rows?.length;
  const hasTip = tip && (tip.what || tip.how || tip.impact || formatTipContext(tip.context));
  if (!hasRows && !hasTip) return null;

  const tooltipBody = (
    <>
      {label && <div className="ex-title">{label}</div>}
      {hasRows
        ? rows!.map((r, i) => (
            <div className="ex-row" key={i}>
              <dt>{r.dt}</dt>
              <dd>{r.dd}</dd>
            </div>
          ))
        : (
          <>
            {tip!.what && (
              <div className="ex-row">
                <dt>這是什麼</dt>
                <dd>{tip!.what}</dd>
              </div>
            )}
            {tip!.how && (
              <div className="ex-row">
                <dt>怎麼看</dt>
                <dd>{tip!.how}</dd>
              </div>
            )}
            {tip!.impact && (
              <div className="ex-row">
                <dt>影響什麼</dt>
                <dd>{tip!.impact}</dd>
              </div>
            )}
            {(() => {
              const ctx = formatTipContext(tip!.context);
              return ctx ? (
                <div className="ex-row">
                  <dt>歷史脈絡</dt>
                  <dd>{ctx}</dd>
                </div>
              ) : null;
            })()}
          </>
        )}
      {(tip?.source || tip?.freq) && (
        <div className="ex-src">
          {tip.source && <span>來源：{tip.source}</span>}
          {tip.freq && <span>更新：{tip.freq}</span>}
        </div>
      )}
    </>
  );

  const tooltipEl = open ? (
    <div
      ref={tipRef}
      className={"explain" + (wide ? " explain-wide" : "")}
      role="tooltip"
      onMouseEnter={keepOpen}
      onMouseLeave={scheduleClose}
      style={{
        position: "fixed",
        left: pos.left,
        top: pos.top,
        maxHeight: pos.maxH,
        overflowY: pos.maxH ? "auto" : undefined,
      }}
    >
      {tooltipBody}
    </div>
  ) : null;

  return (
    <span
      className="qtip"
      ref={wrapRef}
      onMouseEnter={keepOpen}
      onMouseLeave={scheduleClose}
      onFocus={keepOpen}
      onBlur={scheduleClose}
    >
      {trigger ? (
        <span className="explain-trigger" tabIndex={0} role="button">
          {trigger}
        </span>
      ) : (
        <button className="qbtn" aria-label="說明" type="button">
          ?
        </button>
      )}
      {typeof document !== "undefined" && tooltipEl ? createPortal(tooltipEl, document.body) : null}
    </span>
  );
}

// 顯著數字統計卡：大字數值 + 變化 + 「?」說明
export function StatCard({
  label,
  labelEn,
  value,
  change,
  dir,
  sub,
  tip,
}: {
  label: string;
  labelEn?: string;
  value: ReactNode;
  change?: ReactNode;
  dir?: "up" | "down" | "flat";
  sub?: ReactNode;
  tip?: Tip | null;
}) {
  return (
    <div className="card stat-card">
      <div className="stat-head">
        <span className="stat-label">
          {label}
          {labelEn && <span className="en">{labelEn}</span>}
        </span>
        <span style={{ marginLeft: "auto" }}>
          <Explain tip={tip} align="right" />
        </span>
      </div>
      <div className="stat-value">{formatDisplayText(value ?? "—")}</div>
      <div className="stat-sub">
        {change != null && change !== "" && (
          <span className={"stat-chg " + (dir || "flat")}>{formatDisplayText(change)}</span>
        )}
        {sub && <span>{formatDisplayText(sub)}</span>}
      </div>
    </div>
  );
}

function CardAskAI({ focus }: { focus?: AIFocus }) {
  const { openAI } = useAI();
  return (
    <button
      type="button"
      className="ai"
      onClick={() =>
        openAI({
          focus: {
            ...focus,
            cardTitle: focus?.cardTitle || focus?.label || (typeof focus?.label === "string" ? focus.label : undefined),
          },
        })
      }
    >
      問 AI
    </button>
  );
}

export function RefreshButton({
  onClick,
  loading,
  label = "重新整理",
  className,
}: {
  onClick: () => void;
  loading?: boolean;
  label?: string;
  className?: string;
}) {
  return (
    <button
      type="button"
      className={`btn-ghost btn-refresh${className ? ` ${className}` : ""}`}
      disabled={loading}
      onClick={onClick}
    >
      {loading ? "更新中…" : label}
    </button>
  );
}

export function Card({
  title,
  ico,
  ai,
  aiFocus,
  onRefresh,
  refreshing,
  refreshLabel,
  className,
  children,
}: {
  title?: ReactNode;
  ico?: ReactNode;
  ai?: boolean;
  aiFocus?: AIFocus;
  onRefresh?: () => void;
  refreshing?: boolean;
  refreshLabel?: string;
  className?: string;
  children: ReactNode;
}) {
  const focusFromTitle =
    typeof title === "string" ? { cardTitle: title, label: title } : aiFocus;
  const showActions = onRefresh || ai;
  return (
    <div className={"card" + (className ? " " + className : "")}>
      {title && (
        <div className="card-title">
          {ico && <span className="ico ico-svg">{ico}</span>}
          {title}
          {showActions ? (
            <span className="card-title-actions">
              {onRefresh ? (
                <RefreshButton onClick={onRefresh} loading={refreshing} label={refreshLabel} />
              ) : null}
              {ai ? <CardAskAI focus={aiFocus || focusFromTitle} /> : null}
            </span>
          ) : null}
        </div>
      )}
      {children}
    </div>
  );
}

export function Tag({ tone, children }: { tone?: "up" | "down" | "cool" | "gold"; children: ReactNode }) {
  return <span className={"tag" + (tone ? " " + tone : "")}>{children}</span>;
}

export function Loading({ label = "讀取中…" }: { label?: string }) {
  return (
    <div className="state-box pixel state-box--icon">
      <AppIcon name="hourglass" size={24} framed variant="nav" />
      <span>{label}</span>
    </div>
  );
}

export function ErrorState({ title = "讀取失敗", detail }: { title?: string; detail?: string }) {
  return (
    <div className="state-box state-error state-box--icon">
      <div className="pixel state-box-head">
        <AppIcon name="warning" size={24} framed variant="nav" />
        {title}
      </div>
      {detail && <div className="small muted mt-s">{detail}</div>}
    </div>
  );
}

export function MissingKey({ message, hint }: { message?: string; hint?: string }) {
  return (
    <div className="state-box state-key state-box--icon">
      <div className="pixel state-box-head">
        <AppIcon name="key" size={24} framed variant="nav" />
        尚未設定資料金鑰
      </div>
      <div className="small muted mt-s">
        {message || "總經資料需要免費的 FRED API Key。請到後端設定後重新整理。"}
      </div>
      {hint && (
        <div className="small mt-s">
          <a href={hint} target="_blank" rel="noreferrer">
            申請免費金鑰 →
          </a>
        </div>
      )}
    </div>
  );
}
