import {
  ChevronDown,
  ChevronRight,
  Save,
  RotateCcw,
  AlertTriangle,
  CheckCircle2,
  History,
  Gauge,
  SlidersHorizontal,
  Search,
  X,
  Beaker,
} from "lucide-react";
import { useState, useEffect, useCallback, useMemo, useRef, memo } from "react";
import type {
  ParamDef,
  ParamGroup,
  ParamsSummary,
  StrategyVersion,
  ParamUpdate,
  InstanceHealth,
  StrategyPreset,
} from "../types/strategy";
import {
  fetchParamsSchema,
  fetchStrategyParams,
  applyParams,
  fetchStrategyHistory,
  fetchRiskBudget,
  fetchPresets,
  applyPreset,
} from "../api/strategyApi";

// ═══════════════════════════════════════════════════════════════════════════
// Utilities
// ═══════════════════════════════════════════════════════════════════════════

const FLOAT_EPSILON = 1e-9;

function isFloatDirty(a: unknown, b: unknown): boolean {
  if (typeof a === "number" && typeof b === "number") {
    return Math.abs(a - b) > FLOAT_EPSILON;
  }
  return a !== b;
}

function safeEvalCondition(
  expr: string | null,
  ctx: Record<string, unknown>,
): boolean {
  if (!expr) return true;
  try {
    // Convert Python-style expressions to JS-safe equivalents
    const js = expr
      .replace(/\bTrue\b/g, "true")
      .replace(/\bFalse\b/g, "false")
      .replace(/\bNone\b/g, "null")
      .replace(/\band\b/g, "&&")
      .replace(/\bor\b/g, "||")
      .replace(/\bnot\b/g, "!");
    const keys = Object.keys(ctx);
    const vals = Object.values(ctx);
    const fn = new Function(...keys, `"use strict";return Boolean(${js})`);
    return fn(...vals);
  } catch {
    return true;
  }
}

// ═══════════════════════════════════════════════════════════════════════════
// Color palette — consistent with the dashboard design
// ═══════════════════════════════════════════════════════════════════════════

const RISK_COLORS: Record<string, string> = {
  low: "bg-emerald-500/15 text-emerald-400 border-emerald-500/30",
  medium: "bg-amber-500/15 text-amber-400 border-amber-500/30",
  high: "bg-orange-500/15 text-orange-400 border-orange-500/30",
  critical: "bg-red-500/15 text-red-400 border-red-500/30",
};

const RISK_DOT: Record<string, string> = {
  low: "bg-emerald-400",
  medium: "bg-amber-400",
  high: "bg-orange-400",
  critical: "bg-red-400",
};

const GRADE_COLORS: Record<string, string> = {
  A: "text-emerald-400",
  B: "text-lime-400",
  C: "text-amber-400",
  D: "text-orange-400",
  F: "text-red-400",
};

// ═══════════════════════════════════════════════════════════════════════════
// Types
// ═══════════════════════════════════════════════════════════════════════════

interface StrategyCustomizerProps {
  strategyId: string;
  strategyName?: string;
  onClose?: () => void;
}

// ═══════════════════════════════════════════════════════════════════════════
// Sub-components
// ═══════════════════════════════════════════════════════════════════════════

function RiskBadge({ level }: { level: string }) {
  return (
    <span
      className={`inline-flex items-center gap-1 px-1.5 py-0.5 rounded text-[10px] font-medium border ${RISK_COLORS[level] || RISK_COLORS.low}`}
    >
      <span
        className={`w-1.5 h-1.5 rounded-full ${RISK_DOT[level] || RISK_DOT.low}`}
      />
      {level === "critical"
        ? "致命"
        : level === "high"
          ? "高"
          : level === "medium"
            ? "中"
            : "低"}
    </span>
  );
}

function ParamField({
  def,
  value,
  onChange,
  disabled,
}: {
  def: ParamDef;
  value: unknown;
  onChange: (key: string, value: unknown) => void;
  disabled?: boolean;
}) {
  const [editingNumeric, setEditingNumeric] = useState(false);
  const [draftValue, setDraftValue] = useState("");
  const debounceRef = useRef<ReturnType<typeof setTimeout>>(undefined);

  const emit = useCallback(
    (v: unknown) => {
      if (
        def.type === "int" ||
        def.type === "float" ||
        def.type === "float_range"
      ) {
        clearTimeout(debounceRef.current);
        debounceRef.current = setTimeout(() => onChange(def.key, v), 16);
      } else {
        onChange(def.key, v);
      }
    },
    [def.key, def.type, onChange],
  );

  useEffect(() => {
    return () => clearTimeout(debounceRef.current);
  }, []);

  const handleChange = (v: unknown) => emit(v);

  if (def.type === "bool") {
    return (
      <label className="inline-flex items-center gap-2 cursor-pointer">
        <input
          type="checkbox"
          checked={Boolean(value)}
          onChange={(e) => handleChange(e.target.checked)}
          disabled={disabled}
          className="w-4 h-4 rounded border-zinc-600 bg-zinc-800 accent-amber-500"
        />
        <span className="text-xs text-zinc-400">{value ? "启用" : "禁用"}</span>
      </label>
    );
  }

  if (def.type === "string_choice" && def.choices.length > 0) {
    return (
      <select
        value={String(value ?? def.choices[0])}
        onChange={(e) => handleChange(e.target.value)}
        disabled={disabled}
        className="w-full bg-zinc-800 border border-zinc-700 rounded px-2 py-1 text-xs text-zinc-200
                   focus:border-amber-500/50 focus:outline-none disabled:opacity-40"
      >
        {def.choices.map((c) => (
          <option key={c} value={c}>
            {c}
          </option>
        ))}
      </select>
    );
  }

  if (def.type === "int") {
    const v = Number(value ?? def.default);

    const commitNumeric = () => {
      setEditingNumeric(false);
      const parsed = Number(draftValue);
      if (!isNaN(parsed)) {
        const clamped = Math.max(
          def.min_val ?? -Infinity,
          Math.min(def.max_val ?? Infinity, parsed),
        );
        emit(clamped);
      }
    };

    return (
      <div className="flex items-center gap-2">
        <input
          type="range"
          min={def.min_val ?? 0}
          max={def.max_val ?? 100}
          step={def.step ?? 1}
          value={v}
          onChange={(e) => {
            setEditingNumeric(false);
            handleChange(Number(e.target.value));
          }}
          disabled={disabled}
          className="flex-1 h-1 accent-amber-500 disabled:opacity-40"
        />
        {editingNumeric ? (
          <input
            type="number"
            autoFocus
            value={draftValue}
            min={def.min_val ?? undefined}
            max={def.max_val ?? undefined}
            step={def.step ?? 1}
            onChange={(e) => setDraftValue(e.target.value)}
            onBlur={commitNumeric}
            onKeyDown={(e) => {
              if (e.key === "Enter") commitNumeric();
              if (e.key === "Escape") setEditingNumeric(false);
            }}
            className="w-16 text-right text-xs font-mono text-amber-400 tabular-nums
                       bg-zinc-800 border border-amber-500/50 rounded px-1 py-0.5
                       focus:outline-none"
          />
        ) : (
          <button
            onClick={() => {
              setDraftValue(String(v));
              setEditingNumeric(true);
            }}
            disabled={disabled}
            className="w-10 text-right text-xs font-mono text-amber-400 tabular-nums
                       hover:bg-zinc-800 rounded px-0.5 py-0.5 transition-colors
                       disabled:opacity-40"
          >
            {v}
            {def.unit}
          </button>
        )}
      </div>
    );
  }

  // float / float_range
  const v = Number(value ?? def.default);
  const floatPrecision = def.step && def.step >= 0.01 ? 2 : 1;

  const commitFloat = () => {
    setEditingNumeric(false);
    const parsed = Number(draftValue);
    if (!isNaN(parsed)) {
      const clamped = Math.max(
        def.min_val ?? -Infinity,
        Math.min(def.max_val ?? Infinity, parsed),
      );
      emit(clamped);
    }
  };

  return (
    <div className="flex items-center gap-2">
      <input
        type="range"
        min={def.min_val ?? 0}
        max={def.max_val ?? 1}
        step={def.step ?? 0.01}
        value={v}
        onChange={(e) => {
          setEditingNumeric(false);
          handleChange(Number(e.target.value));
        }}
        disabled={disabled}
        className="flex-1 h-1 accent-amber-500 disabled:opacity-40"
      />
      {editingNumeric ? (
        <input
          type="number"
          autoFocus
          value={draftValue}
          min={def.min_val ?? undefined}
          max={def.max_val ?? undefined}
          step={def.step ?? 0.01}
          onChange={(e) => setDraftValue(e.target.value)}
          onBlur={commitFloat}
          onKeyDown={(e) => {
            if (e.key === "Enter") commitFloat();
            if (e.key === "Escape") setEditingNumeric(false);
          }}
          className="w-20 text-right text-xs font-mono text-amber-400 tabular-nums
                     bg-zinc-800 border border-amber-500/50 rounded px-1 py-0.5
                     focus:outline-none"
        />
      ) : (
        <button
          onClick={() => {
            setDraftValue(String(v));
            setEditingNumeric(true);
          }}
          disabled={disabled}
          className="w-14 text-right text-xs font-mono text-amber-400 tabular-nums
                     hover:bg-zinc-800 rounded px-0.5 py-0.5 transition-colors
                     disabled:opacity-40"
        >
          {v.toFixed(floatPrecision)}
          {def.unit}
        </button>
      )}
    </div>
  );
}

const ParamRow = memo(function ParamRow({
  def,
  value,
  dirty,
  onChange,
  onRevert,
  disabled,
}: {
  def: ParamDef;
  value: unknown;
  dirty: boolean;
  onChange: (key: string, value: unknown) => void;
  onRevert: (key: string) => void;
  disabled?: boolean;
}) {
  return (
    <div
      className={`group flex flex-col gap-1 py-2 px-3 rounded-md border transition-colors ${
        dirty
          ? "border-amber-500/40 bg-amber-500/5"
          : "border-transparent hover:border-zinc-700/50 hover:bg-zinc-800/30"
      }`}
    >
      <div className="flex items-center justify-between gap-2">
        <div className="flex items-center gap-1.5 min-w-0">
          <span className="text-xs font-medium text-zinc-300 truncate">
            {def.label}
          </span>
          {def.requires_paper_mode && (
            <span title="需 Paper Mode 验证">
              <Beaker className="w-3 h-3 text-purple-400 flex-shrink-0" />
            </span>
          )}
          <RiskBadge level={def.risk_level} />
          {def.advanced && (
            <span className="text-[10px] text-zinc-500 bg-zinc-800 px-1 rounded">
              高级
            </span>
          )}
        </div>
        <div className="flex items-center gap-1.5 flex-shrink-0">
          {dirty && (
            <>
              <button
                onClick={() => onRevert(def.key)}
                className="text-[10px] text-zinc-500 hover:text-zinc-200 bg-zinc-800 hover:bg-zinc-700
                           px-1.5 py-0.5 rounded transition-colors opacity-0 group-hover:opacity-100"
              >
                回退
              </button>
              <span className="text-[10px] text-amber-400 bg-amber-500/10 px-1.5 py-0.5 rounded">
                已修改
              </span>
            </>
          )}
        </div>
      </div>
      <ParamField
        def={def}
        value={value}
        onChange={onChange}
        disabled={disabled}
      />
      {def.description && (
        <p className="text-[10px] text-zinc-500 leading-tight mt-0.5">
          {def.description}
        </p>
      )}
    </div>
  );
});

function ParamGroupPanel({
  meta,
  params,
  values,
  dirtyKeys,
  onChange,
  onRevert,
  disabled,
  expanded,
  onToggle,
}: {
  meta: ParamGroup;
  params: ParamDef[];
  values: Record<string, unknown>;
  dirtyKeys: Set<string>;
  onChange: (key: string, value: unknown) => void;
  onRevert: (key: string) => void;
  disabled?: boolean;
  expanded: boolean;
  onToggle: () => void;
}) {
  const visibleParams = params.filter((p) => {
    if (!p.visible_when) return true;
    return safeEvalCondition(p.visible_when, values);
  });

  if (visibleParams.length === 0) return null;

  const dirtyCount = visibleParams.filter((p) => dirtyKeys.has(p.key)).length;

  return (
    <div className="border border-zinc-800 rounded-lg overflow-hidden">
      <button
        onClick={onToggle}
        className="w-full flex items-center gap-2 px-4 py-2.5 bg-zinc-900 hover:bg-zinc-800/70 transition-colors text-left"
      >
        {expanded ? (
          <ChevronDown className="w-4 h-4 text-zinc-400" />
        ) : (
          <ChevronRight className="w-4 h-4 text-zinc-400" />
        )}
        <span className="text-sm font-medium text-zinc-200">{meta.label}</span>
        {dirtyCount > 0 && (
          <span className="text-[10px] text-amber-400 bg-amber-500/10 px-1.5 py-0.5 rounded-full">
            {dirtyCount}
          </span>
        )}
        <span className="text-[10px] text-zinc-500 ml-auto">
          {visibleParams.length} 项
        </span>
      </button>
      {expanded && (
        <div className="px-2 pb-2 divide-y divide-zinc-800/50">
          <p className="text-[10px] text-zinc-500 px-3 py-1.5">
            {meta.description}
          </p>
          {visibleParams.map((p) => (
            <ParamRow
              key={p.key}
              def={p}
              value={values[p.key] ?? p.default}
              dirty={dirtyKeys.has(p.key)}
              onChange={onChange}
              onRevert={onRevert}
              disabled={disabled}
            />
          ))}
        </div>
      )}
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════════════
// Summary Bar
// ═══════════════════════════════════════════════════════════════════════════

function ChangesSummaryBar({
  summary,
  dirtyCount,
  onSave,
  onReset,
  saving,
  validationErrors,
}: {
  summary: ParamsSummary | null;
  dirtyCount: number;
  onSave: () => void;
  onReset: () => void;
  saving: boolean;
  validationErrors: Record<string, string[]> | null;
}) {
  if (dirtyCount === 0) return null;

  return (
    <div className="sticky bottom-0 left-0 right-0 z-20 bg-zinc-950 border-t border-zinc-800 px-4 py-3">
      <div className="flex items-center justify-between gap-3">
        <div className="flex items-center gap-3">
          <span className="text-sm text-amber-400 font-medium">
            {dirtyCount} 处修改待保存
          </span>
          {summary && (
            <div className="flex items-center gap-2 text-[10px] text-zinc-400">
              {summary.critical_risk > 0 && (
                <span className="text-red-400 bg-red-500/10 px-1.5 py-0.5 rounded">
                  {summary.critical_risk} 致命
                </span>
              )}
              {summary.high_risk > 0 && (
                <span className="text-orange-400 bg-orange-500/10 px-1.5 py-0.5 rounded">
                  {summary.high_risk} 高
                </span>
              )}
              {summary.requires_paper_mode && (
                <span className="text-purple-400 bg-purple-500/10 px-1.5 py-0.5 rounded flex items-center gap-1">
                  <Beaker className="w-3 h-3" />需 Paper Mode
                </span>
              )}
            </div>
          )}
          {validationErrors && Object.keys(validationErrors).length > 0 && (
            <span className="text-xs text-red-400 flex items-center gap-1">
              <AlertTriangle className="w-3 h-3" />
              校验未通过
            </span>
          )}
        </div>
        <div className="flex items-center gap-2">
          <button
            onClick={onReset}
            className="flex items-center gap-1.5 px-3 py-1.5 text-xs text-zinc-400 hover:text-zinc-200
                       bg-zinc-800 hover:bg-zinc-700 rounded-md transition-colors"
          >
            <RotateCcw className="w-3.5 h-3.5" />
            放弃
          </button>
          <button
            onClick={onSave}
            disabled={saving}
            className="flex items-center gap-1.5 px-4 py-1.5 text-xs font-medium text-zinc-950
                       bg-amber-500 hover:bg-amber-400 disabled:opacity-50 rounded-md transition-colors"
          >
            <Save className="w-3.5 h-3.5" />
            {saving ? "保存中..." : "应用变更"}
          </button>
        </div>
      </div>
    </div>
  );
}

// ═══════════════════════════════════════════════════════════════════════════
// Main Component
// ═══════════════════════════════════════════════════════════════════════════

export default function StrategyCustomizer({
  strategyId,
  strategyName,
  onClose,
}: StrategyCustomizerProps) {
  // Data
  const [schema, setSchema] = useState<Record<string, ParamGroup>>({});
  const [sortedGroups, setSortedGroups] = useState<ParamGroup[]>([]);
  const [originalParams, setOriginalParams] = useState<Record<string, unknown>>(
    {},
  );
  const [currentParams, setCurrentParams] = useState<Record<string, unknown>>(
    {},
  );
  const [loading, setLoading] = useState(true);
  const [saving, setSaving] = useState(false);
  const [error, setError] = useState<string | null>(null);

  // UI state
  const [activeTab, setActiveTab] = useState<
    "params" | "presets" | "history" | "health"
  >("params");
  const [searchQuery, setSearchQuery] = useState("");
  const [showAdvanced, setShowAdvanced] = useState(false);
  const [changeNote, setChangeNote] = useState("");
  const [saveMessage, setSaveMessage] = useState<string | null>(null);
  const [validationErrors, setValidationErrors] = useState<Record<
    string,
    string[]
  > | null>(null);
  const [expandedGroups, setExpandedGroups] = useState<Set<string>>(new Set());
  const [confirmCritical, setConfirmCritical] = useState(false);

  // History
  const [versions, setVersions] = useState<StrategyVersion[]>([]);
  const [historyLoading, setHistoryLoading] = useState(false);

  // Health
  const [healthScores, setHealthScores] = useState<
    Record<string, InstanceHealth>
  >({});
  const [votingWeights, setVotingWeights] = useState<Record<string, number>>(
    {},
  );
  const [presets, setPresets] = useState<StrategyPreset[]>([]);
  const [presetsLoading, setPresetsLoading] = useState(false);

  // Derived
  const dirtyKeys = useMemo(() => {
    const keys = new Set<string>();
    for (const [key, val] of Object.entries(currentParams)) {
      if (isFloatDirty(val, originalParams[key])) {
        keys.add(key);
      }
    }
    return keys;
  }, [currentParams, originalParams]);

  const dirtyParams: ParamUpdate[] = useMemo(() => {
    return [...dirtyKeys].map((key) => ({ key, value: currentParams[key] }));
  }, [dirtyKeys, currentParams]);

  const changesSummary: ParamsSummary | null = useMemo(() => {
    if (dirtyParams.length === 0) return null;
    // Build client-side summary
    let low = 0,
      medium = 0,
      high = 0,
      critical = 0;
    const groups = new Set<string>();
    let needsPaper = false;
    for (const dp of dirtyParams) {
      for (const group of Object.values(schema)) {
        const pd = group.params.find((p) => p.key === dp.key);
        if (pd) {
          groups.add(pd.group);
          if (pd.risk_level === "low") low++;
          else if (pd.risk_level === "medium") medium++;
          else if (pd.risk_level === "high") high++;
          else if (pd.risk_level === "critical") critical++;
          if (pd.requires_paper_mode) needsPaper = true;
          break;
        }
      }
    }
    return {
      total_params: dirtyParams.length,
      low_risk: low,
      medium_risk: medium,
      high_risk: high,
      critical_risk: critical,
      groups_touched: [...groups],
      requires_paper_mode: needsPaper,
    };
  }, [dirtyParams, schema]);

  // Load schema + initial params
  const loadData = useCallback(async () => {
    setLoading(true);
    setError(null);
    try {
      const [schemaRes, paramsRes] = await Promise.all([
        fetchParamsSchema(),
        fetchStrategyParams(strategyId),
      ]);
      if (!schemaRes.success)
        throw new Error(schemaRes.error || "Failed to load schema");
      if (!paramsRes.success)
        throw new Error(paramsRes.error || "Failed to load params");

      setSchema(schemaRes.groups);
      const sorted = Object.values(schemaRes.groups).sort(
        (a, b) => a.order - b.order,
      );
      setSortedGroups(sorted);
      // Init expanded groups (first 4 groups open by default if not already set)
      setExpandedGroups((prev) => {
        if (prev.size > 0) return prev;
        return new Set(sorted.filter((g) => g.order <= 4).map((g) => g.key));
      });

      const params = paramsRes.params;
      setOriginalParams({ ...params });

      // Restore unsaved draft from localStorage
      const draftKey = `strategy_draft_${strategyId}`;
      try {
        const draftJson = localStorage.getItem(draftKey);
        if (draftJson) {
          const draft = JSON.parse(draftJson);
          if (draft.strategyId === strategyId) {
            setCurrentParams({ ...params, ...draft.params });
            if (draft.changeNote) setChangeNote(draft.changeNote);
            return;
          }
        }
      } catch {
        // ignore corrupt draft
      }
      setCurrentParams({ ...params });
    } catch (e) {
      setError(e instanceof Error ? e.message : "Unknown error");
    } finally {
      setLoading(false);
    }
  }, [strategyId]);

  useEffect(() => {
    loadData();
  }, [loadData]);

  // Persist draft to localStorage on changes
  useEffect(() => {
    if (dirtyKeys.size > 0) {
      const draftKey = `strategy_draft_${strategyId}`;
      localStorage.setItem(
        draftKey,
        JSON.stringify({ strategyId, params: currentParams, changeNote }),
      );
    } else {
      localStorage.removeItem(`strategy_draft_${strategyId}`);
    }
  }, [currentParams, changeNote, dirtyKeys.size, strategyId]);

  // History
  const loadHistory = useCallback(async () => {
    setHistoryLoading(true);
    try {
      const res = await fetchStrategyHistory(strategyId);
      if (res.success) setVersions(res.versions || []);
    } catch {
      // ignore
    } finally {
      setHistoryLoading(false);
    }
  }, [strategyId]);

  useEffect(() => {
    if (activeTab === "history") loadHistory();
  }, [activeTab, loadHistory]);

  // Health
  useEffect(() => {
    if (activeTab === "health") {
      fetchRiskBudget().then((res) => {
        if (res.success) {
          setHealthScores(res.health_scores || {});
          setVotingWeights(res.voting_weights || {});
        }
      });
    }
  }, [activeTab]);

  // Presets
  useEffect(() => {
    if (activeTab === "presets") {
      setPresetsLoading(true);
      fetchPresets()
        .then((res) => {
          if (res.success) setPresets(res.presets || []);
        })
        .catch(() => {
          // ignore
        })
        .finally(() => setPresetsLoading(false));
    }
  }, [activeTab]);

  // Handlers
  const handleToggleGroup = useCallback((key: string) => {
    setExpandedGroups((prev) => {
      const next = new Set(prev);
      if (next.has(key)) next.delete(key);
      else next.add(key);
      return next;
    });
  }, []);

  const handleParamChange = useCallback((key: string, value: unknown) => {
    setCurrentParams((prev) => ({ ...prev, [key]: value }));
    setValidationErrors(null);
    setSaveMessage(null);
  }, []);

  const handleParamRevert = useCallback(
    (key: string) => {
      setCurrentParams((prev) => ({
        ...prev,
        [key]: originalParams[key],
      }));
      setValidationErrors(null);
      setSaveMessage(null);
    },
    [originalParams],
  );

  const handleReset = useCallback(() => {
    setCurrentParams({ ...originalParams });
    setValidationErrors(null);
    setSaveMessage(null);
    setChangeNote("");
  }, [originalParams]);

  // Client-side validation
  const clientValidate = useCallback((): Record<string, string[]> | null => {
    const errors: Record<string, string[]> = {};
    for (const dp of dirtyParams) {
      const def = Object.values(schema)
        .flatMap((g) => g.params)
        .find((p) => p.key === dp.key);
      if (!def) continue;

      if (
        def.type === "float" ||
        def.type === "int" ||
        def.type === "float_range"
      ) {
        const v = Number(dp.value);
        if (isNaN(v)) {
          errors[dp.key] = ["必须是数字"];
        } else if (def.min_val !== null && v < def.min_val) {
          errors[dp.key] = [`最小值 ${def.min_val}，当前 ${v}`];
        } else if (def.max_val !== null && v > def.max_val) {
          errors[dp.key] = [`最大值 ${def.max_val}，当前 ${v}`];
        }
      } else if (def.type === "string_choice" && def.choices.length > 0) {
        if (typeof dp.value !== "string" || !def.choices.includes(dp.value)) {
          errors[dp.key] = [`无效选项，可选: ${def.choices.join(", ")}`];
        }
      }
    }
    return Object.keys(errors).length > 0 ? errors : null;
  }, [dirtyParams, schema]);

  const handleSave = useCallback(async () => {
    if (dirtyParams.length === 0) return;

    // Client-side validation
    const clientErrors = clientValidate();
    if (clientErrors) {
      setValidationErrors(clientErrors);
      setSaveMessage("请修正校验错误后再保存");
      return;
    }

    // Confirm dialog for critical-risk changes
    const hasCritical =
      changesSummary?.critical_risk && changesSummary.critical_risk > 0;
    if (hasCritical && !confirmCritical) {
      setConfirmCritical(true);
      return;
    }
    setConfirmCritical(false);

    setSaving(true);
    setSaveMessage(null);
    setValidationErrors(null);

    try {
      const res = await applyParams(
        strategyId,
        dirtyParams,
        changeNote || undefined,
      );
      if (res.success) {
        setOriginalParams({ ...currentParams });
        setSaveMessage(res.message);
        setChangeNote("");
        localStorage.removeItem(`strategy_draft_${strategyId}`);
      } else {
        setValidationErrors(res.validation_errors || null);
        setSaveMessage(res.error || "保存失败");
      }
    } catch (e) {
      setSaveMessage(e instanceof Error ? e.message : "保存失败");
    } finally {
      setSaving(false);
    }
  }, [
    strategyId,
    dirtyParams,
    changeNote,
    currentParams,
    clientValidate,
    changesSummary,
    confirmCritical,
  ]);

  // Filter params by search
  const filteredGroups = useMemo(() => {
    if (!searchQuery.trim()) return sortedGroups;

    const q = searchQuery.toLowerCase();
    return sortedGroups
      .map((g) => ({
        ...g,
        params: g.params.filter(
          (p) =>
            p.label.toLowerCase().includes(q) ||
            p.key.toLowerCase().includes(q) ||
            p.description.toLowerCase().includes(q),
        ),
      }))
      .filter((g) => g.params.length > 0);
  }, [sortedGroups, searchQuery]);

  if (loading) {
    return (
      <div className="flex items-center justify-center h-64 text-zinc-500">
        <div className="animate-pulse text-sm">加载策略参数...</div>
      </div>
    );
  }

  if (error) {
    return (
      <div className="flex flex-col items-center justify-center h-64 gap-3">
        <AlertTriangle className="w-8 h-8 text-red-400" />
        <span className="text-sm text-red-400">{error}</span>
        <button
          onClick={loadData}
          className="px-3 py-1.5 text-xs text-zinc-300 bg-zinc-800 hover:bg-zinc-700 rounded-md transition-colors"
        >
          重试
        </button>
      </div>
    );
  }

  return (
    <div className="flex flex-col h-full bg-zinc-950 text-zinc-200">
      {/* Header */}
      <div className="flex items-center justify-between px-4 py-3 border-b border-zinc-800 flex-shrink-0">
        <div className="flex items-center gap-3">
          <SlidersHorizontal className="w-5 h-5 text-amber-400" />
          <div>
            <h2 className="text-base font-semibold text-zinc-100">
              {strategyName || strategyId}
            </h2>
            <p className="text-[11px] text-zinc-500">
              策略参数定制 · {strategyId}
            </p>
          </div>
        </div>
        <div className="flex items-center gap-2">
          {/* Tabs */}
          <div className="flex items-center bg-zinc-900 rounded-lg p-0.5">
            {(
              [
                ["params", "参数"],
                ["presets", "预设"],
                ["history", "历史"],
                ["health", "健康"],
              ] as const
            ).map(([tab, label]) => (
              <button
                key={tab}
                onClick={() => setActiveTab(tab as typeof activeTab)}
                className={`px-3 py-1 text-xs rounded-md transition-colors ${
                  activeTab === tab
                    ? "bg-amber-500/15 text-amber-400"
                    : "text-zinc-500 hover:text-zinc-300"
                }`}
              >
                {label}
              </button>
            ))}
          </div>
          {onClose && (
            <button
              onClick={onClose}
              className="p-1.5 text-zinc-500 hover:text-zinc-300 hover:bg-zinc-800 rounded-md transition-colors"
            >
              <X className="w-4 h-4" />
            </button>
          )}
        </div>
      </div>

      {/* Params Tab */}
      {activeTab === "params" && (
        <>
          {/* Toolbar */}
          <div className="flex items-center gap-3 px-4 py-2 border-b border-zinc-800 flex-shrink-0">
            <div className="relative flex-1 max-w-xs">
              <Search className="absolute left-2 top-1/2 -translate-y-1/2 w-3.5 h-3.5 text-zinc-500" />
              <input
                type="text"
                placeholder="搜索参数..."
                value={searchQuery}
                onChange={(e) => setSearchQuery(e.target.value)}
                className="w-full bg-zinc-900 border border-zinc-700 rounded-md pl-7 pr-3 py-1.5 text-xs
                           text-zinc-200 placeholder-zinc-500 focus:border-amber-500/50 focus:outline-none"
              />
            </div>
            <label className="flex items-center gap-1.5 text-xs text-zinc-400 cursor-pointer">
              <input
                type="checkbox"
                checked={showAdvanced}
                onChange={(e) => setShowAdvanced(e.target.checked)}
                className="w-3.5 h-3.5 rounded border-zinc-600 bg-zinc-800 accent-amber-500"
              />
              高级参数
            </label>
            <span className="text-[10px] text-zinc-600 ml-auto">
              {Object.keys(currentParams).length} 参数
            </span>
          </div>

          {/* Params scroll area */}
          <div className="flex-1 overflow-y-auto px-4 py-3 space-y-3">
            {filteredGroups.map((group) => {
              const displayParams = showAdvanced
                ? group.params
                : group.params.filter((p) => !p.advanced);
              if (displayParams.length === 0) return null;
              return (
                <ParamGroupPanel
                  key={group.key}
                  meta={group}
                  params={displayParams}
                  values={currentParams}
                  dirtyKeys={dirtyKeys}
                  onChange={handleParamChange}
                  onRevert={handleParamRevert}
                  expanded={expandedGroups.has(group.key)}
                  onToggle={() => handleToggleGroup(group.key)}
                />
              );
            })}
            {filteredGroups.length === 0 && (
              <div className="text-center text-xs text-zinc-500 py-8">
                无匹配参数
              </div>
            )}
          </div>

          {/* Change note */}
          {dirtyKeys.size > 0 && (
            <div className="px-4 py-2 border-t border-zinc-800 flex-shrink-0">
              <input
                type="text"
                placeholder="写一句变更说明（便于版本回溯）..."
                value={changeNote}
                onChange={(e) => setChangeNote(e.target.value)}
                className="w-full bg-zinc-900 border border-zinc-700 rounded-md px-3 py-1.5 text-xs
                           text-zinc-200 placeholder-zinc-500 focus:border-amber-500/50 focus:outline-none"
              />
            </div>
          )}

          {/* Save message toast */}
          {saveMessage && (
            <div
              className={`px-4 py-2 text-xs flex items-center gap-2 flex-shrink-0 ${
                validationErrors || saveMessage.includes("失败")
                  ? "bg-red-500/10 text-red-400"
                  : "bg-emerald-500/10 text-emerald-400"
              }`}
            >
              {validationErrors || saveMessage.includes("失败") ? (
                <AlertTriangle className="w-3.5 h-3.5" />
              ) : (
                <CheckCircle2 className="w-3.5 h-3.5" />
              )}
              {saveMessage}
            </div>
          )}

          {/* Validation errors */}
          {validationErrors && Object.keys(validationErrors).length > 0 && (
            <div className="px-4 py-2 border-t border-red-500/20 bg-red-500/5 flex-shrink-0">
              <p className="text-xs text-red-400 font-medium mb-1">
                参数校验错误:
              </p>
              {Object.entries(validationErrors).map(([key, msgs]) => (
                <div key={key} className="text-[10px] text-red-300 ml-2">
                  <span className="font-mono text-red-400">{key}</span>:{" "}
                  {msgs.join(", ")}
                </div>
              ))}
            </div>
          )}

          {/* Bottom bar */}
          <ChangesSummaryBar
            summary={changesSummary}
            dirtyCount={dirtyKeys.size}
            onSave={handleSave}
            onReset={handleReset}
            saving={saving}
            validationErrors={validationErrors}
          />
        </>
      )}

      {/* History Tab */}
      {activeTab === "history" && (
        <div className="flex-1 overflow-y-auto px-4 py-4 space-y-3">
          {historyLoading ? (
            <div className="text-center text-xs text-zinc-500 py-8 animate-pulse">
              加载中...
            </div>
          ) : versions.length === 0 ? (
            <div className="text-center text-xs text-zinc-500 py-8">
              <History className="w-5 h-5 mx-auto mb-2 text-zinc-600" />
              暂无版本历史
            </div>
          ) : (
            versions.map((v, i) => (
              <div
                key={v.version}
                className={`border rounded-lg p-3 ${
                  i === 0
                    ? "border-amber-500/30 bg-amber-500/5"
                    : "border-zinc-800 bg-zinc-900/50"
                }`}
              >
                <div className="flex items-center justify-between mb-1.5">
                  <div className="flex items-center gap-2">
                    <span className="text-sm font-medium text-zinc-200">
                      v{v.version}
                    </span>
                    {i === 0 && (
                      <span className="text-[10px] text-amber-400 bg-amber-500/10 px-1.5 py-0.5 rounded">
                        当前
                      </span>
                    )}
                  </div>
                  <div className="flex items-center gap-2">
                    <span className="text-[10px] text-zinc-500">
                      {v.published_at}
                    </span>
                    {i !== 0 && (
                      <button
                        onClick={() => {
                          if (v.params_changed && v.params_changed.length > 0) {
                            // Restore old values for changed params
                            setCurrentParams((prev) => {
                              const next = { ...prev };
                              for (const k of v.params_changed!) {
                                next[k] = originalParams[k];
                              }
                              return next;
                            });
                            setActiveTab("params");
                            setSaveMessage(
                              `已加载 v${v.version} 的参数，请检查后保存`,
                            );
                          }
                        }}
                        className="text-[10px] text-amber-400 hover:text-amber-300 bg-amber-500/10 hover:bg-amber-500/20
                                   px-2 py-1 rounded transition-colors"
                      >
                        恢复此版本
                      </button>
                    )}
                  </div>
                </div>
                <p className="text-xs text-zinc-400 mb-2">{v.note}</p>
                {v.params_changed && v.params_changed.length > 0 && (
                  <div className="flex flex-wrap gap-1">
                    {v.params_changed.map((k) => (
                      <code
                        key={k}
                        className="text-[10px] bg-zinc-800 text-zinc-300 px-1.5 py-0.5 rounded font-mono"
                      >
                        {k}
                      </code>
                    ))}
                  </div>
                )}
              </div>
            ))
          )}
        </div>
      )}

      {/* Presets Tab */}
      {activeTab === "presets" && (
        <div className="flex-1 overflow-y-auto px-4 py-4 space-y-3">
          {presetsLoading ? (
            <div className="text-center text-xs text-zinc-500 py-8 animate-pulse">
              加载中...
            </div>
          ) : presets.length === 0 ? (
            <div className="text-center text-xs text-zinc-500 py-8">
              暂无预设方案
            </div>
          ) : (
            presets.map((p) => (
              <div
                key={p.id}
                className="border border-zinc-800 rounded-lg p-4 bg-zinc-900/50 hover:border-amber-500/30 transition-colors"
              >
                <div className="flex items-center justify-between mb-2">
                  <div>
                    <h4 className="text-sm font-medium text-zinc-200">
                      {p.name}
                    </h4>
                    <p className="text-[10px] text-zinc-500 mt-0.5">
                      {p.description || "无描述"}
                    </p>
                  </div>
                  <button
                    onClick={async () => {
                      try {
                        const res = await applyPreset(p.id, strategyId);
                        if (res.success) {
                          // Reload params
                          const paramsRes =
                            await fetchStrategyParams(strategyId);
                          if (paramsRes.success) {
                            setOriginalParams({ ...paramsRes.params });
                            setCurrentParams({ ...paramsRes.params });
                            setActiveTab("params");
                            setSaveMessage(
                              `已应用预设 "${p.name}"，请检查参数后保存`,
                            );
                          }
                        }
                      } catch {
                        setSaveMessage("预设应用失败");
                      }
                    }}
                    className="px-3 py-1.5 text-xs font-medium text-zinc-950 bg-amber-500 hover:bg-amber-400
                               rounded-md transition-colors flex-shrink-0"
                  >
                    应用
                  </button>
                </div>
                <div className="text-[10px] text-zinc-500">
                  保存于 {p.saved_at}
                </div>
              </div>
            ))
          )}
        </div>
      )}

      {/* Health Tab */}
      {activeTab === "health" && (
        <div className="flex-1 overflow-y-auto px-4 py-4 space-y-4">
          {Object.entries(healthScores).length === 0 ? (
            <div className="text-center text-xs text-zinc-500 py-8">
              <Gauge className="w-5 h-5 mx-auto mb-2 text-zinc-600" />
              暂无健康数据
            </div>
          ) : (
            <>
              {/* Health cards */}
              {Object.entries(healthScores).map(([iid, h]) => (
                <div
                  key={iid}
                  className="border border-zinc-800 rounded-lg p-4"
                >
                  <div className="flex items-center justify-between mb-3">
                    <div>
                      <span className="text-sm font-medium text-zinc-200">
                        {h.instance_id}
                      </span>
                      <p className="text-[10px] text-zinc-500">
                        {h.strategy_id}
                      </p>
                    </div>
                    <div className="flex items-center gap-2">
                      <span
                        className={`text-2xl font-bold ${GRADE_COLORS[h.grade] || "text-zinc-400"}`}
                      >
                        {h.grade}
                      </span>
                      <span className="text-xs text-zinc-500">
                        {h.score.toFixed(0)}/100
                      </span>
                    </div>
                  </div>

                  {/* Factor bars */}
                  <div className="grid grid-cols-5 gap-2 mb-3">
                    {[
                      ["胜率", h.win_rate_score],
                      ["盈亏比", h.profit_factor_score],
                      ["稳定性", h.consistency_score],
                      ["市况", h.regime_fit_score],
                      ["近期", h.recency_score],
                    ].map(([label, rawScore]) => {
                      const score = Number(rawScore);
                      return (
                        <div key={label} className="text-center">
                          <div className="text-[9px] text-zinc-500 mb-1">
                            {label}
                          </div>
                          <div className="h-1.5 bg-zinc-800 rounded-full overflow-hidden">
                            <div
                              className="h-full bg-amber-500 rounded-full transition-all"
                              style={{ width: `${Math.min(100, score)}%` }}
                            />
                          </div>
                          <div className="text-[10px] text-zinc-400 mt-0.5">
                            {score.toFixed(0)}
                          </div>
                        </div>
                      );
                    })}
                  </div>

                  {/* Metrics row */}
                  <div className="flex items-center gap-3 text-[10px] text-zinc-500 border-t border-zinc-800 pt-2">
                    <span>交易 {h.total_trades} 笔</span>
                    <span>胜率 {(h.win_rate * 100).toFixed(1)}%</span>
                    <span>PF {h.profit_factor.toFixed(2)}</span>
                    <span>连亏 {h.loss_streak}</span>
                    <span>PnL ${h.total_pnl.toFixed(0)}</span>
                    {h.primary_loss_pattern && (
                      <span className="text-red-400 ml-auto">
                        主因: {h.primary_loss_pattern}
                      </span>
                    )}
                  </div>

                  {/* Suggested action */}
                  {h.suggested_action !== "normal" && (
                    <div className="flex items-center gap-1.5 mt-2 text-[11px]">
                      <AlertTriangle className="w-3 h-3 text-orange-400" />
                      <span className="text-orange-400">
                        {h.suggested_action === "reduce_size"
                          ? `减仓至 ${(h.suggested_scale * 100).toFixed(0)}%`
                          : h.suggested_action === "shadow_only"
                            ? "仅 Shadow 观察"
                            : h.suggested_action === "rehabilitate"
                              ? "康复中..."
                              : h.suggested_action}
                      </span>
                      {h.rehabilitation_progress > 0 &&
                        h.rehabilitation_progress < 1 && (
                          <span className="text-zinc-500">
                            进度 {(h.rehabilitation_progress * 100).toFixed(0)}%
                          </span>
                        )}
                    </div>
                  )}
                </div>
              ))}

              {/* Voting weights */}
              {Object.keys(votingWeights).length > 0 && (
                <div className="border border-zinc-800 rounded-lg p-4">
                  <h3 className="text-xs font-medium text-zinc-300 mb-2">
                    投票权重分配
                  </h3>
                  <div className="space-y-1.5">
                    {Object.entries(votingWeights).map(([iid, w]) => (
                      <div key={iid} className="flex items-center gap-2">
                        <span className="text-[11px] text-zinc-400 w-28 truncate">
                          {iid}
                        </span>
                        <div className="flex-1 h-2 bg-zinc-800 rounded-full overflow-hidden">
                          <div
                            className="h-full bg-amber-500 rounded-full transition-all"
                            style={{ width: `${(w * 100).toFixed(0)}%` }}
                          />
                        </div>
                        <span className="text-[10px] text-zinc-500 w-10 text-right tabular-nums">
                          {(w * 100).toFixed(0)}%
                        </span>
                      </div>
                    ))}
                  </div>
                </div>
              )}
            </>
          )}
        </div>
      )}

      {/* Critical-risk confirm modal */}
      {confirmCritical && changesSummary && (
        <div className="fixed inset-0 z-50 flex items-center justify-center bg-black/60 backdrop-blur-sm">
          <div className="bg-zinc-900 border border-red-500/30 rounded-xl p-6 max-w-md w-full mx-4 shadow-2xl">
            <div className="flex items-center gap-3 mb-4">
              <AlertTriangle className="w-6 h-6 text-red-400" />
              <h3 className="text-base font-semibold text-zinc-100">
                致命参数变更确认
              </h3>
            </div>
            <p className="text-sm text-zinc-400 mb-4">
              以下 {changesSummary.critical_risk}{" "}
              个致命风险级别参数将被修改，可能显著影响交易行为：
            </p>
            <div className="max-h-48 overflow-y-auto space-y-1.5 mb-6 bg-zinc-950 rounded-lg p-3">
              {dirtyParams
                .filter((dp) => {
                  const def = Object.values(schema)
                    .flatMap((g) => g.params)
                    .find((p) => p.key === dp.key);
                  return def?.risk_level === "critical";
                })
                .map((dp) => {
                  const def = Object.values(schema)
                    .flatMap((g) => g.params)
                    .find((p) => p.key === dp.key);
                  return (
                    <div
                      key={dp.key}
                      className="flex items-center gap-2 text-xs"
                    >
                      <span className="w-1.5 h-1.5 rounded-full bg-red-400 flex-shrink-0" />
                      <span className="text-zinc-300">
                        {def?.label || dp.key}
                      </span>
                      <span className="text-zinc-600 font-mono text-[10px] truncate ml-auto">
                        {JSON.stringify(dp.value)}
                      </span>
                    </div>
                  );
                })}
            </div>
            <div className="flex items-center gap-3">
              <button
                onClick={() => setConfirmCritical(false)}
                className="flex-1 px-4 py-2 text-xs text-zinc-300 bg-zinc-800 hover:bg-zinc-700 rounded-lg transition-colors"
              >
                取消
              </button>
              <button
                onClick={() => {
                  setConfirmCritical(true);
                  handleSave();
                }}
                className="flex-1 px-4 py-2 text-xs font-medium text-zinc-950 bg-red-500 hover:bg-red-400 rounded-lg transition-colors"
              >
                确认修改致命参数
              </button>
            </div>
          </div>
        </div>
      )}
    </div>
  );
}
