/ ===== 用地平衡 / 指标核算 / 规范校核 / 造价估算 ===== /

/ ---------- 用地平衡表 ---------- /
function renderLandTable() {
const tb = $("#landTable tbody");
tb.textContent = "";
const total = LAND_CATS.reduce((a, c) => a + (+S.land[c.k] || 0), 0);

LAND_CATS.forEach(c => {

const tr = document.createElement("tr");

const t1 = document.createElement("td");
const sw = document.createElement("span");
sw.className = "sw"; sw.style.background = c.color;
sw.style.display = "inline-block"; sw.style.marginRight = "6px";
t1.append(sw, document.createTextNode(c.n));
tr.appendChild(t1);

const t2 = document.createElement("td");
const inp = document.createElement("input");
inp.type = "number"; inp.min = 0; inp.step = 1; inp.value = +S.land[c.k] || 0;
inp.oninput = () => { S.land[c.k] = +inp.value || 0; };
inp.onchange = () => { renderLandTable(); recalcAll(); };
t2.appendChild(inp);
tr.appendChild(t2);

const t3 = document.createElement("td");
t3.className = "num";
t3.textContent = total > 0 ? pct((+S.land[c.k] || 0) / total * 100) : "—";
tr.appendChild(t3);

const t4 = document.createElement("td");
t4.textContent = c.nature;
tr.appendChild(t4);

tb.appendChild(tr);

});
$("#landTotal").textContent = fmt(total, 1);
}

/ ---------- 指标汇总 ---------- /
window.Calc = (function () {

function summary() {

const m = collectState().meta;
const st = PlantUI.stats();

const landTotal = LAND_CATS.reduce((a, c) => a + (+S.land[c.k] || 0), 0);
const siteArea = m.siteArea > 0 ? m.siteArea : landTotal;

let greenArea = LAND_CATS.filter(c => c.green).reduce((a, c) => a + (+S.land[c.k] || 0), 0);
const waterArea = +S.land.water || 0;
if (m.waterAsGreen) greenArea += waterArea;

const paveArea = (+S.land.pave || 0) + (+S.land.road || 0) + (+S.land.park || 0);
const buildArea = +S.land.build || 0;
const lawnArea = +S.land.lawn || 0;

const greenRate = siteArea > 0 ? greenArea / siteArea * 100 : 0;
const coverRate = siteArea > 0 ? st.canopy / siteArea * 100 : 0;
const treeDen = greenArea > 0 ? st.tree / greenArea * 100 : 0;
const gvPerSqm = greenArea > 0 ? st.gv / greenArea : 0;
const nativeRate = st.total > 0 ? st.native / st.total * 100 : 0;
const lawnRatio = greenArea > 0 ? lawnArea / greenArea * 100 : 0;
const everRatio = (st.ever + st.deci) > 0 ? st.ever / (st.ever + st.deci) * 100 : 0;
const perCapita = m.population > 0 ? greenArea / m.population : 0;
const shrubTreeRatio = st.tree > 0 ? st.shrubQty / st.tree : 0;
const bloomMonths = st.months.slice(1).filter(v => v > 0).length;

return {m, st, siteArea, landTotal, greenArea, waterArea, paveArea, buildArea, lawnArea,
        greenRate, coverRate, treeDen, gvPerSqm, nativeRate, lawnRatio, everRatio,
        perCapita, shrubTreeRatio, bloomMonths, rules:NORM_RULES[m.type]};

}

/ ---------- KPI 卡片 ---------- /
function renderKpi(d) {

const box = $("#kpiGrid");
box.textContent = "";
const items = [
  ["总用地面积", fmt(d.siteArea, 0), "m²", d.m.siteArea > 0 ? "手工录入" : "取各分区之和"],
  ["绿地面积", fmt(d.greenArea, 0), "m²", d.m.waterAsGreen ? "含水体" : "不含水体"],
  ["绿地率", fmt(d.greenRate, 1), "%", "绿地 / 总用地"],
  ["绿化覆盖率", fmt(d.coverRate, 1), "%", "冠幅投影 + 成片种植 / 总用地"],
  ["乔木总量", fmt0(d.st.tree), "株", "含损耗后数量"],
  ["乔木密度", fmt(d.treeDen, 2), "株/100m²", "按绿地面积计"],
  ["三维绿量", fmt(d.st.gv, 0), "m³", "冠幅投影 × 叶层厚度"],
  ["单位绿量", fmt(d.gvPerSqm, 2), "m³/m²", "三维绿量 / 绿地"],
  ["乡土植物比例", fmt(d.nativeRate, 1), "%", "按品种数计"],
  ["常绿乔木占比", fmt(d.everRatio, 1), "%", "按乔木株数加权"],
  ["草坪占绿地", fmt(d.lawnRatio, 1), "%", "控制养护成本"],
  ["灌乔比", fmt(d.shrubTreeRatio, 2), ": 1", "灌木株数 / 乔木株数"],
  ["观赏月份覆盖", d.bloomMonths, "/ 12 月", "有花或色叶的月份数"],
  ["硬质铺装面积", fmt(d.paveArea, 0), "m²", "含铺装 / 园路 / 场地"]
];
if (d.m.population > 0) items.push(["人均绿地", fmt(d.perCapita, 2), "m²/人", "绿地 / 服务人口"]);

items.forEach(it => {
  const el = document.createElement("div");
  el.className = "kpi";
  const k = document.createElement("div"); k.className = "k"; k.textContent = it[0];
  const v = document.createElement("div"); v.className = "v"; v.textContent = it[1];
  const s = document.createElement("small"); s.textContent = " " + it[2];
  v.appendChild(s);
  const dd = document.createElement("div"); dd.className = "d"; dd.textContent = it[3];
  el.append(k, v, dd);
  box.appendChild(el);
});

}

/ ---------- 规范校核 ---------- /
function buildChecks(d) {

const r = d.rules;
const up = (val, min, good) =>
  val >= good ? ["ok", "满足并优于常见要求"] : val >= min ? ["warn", "达到下限,建议继续提升"] : ["bad", "低于常见下限"];
const down = (val, max, good) =>
  val <= good ? ["ok", "控制良好"] : val <= max ? ["warn", "接近上限"] : ["bad", "超出常见上限"];
const band = (val, lo, hi) =>
  val >= lo && val <= hi ? ["ok", "处于合理区间"] : ["warn", "偏离常见区间"];

const rows = [];
const push = (name, val, unit, thr, res, tip) =>
  rows.push({name, val, unit, thr, res:res[0], resText:res[1], tip});

push("绿地率", d.greenRate, "%", "≥ " + r.greenRate[0] + "%(优 ≥ " + r.greenRate[1] + "%)",
  up(d.greenRate, r.greenRate[0], r.greenRate[1]),
  "压缩铺装面宽、把消防车道设为植草砖或嵌草铺装可有效提升");

push("绿化覆盖率", d.coverRate, "%", "≥ " + r.coverRate[0] + "%(优 ≥ " + r.coverRate[1] + "%)",
  up(d.coverRate, r.coverRate[0], r.coverRate[1]),
  "优先增加大冠幅乔木(冠幅每增 1m,单株投影约增 40%),而非增加灌木数量");

push("乔木密度", d.treeDen, "株/100m²", r.treeDen[0] + " ~ " + r.treeDen[1] + " 株/100m²",
  d.treeDen < r.treeDen[0] ? ["bad", "乔木偏少,遮阴与固碳不足"]
    : d.treeDen > r.treeDen[1] * 1.6 ? ["warn", "过密,后期需间移"] : ["ok", "密度合理"],
  "乔木过密会导致郁闭度过高、下层地被衰退;建议按成年冠幅 0.8 倍控制株距");

push("乡土植物比例", d.nativeRate, "%", "≥ " + r.nativeRate[0] + "%(优 ≥ " + r.nativeRate[1] + "%)",
  up(d.nativeRate, r.nativeRate[0], r.nativeRate[1]),
  "提高乡土比例可显著降低成活风险与养护成本,也是评审常见扣分项");

push("草坪占绿地比例", d.lawnRatio, "%", "≤ " + r.lawnMax[0] + "%(优 ≤ " + r.lawnMax[1] + "%)",
  down(d.lawnRatio, r.lawnMax[0], r.lawnMax[1]),
  "大面积纯草坪养护费高;可用观赏草、宿根花境或耐践踏地被替换部分区域");

push("单位绿地三维绿量", d.gvPerSqm, "m³/m²", "≥ " + r.gv[0] + "(优 ≥ " + r.gv[1] + ")",
  up(d.gvPerSqm, r.gv[0], r.gv[1]),
  "复层种植(乔木 + 小乔 + 灌木 + 地被)是提升绿量最有效的手段");

push("常绿乔木占比", d.everRatio, "%", "30% ~ 60%",
  band(d.everRatio, 30, 60),
  "常绿过高冬季显闷、春秋无季相变化;过低则冬季骨架缺失");

push("观赏月份覆盖", d.bloomMonths, "月", "≥ 8 个月",
  up(d.bloomMonths, 6, 8),
  "补充冬春开花品种(梅花、结香、茶梅、南天竹果)最容易补齐空档月");

if (d.m.population > 0)
  push("人均绿地面积", d.perCapita, "m²/人", "≥ 8 m²/人",
    up(d.perCapita, 5, 8), "指标偏低时可考虑立体绿化、屋顶花园折算");

return rows;

}

function renderChecks(rows) {

const tb = $("#checkTable tbody");
tb.textContent = "";
rows.forEach(r => {
  const tr = document.createElement("tr");
  const td = (txt, cls) => { const c = document.createElement("td"); if (cls) c.className = cls; c.textContent = txt; return c; };
  tr.appendChild(td(r.name));
  tr.appendChild(td(fmt(r.val, 2) + " " + r.unit, "num"));
  tr.appendChild(td(r.thr));
  const rc = document.createElement("td");
  const sp = document.createElement("span");
  sp.className = "res " + r.res;
  sp.textContent = (r.res === "ok" ? "● 通过 · " : r.res === "warn" ? "● 关注 · " : "● 不足 · ") + r.resText;
  rc.appendChild(sp);
  tr.appendChild(rc);
  tr.appendChild(td(r.tip));
  tb.appendChild(tr);
});

}

/ ---------- 群落结构 / 季相 ---------- /
function renderStruct(d) {

const box = $("#structChart");
box.textContent = "";
const g = d.greenArea || 1;
const rows = [
  ["乔木层投影", d.st.canopyTree || treeCanopy(), d.siteArea || 1, "m²"],
  ["灌木 / 竹类", d.st.shrubArea, g, "m²"],
  ["地被 / 草坪", d.st.groundArea, g, "m²"],
  ["其中纯草坪", d.lawnArea, g, "m²"]
];
rows.forEach(([lb, val, base, u]) => {
  const p = base > 0 ? Math.min(100, val / base * 100) : 0;
  const row = document.createElement("div");
  row.className = "bar-row";
  const l = document.createElement("span"); l.className = "lb"; l.textContent = lb;
  const t = document.createElement("div"); t.className = "bar-track";
  const f = document.createElement("div"); f.className = "bar-fill"; f.style.width = p.toFixed(1) + "%";
  t.appendChild(f);
  const v = document.createElement("span"); v.className = "vv";
  v.textContent = fmt(val, 0) + " " + u + " · " + fmt(p, 1) + "%";
  row.append(l, t, v);
  box.appendChild(row);
});

}
function treeCanopy() {

let c = 0;
S.plants.forEach(r => {
  PlantUI.derive(r);
  if (r.type === "乔木" || r.type === "小乔木") c += r._canopy;
});
return c;

}

function renderSeason(d) {

const box = $("#seasonChart");
box.textContent = "";
const mx = Math.max.apply(null, d.st.months.slice(1).concat([1]));
for (let i = 1; i <= 12; i++) {
  const c = d.st.months[i];
  const el = document.createElement("div");
  el.className = "mo" + (c >= mx * 0.6 && c > 0 ? " hot" : (c === 0 ? " cold" : ""));
  const n = document.createElement("div"); n.className = "mn"; n.textContent = i + "月";
  const v = document.createElement("div"); v.className = "mc"; v.textContent = c;
  el.append(n, v);
  el.title = c === 0 ? i + " 月无观赏品种,建议补充" : i + " 月有 " + c + " 个观赏品种";
  box.appendChild(el);
}

}

/ ---------- 造价 ---------- /
function costRows(d) {

const c = S.cost;
const plant = d.st.cost;
const greenForSoil = LAND_CATS.filter(x => x.green).reduce((a, x) => a + (+S.land[x.k] || 0), 0);
const rows = [
  ["苗木采购费", fmt0(d.st.total) + " 个品种", plant],
  ["种植施工费", "苗木费 × " + c.planting, plant * c.planting],
  ["一年养护费", "苗木费 × " + c.maint, plant * c.maint],
  ["绿地整理 / 种植土", fmt(greenForSoil, 0) + " m²", greenForSoil * c.soil],
  ["铺装场地", fmt(+S.land.pave || 0, 0) + " m²", (+S.land.pave || 0) * c.pave],
  ["园路", fmt((+S.land.road || 0) + (+S.land.park || 0), 0) + " m²", ((+S.land.road || 0) + (+S.land.park || 0)) * c.road],
  ["水体工程", fmt(d.waterArea, 0) + " m²", d.waterArea * c.water],
  ["给排水 / 电气 / 灌溉", fmt(d.siteArea, 0) + " m²", d.siteArea * c.mep],
  ["景观小品 / 构筑物", fmt(d.siteArea, 0) + " m²", d.siteArea * c.furn]
];
const sub = rows.reduce((a, r) => a + r[2], 0);
rows.push(["不可预见费", "小计 × " + c.risk, sub * c.risk]);
const total = sub * (1 + c.risk);
return {rows, sub, total, perSqm:d.siteArea > 0 ? total / d.siteArea : 0};

}

function renderCost(d) {

const r = costRows(d);
const tb = $("#costTable tbody");
tb.textContent = "";
r.rows.forEach(([name, qty, amt]) => {
  const tr = document.createElement("tr");
  const td = (t, cls) => { const c = document.createElement("td"); if (cls) c.className = cls; c.textContent = t; return c; };
  tr.appendChild(td(name));
  tr.appendChild(td(qty));
  tr.appendChild(td(fmt(amt, 0), "num"));
  tr.appendChild(td(r.total > 0 ? pct(amt / r.total * 100) : "—", "num"));
  tb.appendChild(tr);
});
$("#costTotal").textContent = money(r.total);
$("#costPerSqm").textContent = money(r.perSqm) + " / m²";

// B:单价缺失时明确告知造价不可用
const nt = $("#costNotice");
const missing = d.st.missing;
if (d.st.cost <= 0) {
  nt.textContent = "尚未录入任何苗木单价 → 苗木费、种植费、养护费均为 0,当前总造价不成立。请先在「苗木配置」页导入价格清单或手填单价。";
  nt.style.display = "";
} else if (missing > 0) {
  nt.textContent = "有 " + missing + " 行苗木未填单价,苗木费及其派生费用被低估,总造价仅可作结构参考,不可对外使用。";
  nt.style.display = "";
} else {
  const imported = Object.keys(S.priceBook || {}).length > 0;
  const refUsed = S.plants.some(p => p.priceSrc === "ref");
  nt.textContent = refUsed
    ? "注意:部分单价使用的是内置经验估值(无定额来源),请替换为当地造价信息价或供应商报价后再对外出图。"
    : (imported ? "单价来源:已导入的价格清单 + 手工填写。请确认清单的价格口径(是否含税、含运输、含种植)。"
                : "单价为手工填写。请确认价格口径(是否含税、含运输、含种植损耗)。");
  nt.style.display = "";
}
return r;

}

return {summary, renderKpi, buildChecks, renderChecks, renderStruct, renderSeason, renderCost, costRows, treeCanopy};
})();

/ ---------- 总刷新 ---------- /
function recalcAll() {
const d = Calc.summary();
Calc.renderKpi(d);
Calc.renderChecks(Calc.buildChecks(d));
Calc.renderStruct(d);
Calc.renderSeason(d);
Calc.renderCost(d);
return d;
}