/**
 * 截图还原壳（通用）：顶栏在外层；本组件渲染筛选条 + 通栏侧标 + 主图/状态条/表。
 * 文案与筛选项来自 blueprint.meta.ui / widgets。
 */
import { useEffect, useMemo, useState } from "react";
import { Session, listRows } from "./api";
import { LineChart, SectionMarkChart, StatusStrip } from "./Charts";
import { BlockMachineTag } from "./PageMachineTag";

type WidgetDef = {
  type: string;
  title?: string;
  metric?: string;
  metrics?: string[];
  group_by?: string;
  x_field?: string;
  entity?: string;
  columns?: string[];
  label_field?: string;
  value_field?: string;
  secondary_field?: string;
  warn_field?: string;
  filter_field?: string;
  filter_op?: string;
  filter_value?: number | string;
  y_unit?: string;
  variant?: string;
  cycle_days?: number;
};

type FieldMeta = { name: string; label: string; type: string; enum_values?: string[] };

function formatValue(key: string, value: unknown): string {
  if (value == null || value === "") return "—";
  if (typeof value === "number") return Number.isInteger(value) ? String(value) : value.toFixed(2);
  return String(value);
}

function matchFilter(row: Record<string, unknown>, w: WidgetDef) {
  if (!w.filter_field) return true;
  const raw = row[w.filter_field];
  const n = Number(raw);
  const op = w.filter_op || "gt";
  const v = Number(w.filter_value ?? 0);
  if (op === "gt") return Number.isFinite(n) && n > v;
  if (op === "gte") return Number.isFinite(n) && n >= v;
  if (op === "lt") return Number.isFinite(n) && n < v;
  if (op === "eq") return String(raw) === String(w.filter_value ?? "");
  return true;
}

function toNum(raw: unknown): number {
  const n = typeof raw === "number" ? raw : Number(String(raw ?? "").replace(/,/g, ""));
  return Number.isFinite(n) ? n : NaN;
}

export function ScreenshotFaithfulDashboard(props: {
  session: Session | null;
  slug: string;
  resource: string;
  title: string;
  widgets: WidgetDef[];
  fieldMap: Record<string, FieldMeta>;
  meta: any;
  setError: (v: string) => void;
}) {
  const { session, slug, resource, widgets, fieldMap, meta, setError } = props;
  const [rows, setRows] = useState<Record<string, unknown>[]>([]);
  const [total, setTotal] = useState(0);
  const [radioVal, setRadioVal] = useState("");
  const [selectVal, setSelectVal] = useState("");
  const [scrollX, setScrollX] = useState(0);

  const ui = meta?.ui || {};
  const projectCtx = meta?.project_context || ui.project_context || "";
  const hint = ui.filter_hint || "";

  const radioField =
    ui.radio_field ||
    ui.filter_radio_field ||
    (ui.section_options || ui.filter_radios || ui.filter_style === "section_radios"
      ? "section_type"
      : fieldMap.section_type
        ? "section_type"
        : "");
  const radioOptions: string[] = (
    ui.filter_radios ||
    ui.section_options ||
    (radioField ? fieldMap[radioField]?.enum_values || [] : [])
  ).filter((x: string) => x && x !== "全部");
  const selectField =
    ui.select_field ||
    ui.filter_select_field ||
    (["worksite", "site", "store", "warehouse", "dept"].find((k) => fieldMap[k]) || "");
  const showRadios = Boolean(radioField && radioOptions.length > 0);
  const showSelect = Boolean(selectField);

  useEffect(() => {
    void (async () => {
      try {
        const filters: Record<string, string> = {};
        if (radioField && radioVal) {
          const aliases = (ui.filter_value_aliases || {}) as Record<string, string>;
          filters[radioField] = aliases[radioVal] || radioVal;
        }
        if (selectField && selectVal) filters[selectField] = selectVal;
        const res = await listRows(session, slug, resource, filters, 1, 2000);
        setRows(res.items || []);
        setTotal(res.total || (res.items || []).length);
      } catch (e: any) {
        setError(e.message || String(e));
      }
    })();
  }, [session, slug, resource, radioField, radioVal, selectField, selectVal, setError, ui.filter_value_aliases]);

  const labelOf = (name: string) => {
    const headers: string[] = ui.table_headers || [];
    const tw = widgets.find((w) => w.type === "table");
    const cols = tw?.columns || [];
    const idx = cols.indexOf(name);
    if (idx >= 0 && headers[idx]) {
      // table_headers 常含序号为首列
      if (headers[0] === "序号" && headers[idx + 1]) return headers[idx + 1];
      return headers[idx];
    }
    return fieldMap[name]?.label || name;
  };

  const lineW = widgets.find((w) => w.type === "line_chart" || w.type === "bar_chart");
  const stripW = widgets.find((w) => w.type === "status_strip");
  const tableW = widgets.find((w) => w.type === "table");

  const chartSide = ui.chart_side_label || lineW?.title || "主图";
  const stripSide = ui.strip_side_label || stripW?.title || "状态";
  const tableSide = ui.table_side_label || "明细";
  const tableTitle = ui.table_title || tableW?.title || "明细表";

  const statsLeft = ui.stats_left_label || "";
  const statsRight = ui.stats_right_label || "";

  const sortedRows = useMemo(() => {
    const xf = lineW?.x_field || lineW?.group_by || Object.keys(fieldMap)[0] || "id";
    return [...rows].sort((a, b) => String(a[xf] ?? "").localeCompare(String(b[xf] ?? ""), "zh"));
  }, [rows, lineW, fieldMap]);

  // 截图壳默认用着色测点图；仅当显式 chart_style=line/multi_series 时走多系列折线
  const valueField =
    ui.value_field ||
    lineW?.metric ||
    lineW?.metrics?.[0] ||
    ["value", "cjl_value", "cum_value", "cum_settlement_mm", "amount", "qty"].find((k) => fieldMap[k]) ||
    "";
  const colorField = ui.color_field || ["color", "cjl_color", "mark_color"].find((k) => fieldMap[k]) || "";
  const designField =
    ui.design_field ||
    ["design_value", "design_settlement_mm", "target", "design"].find((k) => fieldMap[k]) ||
    "";
  const categoryColors = (ui.category_colors || {}) as Record<string, string>;
  const categoryField = ui.category_field || radioField || "";

  const style = String(ui.chart_style || "").toLowerCase();
  const forceLine = style === "line" || style === "multi_series" || style === "line_chart";
  const forceMarks = style === "section_marks" || style === "colored_marks";
  const canMarks =
    Boolean(lineW) &&
    Boolean(valueField) &&
    Boolean(lineW?.x_field || lineW?.group_by || Object.keys(fieldMap)[0]);
  // 有主图 widget 且能推断取值字段时，默认 section_marks（避免主图整段不渲染）
  const useSectionMarks = forceMarks || (!forceLine && canMarks);

  const markPoints = useMemo(() => {
    if (!useSectionMarks) return [];
    const xf = lineW?.x_field || lineW?.group_by || Object.keys(fieldMap)[0] || "";
    const palette = ["#1d4f91", "#7c3aed", "#868e96", "#0f766e", "#c2410c"];
    const catIndex = new Map<string, number>();
    return sortedRows.map((row) => {
      const value = toNum(valueField ? row[valueField] : undefined);
      const design = designField ? toNum(row[designField]) : NaN;
      let color = colorField ? String(row[colorField] || "").trim() : "";
      if (!color && categoryField) {
        const cat = String(row[categoryField] ?? "");
        if (categoryColors[cat]) color = categoryColors[cat];
        else if (cat) {
          if (!catIndex.has(cat)) catIndex.set(cat, catIndex.size);
          color = palette[catIndex.get(cat)! % palette.length];
        }
      }
      if (!color) color = palette[0];
      return {
        x: String(row[xf] ?? ""),
        value: Number.isFinite(value) ? value : 0,
        design: Number.isFinite(design) ? design : undefined,
        color,
      };
    });
  }, [
    useSectionMarks,
    sortedRows,
    lineW,
    valueField,
    colorField,
    designField,
    categoryField,
    categoryColors,
    fieldMap,
  ]);

  const lineBlock = useMemo(() => {
    if (!lineW || useSectionMarks) return null;
    const xf = lineW.x_field || lineW.group_by || "";
    let seriesKeys = (lineW.metrics?.length
      ? lineW.metrics
      : lineW.metric && lineW.metric !== "count"
        ? [lineW.metric]
        : []
    ).filter(Boolean) as string[];
    // 蓝图 metrics 为空或对不上数据时，用数值字段兜底，避免主图整段消失
    if (!seriesKeys.length || !seriesKeys.some((k) => fieldMap[k])) {
      seriesKeys = Object.values(fieldMap)
        .filter((f) => ["int", "bigint", "decimal", "number", "float"].includes(String(f.type)))
        .map((f) => f.name)
        .filter((n) => !["id", "tenant_id"].includes(n))
        .slice(0, 4);
    }
    if (!xf || !seriesKeys.length) return null;
    const points = sortedRows.map((row) => {
      const values: Record<string, number> = {};
      for (const k of seriesKeys) {
        const n = toNum(row[k]);
        if (Number.isFinite(n)) values[k] = n;
      }
      return { x: String(row[xf] ?? ""), values };
    });
    const legendMap: Record<string, string> = ui.series_labels || {};
    return {
      title: lineW.title || "图表",
      points,
      series: seriesKeys.map((key) => ({ key, label: legendMap[key] || labelOf(key) })),
      yUnit: lineW.y_unit || ui.y_unit || "",
    };
  }, [lineW, sortedRows, useSectionMarks, ui.series_labels, ui.y_unit, fieldMap]);

  const legendItems: { label: string; color: string }[] = ui.legend_items?.length
    ? ui.legend_items
    : useSectionMarks && Object.keys(categoryColors).length
      ? Object.entries(categoryColors).map(([label, color]) => ({ label, color }))
      : (lineBlock?.series || []).map((s, i) => ({
          label: s.label,
          color: ["#1d4f91", "#7c3aed", "#6b7280", "#c2410c"][i % 4],
        }));

  const stripItems = useMemo(() => {
    if (!stripW) return [];
    const labelF = stripW.label_field || "";
    const valueF = stripW.value_field || "";
    const secondaryF = stripW.secondary_field || "";
    if (!labelF || !valueF) return [];
    const cycle = Number(stripW.cycle_days) || Number(ui.cycle_days) || 30;
    const stacked =
      stripW.variant === "stacked" ||
      stripW.variant === "stacked_days" ||
      Boolean(secondaryF);

    return sortedRows.map((r) => {
      const label = String(r[labelF] ?? "");
      let top = toNum(r[valueF]);
      let bottom = secondaryF ? toNum(r[secondaryF]) : NaN;
      if (!Number.isFinite(top)) top = 0;
      if (!Number.isFinite(bottom)) bottom = 0;
      if (stacked && top + bottom > 50 && top + bottom < 120) {
        const t = Math.round((top / 100) * cycle);
        const b = Math.max(0, cycle - t);
        top = t;
        bottom = b;
      }
      const stop =
        Boolean(r.stop_flag) ||
        String(r.status || "").includes("停") ||
        (stacked && top === 0 && bottom === 0);
      return { label, value: top, secondary: stacked ? bottom : undefined, stop };
    });
  }, [stripW, sortedRows, ui.cycle_days]);

  const stripVariant =
    stripW?.variant === "stacked" ||
    stripW?.variant === "stacked_days" ||
    Boolean(stripW?.secondary_field)
      ? "stacked"
      : "cells";

  const tableCols =
    tableW?.columns?.length
      ? tableW.columns
      : Object.keys(fieldMap).filter((k) => !["id", "tenant_id"].includes(k)).slice(0, 6);
  const tableRows = tableW ? sortedRows.filter((r) => matchFilter(r, tableW)) : [];

  const selectOptions = useMemo(() => {
    if (!selectField) return [] as string[];
    if (fieldMap[selectField]?.enum_values?.length) return fieldMap[selectField].enum_values!;
    const s = new Set<string>();
    for (const r of rows) {
      const w = String(r[selectField] ?? "");
      if (w) s.add(w);
    }
    return Array.from(s).sort();
  }, [selectField, fieldMap, rows]);

  const groupCount = useMemo(() => {
    const key = lineW?.x_field || lineW?.group_by || "";
    if (!key) return total;
    return new Set(rows.map((r) => String(r[key] ?? ""))).size || total;
  }, [rows, total, lineW]);

  const pageSize = Number(ui.chart_page_size) || 28;
  const dataLen = useSectionMarks ? markPoints.length : lineBlock?.points.length || stripItems.length;
  const maxScroll = Math.max(0, dataLen - pageSize);
  const visMarks = markPoints.slice(scrollX, scrollX + pageSize);
  const visLine = lineBlock?.points.slice(scrollX, scrollX + pageSize) || [];
  const visStrip = stripItems.slice(scrollX, scrollX + pageSize);

  const contextLine = (() => {
    if (projectCtx && (statsLeft || statsRight) && projectCtx.includes(statsLeft || "\0")) {
      return projectCtx;
    }
    const parts: string[] = [];
    if (projectCtx) parts.push(projectCtx);
    if (statsLeft) parts.push(`${statsLeft}:${groupCount}个`);
    if (statsRight) parts.push(`${statsRight}: ${total}个`);
    return parts.join(" ") || (total ? `共 ${total} 条` : "");
  })();

  const selectLabel = ui.select_label || (selectField ? labelOf(selectField) : "") || "";
  const yUnit = lineW?.y_unit || ui.y_unit || "";
  const yAxisLabel = ui.y_axis_label || yUnit || "";
  const invertY = Boolean(ui.invert_y);
  const designColor = ui.design_series_color || "#e8590c";
  const emptyTableText = ui.empty_table_text || "暂无数据";
  const filterKeys = [radioField, selectField].filter(Boolean);

  return (
    <div className="sf-root">
      <BlockMachineTag
        slug={slug}
        resource={resource}
        blockId="sf-dashboard"
        endpoint={{ method: "GET", path: `/api/v1/apps/${slug}/${resource}` }}
        extra={{
          page_type: "dashboard",
          filters: filterKeys,
          list_query: "filter.<field>=value&page=1&page_size=2000",
        }}
      />

      <div className="sf-filterbar">
        <div className="sf-context">
          <strong>{contextLine}</strong>
        </div>
        {showRadios && (
          <div className="sf-radios">
            {["", ...radioOptions].map((opt) => (
              <label key={opt || "all"} className={`sf-radio ${(radioVal || "") === opt ? "on" : ""}`}>
                <input
                  type="radio"
                  name="sf-radio"
                  checked={(radioVal || "") === opt}
                  onChange={() => setRadioVal(opt)}
                />
                {opt || ui.radio_all_label || "全部"}
              </label>
            ))}
          </div>
        )}
        {showSelect && (
          <label className="sf-select">
            {selectLabel ? `${selectLabel}:` : null}
            <select value={selectVal} onChange={(e) => setSelectVal(e.target.value)}>
              <option value="">--请选择--</option>
              {selectOptions.map((w) => (
                <option key={w} value={w}>
                  {w}
                </option>
              ))}
            </select>
          </label>
        )}
        {hint ? <span className="sf-hint">{hint}</span> : null}
      </div>

      {(legendItems.length > 0 && lineW) && (
        <div className="sf-legend">
          {legendItems.map((s, i) => (
            <span
              key={`${s.label}-${i}`}
              className={`sf-leg-item${/曲线|折线|line/i.test(s.label) ? " leg-line" : ""}`}
              style={{ ["--leg" as string]: s.color }}
            >
              {s.label}
            </span>
          ))}
        </div>
      )}

      <div className="sf-body">
        <div className="sf-rail">
          {lineW ? <div className="sf-rail-seg chart">{chartSide}</div> : null}
          {stripW ? <div className="sf-rail-seg strip">{stripSide}</div> : null}
          {tableW ? <div className="sf-rail-seg table">{tableSide}</div> : null}
        </div>
        <div className="sf-main">
          {lineW && (
            <div className="sf-line-wrap">
              {useSectionMarks ? (
                <SectionMarkChart
                  points={visMarks.length ? visMarks : markPoints}
                  yUnit={yUnit}
                  yAxisLabel={yAxisLabel}
                  invertY={invertY}
                  designColor={designColor}
                />
              ) : lineBlock ? (
                <LineChart
                  title=""
                  points={visLine.length ? visLine : lineBlock.points}
                  series={lineBlock.series}
                  yUnit={lineBlock.yUnit}
                />
              ) : (
                <div className="empty" style={{ padding: "2rem", textAlign: "center" }}>
                  暂无曲线数据（请检查蓝图 x_field / metrics 与已导入数据）
                </div>
              )}
              {maxScroll > 0 && (
                <input
                  className="sf-scroll"
                  type="range"
                  min={0}
                  max={maxScroll}
                  value={scrollX}
                  onChange={(e) => setScrollX(Number(e.target.value))}
                />
              )}
            </div>
          )}

          {stripW && (
            <div className="sf-strip-wrap">
              <StatusStrip title="" variant={stripVariant} items={visStrip} />
            </div>
          )}

          {tableW && (
            <div className="sf-table-panel">
              <div className="sf-table-title">{tableTitle}</div>
              <div className="table-wrap">
                <table className="sf-table">
                  <thead>
                    <tr>
                      <th>序号</th>
                      {tableCols.map((c) => (
                        <th key={c}>{labelOf(c)}</th>
                      ))}
                    </tr>
                  </thead>
                  <tbody>
                    {tableRows.map((r, i) => (
                      <tr key={i}>
                        <td>{i + 1}</td>
                        {tableCols.map((c) => (
                          <td key={c}>{formatValue(c, r[c])}</td>
                        ))}
                      </tr>
                    ))}
                    {!tableRows.length && (
                      <tr>
                        <td colSpan={tableCols.length + 1} className="empty">
                          {emptyTableText}
                        </td>
                      </tr>
                    )}
                  </tbody>
                </table>
              </div>
            </div>
          )}
        </div>
      </div>
    </div>
  );
}
