#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
客户页空白修复（2026-09-20）：MCP 客户池没有负责人字段
========================================================
现象：刘欣登录后「客户」页面空白。
根因：状态.data.mcpCustomers 由 adaptMcpFields 适配，但源数据里 owner / operators /
      current_status_operator 三个字段**全为空**（1737 条 0 个有负责人），
      而客户 Tab / 看板客户数 / 拜访准备 都按 `c.owner === 用户名` 过滤 → 非管理员必然 0 条。
      真正可用的归属线索只有两样：① ERP 客户表（_key_customers.json）的 salesperson
      ② MCP 的部门标签 field_5ed7ab（诊断原料销售拓展部 1515 / 诊断原料大客户销售部 91 / 浙江 67 / 广东 55）。
修法：新增统一函数 custScopeFilter(sc)，三处复用；区域经理按地区标签，部门经理按部门标签，
      销售按 ERP 客户表本人客户名。admin/gm 仍返回 null（不过滤）。
"""
import io, sys
PC, MB = '工具/index.html', '工具/app.html'
APPLY = '--apply' in sys.argv

HELPER = """/* ── 客户池归属判定（2026-09-20）：MCP 客户没有负责人字段（owner/operators 全空），
   归属只能靠 ① ERP 客户表销售员 ② 本部门标签 ③（区域经理）地区标签。
   返回过滤函数；admin/gm（sc 为 null）返回 null＝不过滤。 ── */
function custScopeFilter(sc) {
  if (!sc) return null;
  var u = state.user || {};
  var nd = normDeptName(u.dept);
  var byName = {};
  (state.data.customers || []).forEach(function(c) {
    if (c && c.name && sc.names.indexOf(c.salesperson || c.sales) >= 0) byName[c.name] = 1;
  });
  return function(c) {
    if (!c) return false;
    if (sc.names.indexOf(c.owner) >= 0) return true;
    if ((c.operators || []).some(function(op) { return sc.names.indexOf(op) >= 0; })) return true;
    if (byName[c.name]) return true;
    if (sc.region) return String(c.dept || '').indexOf(sc.region) >= 0;
    if (u.role === 'manager') return normDeptName(c.dept) === nd;
    return false;
  };
}
"""

PATCHES = [
    # ── index：客户 Tab ──
    (PC, """  var userName = (state.user||{}).name;
  var _scCust = deptScope();
  if (_scCust) {
    all = all.filter(function(c){
      if (_scCust.region) return String(c.dept||'').indexOf(_scCust.region) >= 0 || _scCust.names.indexOf(c.owner) >= 0;
      return _scCust.names.indexOf(c.owner) >= 0 || (c.operators||[]).some(function(op){return _scCust.names.indexOf(op)>=0;});
    });
  }""",
     """  var userName = (state.user||{}).name;
  var _fCust = custScopeFilter(deptScope());
  if (_fCust) all = all.filter(_fCust);""",
     '客户 Tab 作用域'),
    # ── index：看板客户数 + MCP 过滤 ──
    (PC, """  var mcpFiltered = mcpAll;
  if (_scDash) {
    mcpFiltered = mcpAll.filter(function(c){
      if (c.owner === userName || (c.operators||[]).indexOf(userName) >= 0) return true;
      return _scDash.names.indexOf(c.owner) >= 0 || (c.operators||[]).some(function(op){return _scDash.names.indexOf(op)>=0;});
    });
  }
  if (filteredSalesSet) {
    mcpFiltered = mcpFiltered.filter(function(c){return filteredSalesSet.has(c.owner)||c.operators.some(function(op){return filteredSalesSet.has(op);});});
  }""",
     """  var mcpFiltered = mcpAll;
  var _fDash = custScopeFilter(_scDash);
  if (_fDash) mcpFiltered = mcpFiltered.filter(_fDash);
  if (filteredSalesSet) {
    var _fsNames = {};
    (state.data.customers || []).forEach(function(c) { if (c && c.name && filteredSalesSet.has(c.salesperson || c.sales)) _fsNames[c.name] = 1; });
    mcpFiltered = mcpFiltered.filter(function(c) {
      if (_fsNames[c.name]) return true;
      return filteredSalesSet.has(c.owner) || (c.operators || []).some(function(op) { return filteredSalesSet.has(op); });
    });
  }""",
     '看板客户数口径'),
    # ── index：拜访准备（经理分支）──
    (PC, """    return mcpAll.filter(function(c) {
      if (_scVp.region) return String(c.dept||'').indexOf(_scVp.region) >= 0;
      return _scVp.names.indexOf(c.owner) >= 0 || (c.operators||[]).some(function(op){return _scVp.names.indexOf(op)>=0;})
             || normDeptName(c.dept) === normDeptName(dept);
    });""",
     """    var _fVp = custScopeFilter(_scVp);
    return _fVp ? mcpAll.filter(_fVp) : mcpAll;""",
     '拜访准备·经理'),
    # ── app：客户 Tab ──
    (MB, """  var userName = (state.user||{}).name;
  var _scC = deptScope();
  if (_scC) {
    all = all.filter(function(c){return inScope(_scC, c.owner)||(_scC.names.some(function(nm){return (c.operators||[]).indexOf(nm)>=0;}));});
  }""",
     """  var userName = (state.user||{}).name;
  var _fCust = custScopeFilter(deptScope());
  if (_fCust) all = all.filter(_fCust);""",
     '客户 Tab 作用域'),
    # ── app：看板客户数 ──
    (MB, "  var mcpFiltered = userName ? mcpAll.filter(function(c){return c.owner===userName||(c.operators||[]).indexOf(userName)>=0;}) : mcpAll;",
     """  var _fDash = custScopeFilter(deptScope());
  var mcpFiltered = _fDash ? mcpAll.filter(_fDash) : mcpAll;""",
     '看板客户数口径'),
    # ── app：拜访准备（经理 + 普通销售）──
    (MB, """  if (role === 'manager') {
    var mcpDept = (dept === '销售拓展部') ? '诊断原料销售拓展部' : dept;
    return mcpAll.filter(function(c) { return c.dept === mcpDept; });
  }""",
     """  if (role === 'manager') {
    var _fVp = custScopeFilter(deptScope());
    return _fVp ? mcpAll.filter(_fVp) : mcpAll;
  }""",
     '拜访准备·经理'),
    (MB, """  // 普通销售: 按所属人
  return mcpAll.filter(function(c) {
    return c.owner === name || (c.operators || []).indexOf(name) >= 0;
  });""",
     """  // 普通销售: ERP 客户表本人客户 + 部门标签兜底
  var _fVp2 = custScopeFilter(deptScope());
  return _fVp2 ? mcpAll.filter(_fVp2) : mcpAll;""",
     '拜访准备·销售'),
]

def main():
    n = 0
    # ① 注入 helper（放在 inScope 定义之后）
    for path in (PC, MB):
        s = io.open(path, encoding='utf-8').read()
        if 'function custScopeFilter(' in s:
            print('  ⏭  %s 已有 custScopeFilter' % path); continue
        anchor = "function inScope(sc, name) { return !!sc && sc.names.indexOf(name) >= 0; }"
        if anchor in s:
            s = s.replace(anchor, anchor + "\n\n" + HELPER, 1)
            io.open(path, 'w', encoding='utf-8').write(s)
            print('  ✅ %s 注入 custScopeFilter' % path); n += 1
        else:
            print('  ⚠️  %s 未找到 inScope 锚点' % path)
    # ② 三处替换
    for path, old, new, tag in PATCHES:
        s = io.open(path, encoding='utf-8').read()
        if old in s:
            io.open(path, 'w', encoding='utf-8').write(s.replace(old, new, 1))
            print('  ✅ %s  %s' % (path.split('/')[-1], tag)); n += 1
        else:
            print('  ⏭  %s  %s（锚点不匹配）' % (path.split('/')[-1], tag))
    print('\n  共 %d 处' % n)

main()
