#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""P2-P1 前端：把「拜访视图」升级为「拜访与活动」一页两态（看 / 录）。

替换区间：`var visitMonth = '';` … `function renderTodos(main) {`
（该区间包含旧的 renderVisitStats / kpiCard / renderVisitBody / visitToggle / visitDetail）
"""
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 visitScope = '';
var visitHubState = 'see';        /* see | rec */
var visitHubCustomer = '';
var visitQ = '';

function visitScopeDefault() {
  var r = (state.user || {}).role;
  return (r === 'admin' || r === 'gm' || r === 'manager') ? 'team' : 'me';
}
function visitIsMgr() {
  var r = (state.user || {}).role;
  return r === 'admin' || r === 'gm' || r === 'manager';
}
function visitScopeLabel(sc) { return sc === 'team' ? '全队' : '我的'; }
function visitSetState(st) { visitHubState = st; if (st === 'see') visitHubCustomer = ''; render(); }
function visitSetScope(sc) { visitScope = sc; render(); }
function visitSetMonth(m) { visitMonth = m; render(); }
function visitSetQ(v) { visitQ = v; }
function visitPrepGo(cust) {
  /* 去前准备：访前准备页目前是步骤引导页，没有客户级参数，所以只做跳转 + 明确提示，
     不做「假装已带入」的假功能。加 ?customer= 预填是后续项。 */
  try { sessionStorage.setItem('bior_vp_customer', cust || ''); } catch (e) {}
  switchTab('visitPrep');
  setTimeout(function () { toast('已切到「访前准备」｜本次要准备：' + (cust || '（未指定客户）')); }, 400);
}

function renderVisitStats(main) {
  if (!state.user) { main.innerHTML = ''; return; }
  if (!visitMonth) visitMonth = today().slice(0, 7);
  if (!visitScope) visitScope = visitScopeDefault();
  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">结构化录入 → 提交后自动回到「看」并计入统计（同一份数据，不再两个 Tab）</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)">' +
        (mgr ? '<div class="todo-filters">' +
                 '<button class="btn btn-sm ' + (visitScope === 'me' ? 'active' : '') + '" onclick="visitSetScope(\'me\')">👤 我的</button>' +
                 '<button class="btn btn-sm ' + (visitScope === 'team' ? 'active' : '') + '" onclick="visitSetScope(\'team\')">👥 全队</button>' +
               '</div>' : '<span class="tag tag-gray">口径：我的</span>') +
        '<button class="btn btn-sm" onclick="visitSetMonth(today().slice(0,7));">本月</button>' +
        '<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=20261007p2' + 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(visitScope),
        { 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 mx = 1;
  t.forEach(function (x) { mx = Math.max(mx, (x.offline || 0) + (x.online || 0)); });
  return '<div style="display:flex;gap:5px;align-items:flex-end;height:86px;margin:2px 0 14px">' +
    t.map(function (x) {
      var o = Math.round(((x.offline || 0) / mx) * 62), n = Math.round(((x.online || 0) / mx) * 62);
      return '<div title="' + esc(x.month) + '：线下 ' + (x.offline || 0) + ' ｜ 线上 ' + (x.online || 0) + '" style="flex:1;text-align:center">' +
        '<div style="height:62px;display:flex;flex-direction:column;justify-content:flex-end">' +
          (n ? '<div style="height:' + n + 'px;background:#002FA7;border-radius:3px 3px 0 0"></div>' : '') +
          (o ? '<div style="height:' + o + 'px;background:#1F4E9C"></div>' : '') +
        '</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:6px"><div>■ <span style="color:#1F4E9C">线下</span></div><div>■ <span style="color:#002FA7">线上</span></div></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 all = j.customers || [];
  var rows = visitQ ? all.filter(function (c) { return (c.customer || '').indexOf(visitQ) >= 0; }) : all;

  var html = '<div class="stats-grid" style="margin-bottom:10px">' +
    kpiCard('线下拜访次数', j.offline_count, '#1F4E9C', visitScopeLabel(j.scope) + ' · ' + esc(j.month)) +
    kpiCard('线上跟进次数', j.online_count, '#002FA7', visitScopeLabel(j.scope) + ' · ' + esc(j.month)) +
    kpiCard('当月活动合计', j.total, '#5f6368', (j.unclassified ? ('未标形式 ' + j.unclassified + ' 条') : '全部已标形式')) +
    '</div>' + visitTrend(j);

  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 + ' 家客户' +
      (visitQ && j.customer_count ? ('（全部 ' + j.customer_count + '）') : '') + '</span>' +
    '</div>';

  if (!rows.length) {
    b.innerHTML = html + '<div class="todo-empty"><span class="icon">📈</span>该月暂无活动' +
      '<br><span style="font-size:12px">点右上「＋ 记一次拜访/跟进」，或到「客户」里发起拜访</span></div>';
    return;
  }

  var ai = 0, body = '';
  rows.forEach(function (c, i) {
    body += '<tr><td>' + esc(c.customer) + '</td>' +
      '<td><b style="color:#1F4E9C">' + c.offline + '</b></td>' +
      '<td><b style="color:#002FA7">' + c.online + '</b></td>' +
      '<td>' + c.total + '</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="5" style="background:#f8f9fa;padding:8px 12px">' +
      (c.acts || []).map(function (a) {
        var n = ai++;
        var ch = (a.channel === 'offline') ? '<span class="tag tag-blue">线下</span>'
               : (a.channel === 'online') ? '<span class="tag tag-gray">线上</span>'
               : '<span class="tag tag-orange">未标</span>';
        return '<div style="padding:5px 0;border-bottom:1px dashed #e5e7eb">' + ch +
          ' <b>' + esc(a.date || '') + '</b> ｜ ' + esc(a.type || '') + ' ｜ ' + esc(a.sales || '') + ' ｜ ' + esc(a.id || '') +
          ' <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>';
  });

  b.innerHTML = html + '<div style="overflow-x:auto"><table><thead><tr><th>客户</th>' +
    '<th style="width:90px">线下拜访</th><th style="width:90px">线上跟进</th><th style="width:80px">合计</th><th style="width:190px"></th>' +
    '</tr></thead><tbody>' + body + '</tbody></table></div>';
}

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 ch = rec.channel === 'offline' ? '线下拜访' : (rec.channel === 'online' ? '线上跟进' : '—');
      host.innerHTML = '<div class="text-muted" style="font-size:12px;margin:6px 0 2px">活动形式：' + esc(ch) +
        ' ｜ 关联订单：' + ((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:320px;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
    ts = time.strftime('%Y%m%d-%H%M%S')
    bak = os.path.join(ROOT, '_bak_p2_visithub_%s' % ts)
    print('P2-P1 融合页补丁 —— %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
        print('  ✅ %s：替换 %d 字符 → 新实现 %d 字符'
              % (os.path.basename(f), j - i, len(NEW)))
        out = s[:i] + NEW + s[j:]
        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)
    print('\n%s' % ('全部命中 ✅' if allok else '有未命中 ❌'))
    return 0 if allok else 1


sys.exit(main())
