#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""拜访与活动：KPI 与表格列改为按飞书「活动类型」动态生成（线下拜访/线上跟进/陌拜/协同拜访/未填）。

替换区间同前：`var visitMonth = '';` … `function renderTodos(main) {`
"""
import io
import os
import shutil
import sys
import time

ROOT = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
FILES = [os.path.join(ROOT, '工具', 'index.html'), os.path.join(ROOT, '工具', 'app.html')]
START = "var visitMonth = '';"
END = "function renderTodos(main) {"

NEW = r'''var visitMonth = '';
var visitView = '';                      /* me | dept | cust —— 互斥单一维度，不再有「全队」按钮 */
var visitDrill = { dept: '', sales: '' };
var visitHubState = 'see';
var visitHubCustomer = '';
var visitQ = '';
var VT_COLOR = { '线下拜访': '#1F4E9C', '线上跟进': '#002FA7', '陌拜': '#7C3AED',
                 '协同拜访': '#0E7490', '未填': '#cbd5e1' };

function visitIsMgr() {
  var r = (state.user || {}).role;
  return r === 'admin' || r === 'gm' || r === 'manager';
}
function visitViewDefault() { return visitIsMgr() ? 'dept' : 'me'; }
function visitScopeOf(v) { return v === 'me' ? 'me' : 'team'; }
function visitSetView(v) { visitView = v; visitDrill = { dept: '', sales: '' }; visitQ = ''; render(); }
function visitSetMonth(m) { visitMonth = m; visitDrill = { dept: '', sales: '' }; render(); }
function visitSetState(st) { visitHubState = st; if (st === 'see') visitHubCustomer = ''; render(); }
function visitSetQ(v) { visitQ = v; }
function visitDrillTo(dept, sales) {
  visitDrill = { dept: dept || '', sales: sales || '' };
  if (window.__vj) renderVisitBody(window.__vj);
}
function visitPrepGo(cust) {
  try { sessionStorage.setItem('bior_vp_customer', cust || ''); } catch (e) {}
  switchTab('visitPrep');
  setTimeout(function () { if (typeof showToast === 'function') showToast('已切到「访前准备」｜本次要准备：' + (cust || '（未指定客户）')); }, 400);
}
function visitCrumb() {
  var parts = ['<a href="#" onclick="visitDrillTo(\'\',\'\');return false" style="color:var(--pri)">全部部门</a>'];
  if (visitDrill.dept) parts.push('<a href="#" onclick="visitDrillTo(\'' + esc(visitDrill.dept) + '\',\'\');return false" style="color:var(--pri)">' + esc(visitDrill.dept) + '</a>');
  if (visitDrill.sales) parts.push('<span style="font-weight:700">' + esc(visitDrill.sales) + '</span>');
  return '<div id="visitCrumb" class="text-muted" style="font-size:12.5px;margin:0 0 8px">' + parts.join(' <b>›</b> ') + '</div>';
}
function visitEmpty() {
  return '<div class="todo-empty"><span class="icon">📈</span>该月暂无活动' +
    '<br><span style="font-size:12px">点右上「＋ 记一次拜访/跟进」，或到「客户」里发起拜访</span></div>';
}

/* ── 活动类型列（全部从接口的 type_order 生成，不写死） ── */
function vtCols() {
  var j = window.__vj || {};
  var order = (j.type_order || []).slice();
  var t = j.types || {};
  if (t['未填'] || Object.prototype.hasOwnProperty.call(t, '未填')) order.push('未填');
  return order;
}
function vtHead() {
  return vtCols().map(function (c) { return '<th style="width:88px">' + esc(c) + '</th>'; }).join('');
}
function vtCells(types) {
  types = types || {};
  return vtCols().map(function (c) {
    var n = types[c] || 0;
    var col = n ? (VT_COLOR[c] || '#5f6368') : '#cbd5e1';
    return '<td style="text-align:center"><b style="color:' + col + '">' + (n || '—') + '</b></td>';
  }).join('');
}
function vtTags(types) {
  types = types || {};
  return vtCols().filter(function (c) { return types[c]; }).map(function (c) {
    return '<span class="tag" style="background:' + (VT_COLOR[c] || '#eee') + ';color:#fff;margin-right:4px">' +
      esc(c) + ' ' + types[c] + '</span>';
  }).join('');
}

var visitActSeq = 0;
function visitCustTable(rows) {
  visitActSeq = 0;
  var body = '';
  rows.forEach(function (c, i) {
    body += '<tr><td style="text-align:left">' + esc(c.customer) + '</td>' + vtCells(c.types) +
      '<td style="text-align:center"><b>' + c.total + '</b></td>' +
      '<td style="white-space:nowrap">' +
        '<button class="btn btn-sm" onclick="visitToggle(' + i + ')">活动明细</button> ' +
        '<button class="btn btn-sm" onclick="visitPrepGo(&quot;' + esc(c.customer) + '&quot;)">去前准备</button>' +
      '</td></tr>' +
      '<tr id="visitActs' + i + '" style="display:none"><td colspan="' + (vtCols().length + 3) + '" style="background:#f8f9fa;padding:8px 12px">' +
      (c.acts || []).map(function (a) {
        var n = visitActSeq++;
        var at = a.atype || '未填';
        return '<div style="padding:5px 0;border-bottom:1px dashed #e5e7eb">' +
          '<span class="tag" style="background:' + (VT_COLOR[at] || '#cbd5e1') + ';color:#fff">' + esc(at) + '</span> ' +
          (a.src === 'feishu' ? '<span class="tag tag-gray" style="font-size:11px">飞书</span> ' : '') +
          '<b>' + esc(a.date || '') + '</b> ｜ ' + esc(a.type || '') + ' ｜ ' + esc(a.sales || '') +
          ' <a href="#" onclick="visitDetail(&quot;' + esc(a.id || '') + '&quot;,' + n + ');return false" style="color:var(--pri)">看详情 →</a>' +
          '<div id="visitDetail' + n + '"></div></div>';
      }).join('') + '</td></tr>';
  });
  return '<div style="overflow-x:auto"><table><thead><tr><th style="text-align:left">客户</th>' +
    vtHead() + '<th style="width:70px">合计</th><th style="width:180px"></th></tr></thead><tbody>' + body + '</tbody></table></div>';
}

function renderVisitStats(main) {
  if (!state.user) { main.innerHTML = ''; return; }
  if (!visitMonth) visitMonth = today().slice(0, 7);
  if (!visitView) visitView = visitViewDefault();
  if (!visitIsMgr()) visitView = 'me';
  var mgr = visitIsMgr();

  var head = '<div class="card" style="padding:14px 16px 12px">' +
    '<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap">' +
      '<h2 style="font-size:19px;font-weight:900;margin:0">📈 拜访与活动</h2>' +
      '<span class="text-muted" style="font-size:12px">录一次、看一次，同一个页面；下钻 部门 › 销售 › 客户 › 明细</span>' +
      '<div class="todo-filters" style="margin-left:auto">' +
        '<button class="btn btn-sm ' + (visitHubState === 'see' ? 'active' : '') + '" onclick="visitSetState(\'see\')">👀 看</button>' +
        '<button class="btn btn-sm ' + (visitHubState === 'rec' ? 'active' : '') + '" onclick="visitSetState(\'rec\')">✍️ 录</button>' +
      '</div>' +
    '</div>';
  head += (visitHubState === 'rec')
    ? '<div class="text-muted" style="font-size:12.5px;margin-top:6px">结构化录入 → 提交后自动回到「看」并计入统计</div></div>'
    : '<div style="display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:10px">' +
        '<input type="month" class="input" style="width:150px" value="' + esc(visitMonth) + '" onchange="visitSetMonth(this.value)">' +
        '<button class="btn btn-sm" onclick="visitSetMonth(today().slice(0,7));">本月</button>' +
        '<span style="width:8px"></span>' +
        '<div class="todo-filters">' +
          '<button class="btn btn-sm ' + (visitView === 'me' ? 'active' : '') + '" onclick="visitSetView(\'me\')">👤 我的</button>' +
          (mgr ? '<button class="btn btn-sm ' + (visitView === 'dept' ? 'active' : '') + '" onclick="visitSetView(\'dept\')">🏢 按部门</button>' +
                 '<button class="btn btn-sm ' + (visitView === 'cust' ? 'active' : '') + '" onclick="visitSetView(\'cust\')">📋 按客户</button>' : '') +
        '</div>' +
        '<button class="btn btn-primary btn-sm" style="margin-left:auto" onclick="visitSetState(\'rec\')">＋ 记一次拜访/跟进</button>' +
      '</div></div>';

  if (visitHubState === 'rec') {
    var q = visitHubCustomer ? ('&customer=' + encodeURIComponent(visitHubCustomer)) : '';
    main.innerHTML = head +
      '<div class="card" style="padding:0;overflow:hidden"><div style="height:calc(100vh - 240px);min-height:680px;background:#fff">' +
      '<iframe src="/工具/活动录入.html?v=20261007p4' + aeUserQS() + q + '" style="width:100%;height:100%;border:none" title="活动录入"></iframe>' +
      '</div></div>';
    return;
  }

  main.innerHTML = head + '<div class="card"><div class="card-body" id="visitBody">' +
    '<div class="text-muted" style="padding:14px">加载中…</div></div></div>';
  wbReq('/api/visit/stats?month=' + encodeURIComponent(visitMonth) + '&scope=' + encodeURIComponent(visitScopeOf(visitView)),
        { cache: 'no-store' })
    .then(function (j) { renderVisitBody(j); })
    .catch(function (e) {
      var b = document.getElementById('visitBody');
      if (b) b.innerHTML = '<div class="error-box" style="margin:12px">加载失败：' + esc(String((e && e.message) || e)) + '</div>';
    });
}

function visitTrend(j) {
  var t = (j && j.trend) || [];
  if (t.length < 2) return '';
  var cols = vtCols();
  var mx = 1;
  t.forEach(function (x) { mx = Math.max(mx, x.total || 0); });
  return '<div style="display:flex;gap:5px;align-items:flex-end;height:96px;margin:2px 0 14px">' +
    t.map(function (x) {
      var ty = x.types || {};
      var h = function (n) { return Math.round((n / mx) * 68); };
      var tip = x.month + '：合计 ' + (x.total || 0) + ' ｜ ' +
        cols.filter(function (c) { return ty[c]; }).map(function (c) { return c + ' ' + ty[c]; }).join(' ｜ ');
      return '<div title="' + esc(tip) + '" style="flex:1;text-align:center">' +
        '<div style="height:68px;display:flex;flex-direction:column;justify-content:flex-end">' +
          cols.slice().reverse().map(function (c) {
            var n = ty[c] || 0;
            return n ? '<div style="height:' + h(n) + 'px;background:' + (VT_COLOR[c] || '#cbd5e1') + '"></div>' : '';
          }).join('') +
        '</div>' +
        '<div style="font-size:10px;color:#9aa0a6;margin-top:3px">' + esc(String(x.month || '').slice(5)) + '</div></div>';
    }).join('') +
    '<div style="font-size:10.5px;color:#9aa0a6;align-self:flex-start;margin-left:8px">' +
      cols.map(function (c) { return '<div>■ <span style="color:' + (VT_COLOR[c] || '#cbd5e1') + '">' + esc(c) + '</span></div>'; }).join('') +
    '</div></div>';
}

function kpiCard(label, val, color, sub) {
  return '<div class="stat-card" style="border-left:4px solid ' + color + '">' +
    '<div class="text-muted" style="font-size:12px">' + label + '</div>' +
    '<div class="value" style="color:' + color + '">' + val + '</div>' +
    '<div class="text-muted" style="font-size:12px">' + (sub || '') + '</div></div>';
}

function renderVisitBody(j) {
  var b = document.getElementById('visitBody'); if (!b) return;
  if (!j || !j.ok) {
    b.innerHTML = '<div class="error-box" style="margin:12px">' + esc((j && j.error) || '加载失败') + '</div>';
    return;
  }
  window.__vj = j;
  var ty = j.types || {};
  var sb = j.src_breakdown || {};
  var cols = vtCols();

  var html = '<div class="stats-grid" style="margin-bottom:6px">' +
    kpiCard('活动总数', j.total, '#5f6368', (j.month || '')) +
    cols.map(function (c) {
      return kpiCard(c, ty[c] || 0, VT_COLOR[c] || '#5f6368',
        c === '未填' ? '飞书未填活动类型' : '飞书「活动类型」原值');
    }).join('') +
    '</div>';
  html += '<div class="text-muted" style="font-size:12px;margin-bottom:8px">' +
    '活动形式取自飞书「活动类型」字段原值（线下拜访 / 线上跟进 / 陌拜 / 协同拜访），**不是**文本推断。' +
    ((sb.feishu || 0) ? ('　来源：飞书历史 ' + sb.feishu + ' 条' + ((sb.workbench || 0) ? (' + 工作台 ' + sb.workbench + ' 条') : '')) : '') +
    '</div>';
  html += visitTrend(j);

  if (visitView === 'dept') {
    var deps = j.departments || [];
    html += visitCrumb();
    if (!visitDrill.dept) {
      if (!deps.length) { b.innerHTML = html + visitEmpty(); return; }
      b.innerHTML = html + '<div style="overflow-x:auto"><table><thead><tr><th style="text-align:left">部门</th>' +
        vtHead() + '<th style="width:70px">合计</th><th style="width:80px">销售数</th><th style="width:90px"></th></tr></thead><tbody>' +
        deps.map(function (d) {
          return '<tr><td style="text-align:left"><b>' + esc(d.dept) + '</b></td>' + vtCells(d.types) +
            '<td style="text-align:center"><b>' + d.total + '</b></td>' +
            '<td style="text-align:center">' + (d.sales_count || 0) + '</td>' +
            '<td><button class="btn btn-sm" onclick="visitDrillTo(&quot;' + esc(d.dept) + '&quot;,&quot;&quot;)">下钻 →</button></td></tr>';
        }).join('') + '</tbody></table></div>';
      return;
    }
    var dep = null;
    for (var i = 0; i < deps.length; i++) { if (deps[i].dept === visitDrill.dept) { dep = deps[i]; break; } }
    if (!dep) { b.innerHTML = html + '<div class="error-box" style="margin:12px">该部门本月无数据</div>'; return; }
    if (!visitDrill.sales) {
      b.innerHTML = html + '<div style="overflow-x:auto"><table><thead><tr><th style="text-align:left">销售</th>' +
        '<th style="width:80px">区域</th>' + vtHead() + '<th style="width:70px">合计</th>' +
        '<th style="width:80px">客户数</th><th style="width:90px"></th></tr></thead><tbody>' +
        (dep.sales || []).map(function (s) {
          return '<tr><td style="text-align:left"><b>' + esc(s.name) + '</b></td>' +
            '<td style="text-align:center" class="text-muted">' + esc(s.region || '—') + '</td>' +
            vtCells(s.types) +
            '<td style="text-align:center"><b>' + s.total + '</b></td>' +
            '<td style="text-align:center">' + (s.customer_count || 0) + '</td>' +
            '<td><button class="btn btn-sm" onclick="visitDrillTo(&quot;' + esc(visitDrill.dept) + '&quot;,&quot;' + esc(s.name) + '&quot;)">下钻 →</button></td></tr>';
        }).join('') + '</tbody></table></div>';
      return;
    }
    var sal = null;
    (dep.sales || []).forEach(function (s) { if (s.name === visitDrill.sales) { sal = s; } });
    if (!sal) { b.innerHTML = html + '<div class="error-box" style="margin:12px">该销售本月无数据</div>'; return; }
    b.innerHTML = html + visitCustTable(sal.customers || []);
    return;
  }

  /* 我的 / 按客户：扁平客户清单 */
  var all = j.customers || [];
  var rows = visitQ ? all.filter(function (c) { return (c.customer || '').indexOf(visitQ) >= 0; }) : all;
  html += '<div style="display:flex;gap:8px;align-items:center;margin-bottom:8px;flex-wrap:wrap">' +
    '<input class="input" style="max-width:240px" placeholder="按客户名筛选…" value="' + esc(visitQ) + '" ' +
      'oninput="visitSetQ(this.value);clearTimeout(window.__vq);window.__vq=setTimeout(function(){renderVisitBody(window.__vj);},280)">' +
    '<span class="text-muted" style="font-size:12px">' + rows.length + ' 家客户</span></div>';
  if (!rows.length) { b.innerHTML = html + visitEmpty(); return; }
  b.innerHTML = html + visitCustTable(rows);
}

function visitToggle(i) {
  var el = document.getElementById('visitActs' + i);
  if (el) el.style.display = (el.style.display === 'none' ? '' : 'none');
}

function visitDetail(id, n) {
  var host = document.getElementById('visitDetail' + n); if (!host) return;
  host.innerHTML = '<div class="text-muted" style="font-size:12px;padding:4px 0">加载中…</div>';
  wbReq('/api/activity/detail?id=' + encodeURIComponent(id), { cache: 'no-store' })
    .then(function (j) {
      if (!j.ok) { host.innerHTML = '<div class="error-box" style="margin:6px 0">' + esc(j.error || '加载失败') + '</div>'; return; }
      var rec = j.activity || {};
      var at = rec.atype || '未填';
      host.innerHTML = '<div class="text-muted" style="font-size:12px;margin:6px 0 2px">' +
        '活动类型：<b style="color:' + (VT_COLOR[at] || '#5f6368') + '">' + esc(at) + '</b>' +
        (rec.src === 'feishu' ? ' ｜ <span class="tag tag-gray" style="font-size:11px">飞书历史</span>' : '') +
        ' ｜ 关联订单：' + ((rec.order_ref_nos || []).length ? esc(rec.order_ref_nos.join('、')) : '—') + '</div>' +
        '<pre style="white-space:pre-wrap;font-size:12px;margin:6px 0;max-height:340px;overflow:auto;' +
        'background:#fff;padding:10px;border:1px solid #e5e7eb;border-radius:6px">' + esc(rec.feishu_text || '') + '</pre>';
    })
    .catch(function (e) {
      host.innerHTML = '<div class="error-box" style="margin:6px 0">' + esc(String((e && e.message) || e)) + '</div>';
    });
}

'''


def main():
    apply = '--apply' in sys.argv
    bak = os.path.join(ROOT, '_bak_p2d_types_view_%s' % time.strftime('%Y%m%d-%H%M%S'))
    print('前端按活动类型渲染 —— %s\n' % ('APPLY' if apply else 'DRY-RUN'))
    allok = True
    for f in FILES:
        s = io.open(f, encoding='utf-8').read()
        i = s.find(START); j = s.find(END, i)
        if i < 0 or j < 0:
            print('  ❌ %s 区间未找到' % os.path.basename(f)); allok = False; continue
        out = s[:i] + NEW + s[j:]
        print('  ✅ %s：%d → %d 字符' % (os.path.basename(f), j - i, len(NEW)))
        if apply:
            os.makedirs(bak, exist_ok=True)
            shutil.copy2(f, os.path.join(bak, os.path.basename(f)))
            io.open(f, 'w', encoding='utf-8').write(out)
    if apply and allok:
        print('\n  备份 → %s' % bak)
    return 0 if allok else 1


sys.exit(main())
