import { Activity, BarChart3, Database, History, LineChart, MonitorCog } from "lucide-react";
import { AnimatePresence, MotionConfig, motion } from "motion/react";
import type { ReactNode } from "react";
import { cn } from "@/shared/ui/cn";
import { StatusIcon } from "@/shared/ui/status";
import { normalizeSafeChartData, parseChartSpec, type SafeChartData, type SafeChartSeries, type SafeChartSpec } from "../chart-data";
import type { AgentUiResult } from "../types";
import type { UIEnvelope } from "../ui-envelope";
import {
  agentLayoutTransition,
  agentPanelItemVariants,
  agentPanelListVariants
} from "./agent-motion";

type AgentUiEnvelopeRendererProps = {
  results: AgentUiResult[];
};

export function AgentUiEnvelopeRenderer({ results }: AgentUiEnvelopeRendererProps) {
  if (results.length === 0) {
    return null;
  }

  return (
    <MotionConfig reducedMotion="user">
      <motion.div className="space-y-3" variants={agentPanelListVariants} animate="animate">
        <AnimatePresence initial={false} mode="popLayout">
          {results.map((result) => (
            <motion.div
              key={result.id}
              initial="initial"
              animate="animate"
              exit="exit"
              variants={agentPanelItemVariants}
              transition={agentLayoutTransition}
            >
              <AgentUiResultCard result={result} />
            </motion.div>
          ))}
        </AnimatePresence>
      </motion.div>
    </MotionConfig>
  );
}

function AgentUiResultCard({ result }: { result: AgentUiResult }) {
  const { envelope } = result;

  return (
    <div className="agent-panel-message w-full rounded-2xl p-3 text-slate-700 shadow-xs">
      <div className="mb-3 flex items-center justify-between gap-2">
        <div className="flex min-w-0 items-center gap-2">
          <span className="grid h-8 w-8 shrink-0 place-items-center rounded-xl bg-blue-100 text-blue-700">
            {getEnvelopeIcon(envelope)}
          </span>
          <div className="min-w-0">
            <p className="truncate text-sm font-semibold text-slate-950">{getEnvelopeTitle(envelope)}</p>
            <p className="text-xs text-slate-500">{getSurfaceLabel(envelope.surface)}</p>
          </div>
        </div>
        <span className="shrink-0 rounded-full bg-slate-100 px-2 py-1 text-xs font-semibold text-slate-600">
          agent-ui@1
        </span>
      </div>

      {envelope.kind === "chart" ? <SafeChartEnvelope envelope={envelope} /> : null}
      {envelope.kind === "registered_component" ? <RegisteredComponentEnvelope envelope={envelope} /> : null}
      {envelope.kind === "map_action" ? null : null}
    </div>
  );
}

function SafeChartEnvelope({ envelope }: { envelope: Extract<UIEnvelope, { kind: "chart" }> }) {
  const spec = parseChartSpec(envelope.spec);
  const data = normalizeSafeChartData(envelope.data);

  if (!data || data.series.length === 0 || data.xData.length === 0) {
    return <FallbackText text={envelope.fallbackText ?? "图表数据未通过前端安全子集校验。"} />;
  }

  return (
    <div className="agent-panel-nested rounded-xl p-3">
      <div className="mb-3 flex items-start justify-between gap-3">
        <div className="min-w-0">
          <p className="truncate text-sm font-semibold text-slate-950" title={spec.title}>
            {spec.title ?? "Agent 图表"}
          </p>
          <p className="mt-1 text-xs text-slate-500">
            {spec.chartType === "bar" ? "柱状图" : "折线图"}
            {spec.yAxisName ? ` · ${spec.yAxisName}` : ""}
          </p>
        </div>
        <div className="flex shrink-0 flex-wrap justify-end gap-1.5">
          {data.series.slice(0, 3).map((series, index) => (
            <span key={series.name} className="flex items-center gap-1 text-xs text-slate-600">
              <span className={cn("h-2 w-2 rounded-full", chartColorClass(index))} />
              {series.name}
            </span>
          ))}
        </div>
      </div>
      <SafeChartSvg spec={spec} data={data} />
      {spec.xAxisName ? <p className="mt-2 text-center text-xs text-slate-500">{spec.xAxisName}</p> : null}
    </div>
  );
}

function SafeChartSvg({ spec, data }: { spec: SafeChartSpec; data: SafeChartData }) {
  const width = 360;
  const height = 190;
  const padding = { top: 16, right: 18, bottom: 32, left: 38 };
  const plotWidth = width - padding.left - padding.right;
  const plotHeight = height - padding.top - padding.bottom;
  const values = data.series.flatMap((series) => series.data);
  const min = Math.min(0, ...values);
  const max = Math.max(...values);
  const range = max - min || 1;
  const xFor = (index: number) =>
    padding.left + (data.xData.length === 1 ? plotWidth / 2 : (index / (data.xData.length - 1)) * plotWidth);
  const yFor = (value: number) => padding.top + plotHeight - ((value - min) / range) * plotHeight;
  const ticks = createTicks(min, max);

  return (
    <svg viewBox={`0 0 ${width} ${height}`} role="img" aria-label={spec.title ?? "Agent 图表"} className="h-48 w-full">
      <rect x="0" y="0" width={width} height={height} rx="12" className="fill-white/70" />
      {ticks.map((tick) => {
        const y = yFor(tick);
        return (
          <g key={tick}>
            <line x1={padding.left} x2={width - padding.right} y1={y} y2={y} className="stroke-slate-200" />
            <text x={padding.left - 8} y={y + 4} textAnchor="end" className="fill-slate-400 text-xs">
              {formatChartNumber(tick)}
            </text>
          </g>
        );
      })}
      <line x1={padding.left} x2={padding.left} y1={padding.top} y2={height - padding.bottom} className="stroke-slate-300" />
      <line x1={padding.left} x2={width - padding.right} y1={height - padding.bottom} y2={height - padding.bottom} className="stroke-slate-300" />
      {data.xData.map((label, index) => (
        <text
          key={`${label}-${index}`}
          x={xFor(index)}
          y={height - 12}
          textAnchor="middle"
          className="fill-slate-500 text-xs"
        >
          {shortLabel(label)}
        </text>
      ))}
      {data.series.slice(0, 4).map((series, seriesIndex) =>
        (series.type ?? spec.chartType) === "bar" ? (
          <BarSeries
            key={series.name}
            series={series}
            seriesIndex={seriesIndex}
            seriesCount={Math.min(data.series.length, 4)}
            xFor={xFor}
            yFor={yFor}
            baselineY={yFor(0)}
          />
        ) : (
          <LineSeries key={series.name} series={series} seriesIndex={seriesIndex} xFor={xFor} yFor={yFor} />
        )
      )}
    </svg>
  );
}

function LineSeries({
  series,
  seriesIndex,
  xFor,
  yFor
}: {
  series: SafeChartSeries;
  seriesIndex: number;
  xFor: (index: number) => number;
  yFor: (value: number) => number;
}) {
  const points = series.data.map((value, index) => `${xFor(index)},${yFor(value)}`).join(" ");
  const stroke = chartStroke(seriesIndex);

  return (
    <g>
      <polyline
        points={points}
        fill="none"
        stroke={stroke}
        strokeWidth="2.5"
        strokeLinecap="round"
        strokeLinejoin="round"
        className="agent-chart-line"
      />
      {series.data.map((value, index) => (
        <circle
          key={`${series.name}-${index}`}
          cx={xFor(index)}
          cy={yFor(value)}
          r="2.8"
          fill={stroke}
          className="agent-chart-point"
          style={{ animationDelay: `${80 + index * 18}ms` }}
        />
      ))}
    </g>
  );
}

function BarSeries({
  series,
  seriesIndex,
  seriesCount,
  xFor,
  yFor,
  baselineY
}: {
  series: SafeChartSeries;
  seriesIndex: number;
  seriesCount: number;
  xFor: (index: number) => number;
  yFor: (value: number) => number;
  baselineY: number;
}) {
  const fill = chartStroke(seriesIndex);
  const barWidth = Math.max(5, 22 / seriesCount);
  const offset = (seriesIndex - (seriesCount - 1) / 2) * (barWidth + 2);

  return (
    <g>
      {series.data.map((value, index) => {
        const y = yFor(value);
        return (
          <rect
            key={`${series.name}-${index}`}
            x={xFor(index) - barWidth / 2 + offset}
            y={Math.min(y, baselineY)}
            width={barWidth}
            height={Math.max(1, Math.abs(baselineY - y))}
            rx="2"
            fill={fill}
            className="agent-chart-bar"
            style={{ animationDelay: `${seriesIndex * 28 + index * 16}ms` }}
          />
        );
      })}
    </g>
  );
}

function RegisteredComponentEnvelope({
  envelope
}: {
  envelope: Extract<UIEnvelope, { kind: "registered_component" }>;
}) {
  if (envelope.component === "HistoryPanel") {
    return (
      <TrustedPanelShell icon={<History size={15} aria-hidden="true" />} title="历史数据面板">
        <PropRows props={envelope.props} labels={{ feature_id: "资产 ID", featureId: "资产 ID", metric: "指标", range: "时间范围" }} />
      </TrustedPanelShell>
    );
  }

  if (envelope.component === "ScadaPanel") {
    return (
      <TrustedPanelShell icon={<MonitorCog size={15} aria-hidden="true" />} title="SCADA 面板">
        <PropRows props={envelope.props} labels={{ device_id: "设备 ID", deviceId: "设备 ID", metric: "指标", range: "时间范围" }} />
      </TrustedPanelShell>
    );
  }

  return <FallbackText text={envelope.fallbackText ?? "当前注册组件未接入渲染器。"} />;
}

function TrustedPanelShell({
  icon,
  title,
  children
}: {
  icon: ReactNode;
  title: string;
  children: ReactNode;
}) {
  return (
    <div className="agent-panel-nested rounded-xl p-3">
      <div className="mb-3 flex items-center gap-2 text-sm font-semibold text-slate-950">
        <StatusIcon tone="success" size="md">{icon}</StatusIcon>
        {title}
      </div>
      {children}
    </div>
  );
}

function PropRows({ props, labels }: { props: unknown; labels: Record<string, string> }) {
  const rows = safePropRows(props, labels);

  if (rows.length === 0) {
    return <p className="text-sm text-slate-600">已打开可信面板，暂无可展示参数。</p>;
  }

  return (
    <dl className="grid grid-cols-2 gap-2">
      {rows.map((row) => (
        <div key={row.label} className="surface-reading min-w-0 rounded-lg px-2.5 py-2">
          <dt className="truncate text-xs text-slate-500">{row.label}</dt>
          <dd className="mt-1 truncate text-sm font-semibold text-slate-900" title={row.value}>
            {row.value}
          </dd>
        </div>
      ))}
    </dl>
  );
}

function FallbackText({ text }: { text: string }) {
  return (
    <div className="agent-panel-nested rounded-xl p-3 text-sm leading-6 text-slate-600">
      {text}
    </div>
  );
}

function safePropRows(props: unknown, labels: Record<string, string>) {
  if (!isRecord(props)) {
    return [];
  }

  return Object.entries(labels).flatMap(([key, label]) => {
    const value = props[key];
    if (typeof value !== "string" && typeof value !== "number" && typeof value !== "boolean") {
      return [];
    }

    return [{ label, value: String(value) }];
  });
}

function getEnvelopeIcon(envelope: UIEnvelope) {
  if (envelope.kind === "chart") {
    return envelope.spec && isRecord(envelope.spec) && envelope.spec.chart_type === "bar" ? (
      <BarChart3 size={16} aria-hidden="true" />
    ) : (
      <LineChart size={16} aria-hidden="true" />
    );
  }

  if (envelope.kind === "registered_component") {
    return <Database size={16} aria-hidden="true" />;
  }

  return <Activity size={16} aria-hidden="true" />;
}

function getEnvelopeTitle(envelope: UIEnvelope) {
  if (envelope.kind === "chart") {
    const spec = parseChartSpec(envelope.spec);
    return spec.title ?? "Agent 图表";
  }

  if (envelope.kind === "registered_component") {
    return envelope.component;
  }

  return envelope.action;
}

function getSurfaceLabel(surface: UIEnvelope["surface"]) {
  if (surface === "chat_inline") return "聊天内联展示";
  if (surface === "side_panel") return "侧栏展示";
  if (surface === "canvas") return "画布展示";
  return "地图叠加";
}

function createTicks(min: number, max: number) {
  const range = max - min || 1;
  return [max, min + range * 0.5, min];
}

function chartStroke(index: number) {
  return ["#2563eb", "#0aa6a6", "#ff7a45", "#0477bf"][index % 4] ?? "#2563eb";
}

function chartColorClass(index: number) {
  return ["bg-blue-600", "bg-teal-600", "bg-orange-500", "bg-sky-700"][index % 4] ?? "bg-blue-600";
}

function formatChartNumber(value: number) {
  if (Math.abs(value) >= 100) {
    return value.toFixed(0);
  }

  if (Math.abs(value) >= 10) {
    return value.toFixed(1);
  }

  return value.toFixed(2);
}

function shortLabel(value: string) {
  return value.length > 8 ? `${value.slice(0, 7)}...` : value;
}

function isRecord(value: unknown): value is Record<string, unknown> {
  return typeof value === "object" && value !== null;
}
