#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""销售日报 HTML 生成器（全量、不截断）
输入：数据/daily_briefs/latest.md（8:30 cron 产出）
     数据/mcp_complaints.json（客诉，含 days/urgency/status）
     数据/kpi_progress.json（本月 KPI 进度）
     数据/erp_amount.json（ERP 金额：日/月/人/部门）
输出：数据/daily_briefs/宝锐销售日报_<日期>.html  +  index.html（归档页）
可选：--deploy 部署到 CloudBase /daily/ 目录
用法：python3 脚本/gen_daily_brief_html.py [--deploy] [--date YYYY-MM-DD]
"""
import os, re, sys, json, base64, datetime, subprocess, shutil

ROOT = os.path.expanduser('~/Desktop/Hermes输出-工作类')
sys.path.insert(0, os.path.join(ROOT, '脚本'))
from html_editable_layer import apply as apply_edit

MD = os.path.join(ROOT, '数据/daily_briefs/latest.md')
OUTDIR = os.path.join(ROOT, '数据/daily_briefs')
LOGO = os.path.expanduser('~/work/公司资料/logo_transparent.png')
ENV = 'bier-sales-d0gatbvlx288724e9'
CDN = 'https://bier-sales-d0gatbvlx288724e9-1458438710.tcloudbaseapp.com'
DEPLOY = '--deploy' in sys.argv
DATE_ARG = None
if '--date' in sys.argv:
    DATE_ARG = sys.argv[sys.argv.index('--date') + 1]
TODAY = DATE_ARG or datetime.date.today().isoformat()
logo = ('data:image/png;base64,' + base64.b64encode(open(LOGO, 'rb').read()).decode()) if os.path.exists(LOGO) else ''
raw = open(MD, encoding='utf-8').read()
lines = [l.rstrip('\n') for l in raw.split('\n')]


def esc(s):
    return (str(s).replace('&', '&amp;').replace('<', '&lt;').replace('>', '&gt;'))


def md_links(s):
    """[文本](url) → <a>，其余转义"""
    out, pos = [], 0
    for m in re.finditer(r'\[([^\]]+)\]\((https?://[^)]+)\)', s):
        out.append(esc(s[pos:m.start()]))
        out.append('<a href="%s" target="_blank" rel="noopener">%s</a>' % (m.group(2), esc(m.group(1))))
        pos = m.end()
    out.append(esc(s[pos:]))
    return ''.join(out)


# ── 1) 解析 latest.md ─────────────────────────────────────────
def grab(pat, default='—'):
    m = re.search(pat, raw)
    return m.group(1) if m else default

meta = {
    'date_line': next((l.strip() for l in lines[:4] if l.startswith('2026') or re.match(r'^\d{4}-', l)), ''),
    'src_line': next((l.strip() for l in lines[:6] if l.startswith('📅')), ''),
    'act': grab(r'活动\s*(\d+)\s*条'),
    'complaint': grab(r'未关闭客诉\s*(\d+)\s*条'),
    'orders': grab(r'近7天\s*(\d+)\s*单'),
    'amount': grab(r'¥\s*([\d.]+)\s*万'),
}
# 日期（周四）
dm = re.search(r'(\d{4}-\d{2}-\d{2})（周(.)）', raw)
REPORT_DATE = dm.group(1) if dm else TODAY

IS_HDR = re.compile(r'^(⚠️|👥|📊|⚔️|🚀|💰|🟢|🔴)')
sections, cur, cont = [], None, []
for ln in lines:
    if not ln.strip() or ln.startswith(('---', '🤖', '📋', '📅')):
        continue
    if IS_HDR.match(ln):
        if cur is not None:
            sections.append((cur, cont))
        cur, cont = ln.strip(), []
    elif cur is not None:
        cont.append(ln)
if cur is not None:
    sections.append((cur, cont))

# 子标题（积极进展/风险/受阻）拆分为独立节
SUB = re.compile(r'^[·\s]*[🟢🔴]\s*(积极进展|风险/?受阻|风险|受阻)')
flat, pending_parent = [], None
for title, cont in sections:
    if not cont and not SUB.search(title):
        pending_parent = title
        continue
    if SUB.search(title):
        base = '' if (pending_parent is None) else pending_parent
        nm = re.sub(r'^[·\s]*[🟢🔴]?\s*', '', title).strip().rstrip('：')
        flat.append(((base + ' ｜ ' if base else '') + title.strip(), cont, nm))
        pending_parent = None
    else:
        flat.append((title, cont, re.sub(r'^\S+\s*', '', title).split('（')[0]))
        pending_parent = None
sections = flat


def items_of(block):
    """把内容行拆成 (客户, 文本, 链接)"""
    out = []
    for ln in block:
        s = ln.strip()
        if not s or s.startswith('近30天') or s.startswith('其余'):
            continue
        s = s.lstrip('· ').strip()
        if not s:
            continue
        link = ''
        m = re.search(r'\[([^\]]*)\]\((https?://[^)]+)\)', s)
        if m:
            link = m.group(2)
            s = s[:m.start()].strip()
        out.append((s, link))
    return out


def split_cust(s):
    """'客户｜内容｜人' → (客户, 内容, 人)"""
    parts = [p.strip() for p in re.split(r'｜', s)]
    if len(parts) >= 3:
        return parts[0], '｜'.join(parts[1:-1]).strip(), parts[-1]
    if len(parts) == 2:
        return parts[0], parts[1], ''
    return '', s, ''


# ── 2) 新增管理视角数据 ───────────────────────────────────────
def load(p):
    try:
        return json.load(open(os.path.join(ROOT, p), encoding='utf-8'))
    except Exception:
        return None


kpi = load('数据/kpi_progress.json') or {}
erp = load('数据/erp_amount.json') or {}
comp = load('数据/mcp_complaints.json') or {}
comp_items = comp.get('items') if isinstance(comp, dict) else (comp or [])

# 超期未关闭客诉分层
open_comp = [c for c in (comp_items or []) if str(c.get('status', '')) not in ('结束', '已关闭', '关闭')]
buckets = {'>180天': 0, '90-180天': 0, '30-90天': 0, '<30天': 0}
owner_stat = {}
for c in open_comp:
    try:
        d = int(c.get('days') or 0)
    except Exception:
        d = 0
    k = '>180天' if d > 180 else ('90-180天' if d > 90 else ('30-90天' if d > 30 else '<30天'))
    buckets[k] += 1
    who = str(c.get('creator') or '未指派')
    st = owner_stat.setdefault(who, {'n': 0, 'oldest': 0})
    st['n'] += 1
    st['oldest'] = max(st['oldest'], d)
comp_rows = sorted([{'name': str(c.get('name') or ''), 'days': int(c.get('days') or 0),
                     'status': str(c.get('node') or c.get('status') or ''), 'cust': str(c.get('customer') or ''),
                     'who': str(c.get('creator') or ''), 'id': str(c.get('id') or ''),
                     'created': str(c.get('created') or '')[:10]}
                    for c in open_comp], key=lambda x: -x['days'])

# ERP 金额（结构：月 → 总额 / 月 → {部门|人|客户: 金额}）
daily = erp.get('daily') or {}
d_days = sorted(daily.keys())[-14:]
d_max = max([float(v or 0) for v in daily.values()] or [0]) or 1
erp_last = max(daily.keys()) if daily else '—'
monthly = erp.get('monthly') or {}
latest_month = max(monthly.keys()) if monthly else '—'
this_month = REPORT_DATE[:7]
month_is_latest = (this_month not in monthly)
use_month = latest_month if month_is_latest else this_month
m_amt = float(monthly.get(use_month) or 0)
dept_month = (erp.get('monthly_dept') or {}).get(use_month) or {}
person_month = (erp.get('monthly_person') or {}).get(use_month) or {}
month_label = ('%s（ERP 最新完整月，本月 %s 尚未入账）' % (use_month, this_month)) if month_is_latest else use_month

CSS = """
*{box-sizing:border-box}
body{margin:0;background:#eef2f7;color:#1f2d3d;font:13px/1.65 -apple-system,"PingFang SC","Microsoft YaHei",sans-serif}
.topbar{position:sticky;top:0;z-index:60;display:flex;align-items:center;gap:12px;padding:9px 18px;background:#fff;border-bottom:3px solid #1a73c9;box-shadow:0 2px 10px rgba(16,42,80,.08)}
.topbar img{height:34px}
.brand{font-weight:800;color:#12325b;font-size:15px;letter-spacing:.4px}
.brand small{display:block;font-weight:500;font-size:10.5px;color:#7286a0;letter-spacing:1.6px}
.ttl{margin-left:auto;text-align:right;color:#12325b;font-weight:700;font-size:15px}
.ttl span{display:block;font-weight:400;color:#8a9bb0;font-size:11.5px}
.wrap{max-width:1500px;margin:0 auto;padding:14px 18px 70px}
.kpis{display:grid;grid-template-columns:repeat(auto-fit,minmax(148px,1fr));gap:10px;margin-bottom:14px}
.kpi{background:#fff;border-radius:10px;padding:11px 13px;border-left:4px solid #1a73c9;box-shadow:0 1px 6px rgba(16,42,80,.07)}
.kpi .v{font-size:22px;font-weight:800;color:#12325b;line-height:1.2}
.kpi .l{font-size:11.5px;color:#7286a0;margin-top:2px}
.kpi.g{border-left-color:#12a15c}.kpi.o{border-left-color:#e8912a}.kpi.r{border-left-color:#d94b4b}.kpi.p{border-left-color:#7a5cd6}
.card{background:#fff;border-radius:11px;padding:14px 16px;margin-bottom:14px;box-shadow:0 1px 7px rgba(16,42,80,.07)}
.card>h2{margin:0 0 8px;font-size:15px;color:#12325b;display:flex;align-items:center;gap:8px}
.card>h2 em{font-style:normal;font-size:11.5px;font-weight:500;color:#8a9bb0;margin-left:auto}
.hint{font-size:12.3px;color:#7286a0;margin:0 0 9px}
table{width:100%;border-collapse:collapse;font-size:12.4px}
th{background:#f2f7fd;color:#12325b;font-weight:600;text-align:left;padding:6px 8px;border-bottom:1px solid #dbe6f3;position:sticky;top:0}
td{padding:5px 8px;border-bottom:1px solid #f0f5fa;vertical-align:top}
tr:hover td{background:#f8fbff}
.tag{display:inline-block;border-radius:10px;padding:1px 7px;font-size:11.5px;white-space:nowrap}
.t-red{background:#fdeaea;color:#b52c2c}.t-org{background:#fdf0dc;color:#a8641a}.t-blue{background:#e3eefb;color:#1a5fa8}
.t-green{background:#e2f6ec;color:#0d7a47}.t-grey{background:#eef1f5;color:#6b7c91}
a{color:#1a73c9;text-decoration:none}a:hover{text-decoration:underline}
.li{padding:6px 0;border-bottom:1px solid #f0f5fa}
.li:last-child{border-bottom:0}
.li .c{font-weight:600;color:#12325b}
.li .b{color:#33506f}
.li .o{color:#8a9bb0;font-size:11.8px}
.bar{height:9px;background:#eef3f9;border-radius:5px;overflow:hidden;margin-top:3px}
.bar i{display:block;height:100%;background:linear-gradient(90deg,#1a73c9,#4da3e8)}
.grid2{display:grid;grid-template-columns:1fr 1fr;gap:14px}
@media(max-width:900px){.grid2{grid-template-columns:1fr}}
.foot{font-size:11px;color:#8a9bb0;line-height:1.9;border-top:1px solid #dbe6f3;padding-top:8px;margin-top:4px}
.warnbox{background:#fff8e6;border:1px solid #f0d080;color:#7a5200;border-radius:8px;padding:9px 12px;font-size:12.4px;margin:8px 0}
.__EDITCSS__
"""

# ── 3) 渲染分区 ────────────────────────────────────────────────
def render_list(title, block, cap=None):
    rows = items_of(block)
    if not rows:
        return ''
    h = ['<div class="card"><h2>%s <em>%d 条（全量）</em></h2>' % (esc(title), len(rows))]
    for s, link in rows:
        cust, body, who = split_cust(s)
        line = '<div class="li"><span class="c">%s</span> <span class="b">%s</span>' % (esc(cust), esc(body[:400]) if body else '')
        if who:
            line += ' <span class="o">｜%s</span>' % esc(who)
        if link:
            line += ' <a href="%s" target="_blank" rel="noopener">[详情]</a>' % esc(link)
        h.append(line + '</div>')
    h.append('</div>')
    return ''.join(h)


def render_focus(block):
    rows = items_of(block)
    if not rows:
        return ''
    kinds = {}
    for s, link in rows:
        k = '客诉' if '客诉' in s else ('风险' if '风险' in s else ('重点' if '重点' in s or '进展' in s else '其他'))
        kinds.setdefault(k, []).append((s, link))
    h = ['<div class="card"><h2>⚠️ 今日关注 <em>%d 条（全量·不截断）</em></h2>' % len(rows)]
    h.append('<div class="hint">按类型分组：客诉信号 / 风险信号 / 重点进展。原卡片仅显示前 14 条，此处全量。</div>')
    for k in ('客诉', '风险', '重点', '其他'):
        if k not in kinds:
            continue
        h.append('<h3 style="font-size:13px;color:#1a5fa8;margin:10px 0 4px">%s（%d）</h3>' % (k, len(kinds[k])))
        for s, link in kinds[k]:
            cust, body, who = split_cust(s)
            h.append('<div class="li"><span class="c">%s</span> <span class="b">%s</span>%s</div>' % (
                esc(cust), esc(body[:320]), (' <a href="%s" target="_blank">[详情]</a>' % esc(link)) if link else ''))
    h.append('</div>')
    return ''.join(h)


def render_people(block):
    txt = ' '.join(block)
    m = re.search(r'·\s*([^·]*(?:\(\d+\)[^·]*)+)', txt)
    people = re.findall(r'([^\s、·（(]+)\((\d+)\)', txt)
    if not people:
        return ''
    tot = sum(int(n) for _, n in people)
    h = ['<div class="card"><h2>👥 今日活动人员 <em>%d 人 / %d 条</em></h2>' % (len(people), tot)]
    h.append('<table><tr><th style="width:50px">#</th><th>销售</th><th style="width:70px">活动数</th><th>占比</th></tr>')
    for i, (n, c) in enumerate(sorted(people, key=lambda x: -int(x[1])), 1):
        pct = int(c) / max(tot, 1) * 100
        h.append('<tr><td>%d</td><td>%s</td><td><b>%s</b></td><td><div class="bar"><i style="width:%.0f%%"></i></div></td></tr>'
                 % (i, esc(n), c, pct))
    h.append('</table></div>')
    return ''.join(h)


def render_table(title, block, note=''):
    rows = items_of(block)
    if not rows:
        return ''
    h = ['<div class="card"><h2>%s <em>%d 条</em></h2>' % (esc(title), len(rows))]
    if note:
        h.append('<div class="hint">%s</div>' % note)
    h.append('<table><tr><th style="width:96px">日期</th><th style="width:60px">天数</th><th style="width:150px">状态</th><th>事项</th><th style="width:80px">责任人</th><th style="width:60px">详情</th></tr>')
    for s, link in rows:
        parts = [p.strip() for p in re.split(r'｜', s)]
        date = parts[0] if parts else ''
        status = parts[1] if len(parts) > 1 else ''
        rest = '｜'.join(parts[2:]) if len(parts) > 2 else (parts[-1] if parts else '')
        who = ''
        m = re.search(r'｜\s*([\u4e00-\u9fa5]{2,4})\s*$', s)
        if m:
            who = m.group(1)
            rest = rest.replace('｜' + who, '').strip()
        dval = ''
        if re.match(r'^\d{4}-\d{2}-\d{2}$', date):
            try:
                dval = (datetime.date.fromisoformat(REPORT_DATE) - datetime.date.fromisoformat(date)).days
            except Exception:
                dval = ''
        h.append('<tr><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td><td>%s</td></tr>' % (
            esc(date), (str(dval) + ' 天') if dval != '' else '', esc(status), esc(rest[:220]), esc(who),
            ('<a href="%s" target="_blank">打开</a>' % esc(link)) if link else '—'))
    h.append('</table></div>')
    return ''.join(h)


# 分区路由
body = []
for title, cont, nm in sections:
    t = title
    if '今日关注' in t:
        body.append(render_focus(cont))
    elif '今日活动人员' in t:
        body.append(render_people(cont))
    elif '未关闭客诉' in t:
        body.append(render_table('🔴 未关闭客诉（近30天）', cont, '全量客诉台账见下方「管理视角 · 超期分层」；此处为近 30 天新增明细。'))
    elif '活跃客户' in t:
        body.append(render_list('📊 近7天活跃客户', cont))
    elif '竞品动态' in t:
        body.append(render_list('⚔️ 竞品动态', cont))
    elif '积极进展' in t or '重点项目' in t and not cont:
        body.append(render_list('🟢 ' + nm if '积极' not in nm else '🚀 重点项目 · 积极进展', cont))
    elif '风险' in nm or '受阻' in nm:
        body.append(render_list('🔴 重点项目 · ' + nm, cont))
    elif '回款' in t:
        body.append(render_list('💰 回款/付款跟进', cont))
    elif cont:
        body.append(render_list(t, cont))
body_html = ''.join(body)

# 管理视角（新增）
mg = []
mg.append('<div class="card"><h2>📈 管理视角 · 本月进度与金额 <em>新增</em></h2>')
mg.append('<div class="hint">数据源：kpi_progress.json（%s 刷新）｜ erp_amount.json（ERP 入账至 %s%s）</div>'
          % (str(kpi.get('updated_at', '—'))[:16], erp_last,
             '，导出滞后' if erp_last != '—' and erp_last < REPORT_DATE else ''))
mg.append('<div class="grid2"><div>')
mg.append('<h3 style="font-size:13px;color:#1a5fa8;margin:2px 0 6px">月度累计金额（%s）</h3>' % month_label)
mg.append('<div style="font-size:26px;font-weight:800;color:#12325b">¥%.1f 万</div>' % (m_amt / 10000))
if dept_month:
    tot_d = sum(float(v or 0) for v in dept_month.values() if not isinstance(v, dict)) or 1
    mg.append('<table style="margin-top:8px"><tr><th>部门</th><th style="width:90px">金额</th><th>占比</th></tr>')
    for d, v in sorted(((k, x) for k, x in dept_month.items() if not isinstance(x, dict)), key=lambda x: -float(x[1] or 0))[:8]:
        mg.append('<tr><td>%s</td><td>¥%.1f万</td><td><div class="bar"><i style="width:%.0f%%"></i></div></td></tr>'
                  % (esc(d), float(v or 0) / 10000, float(v or 0) / tot_d * 100))
    mg.append('</table>')
mg.append('</div><div>')
mg.append('<h3 style="font-size:13px;color:#1a5fa8;margin:2px 0 6px">近 14 天 ERP 日入账</h3>')
mg.append('<table><tr><th style="width:96px">日期</th><th style="width:90px">金额</th><th>趋势</th></tr>')
for d in reversed(d_days):
    v = float(daily.get(d) or 0)
    mg.append('<tr><td>%s</td><td>¥%.1f万</td><td><div class="bar"><i style="width:%.0f%%"></i></div></td></tr>'
              % (d, v / 10000, v / d_max * 100))
mg.append('</table></div></div></div>')

mg.append('<div class="card"><h2>⏰ 管理视角 · 未关闭客诉超期分层 <em>新增 · 含全部历史挂起</em></h2>')
mg.append('<div class="hint">数据源：mcp_complaints.json（全量 %d 条，未关闭 %d 条）｜「天数」= 距创建日天数，按当前日期实时计算</div>'
          % (len(comp_items or []), len(open_comp)))
mg.append('<div class="kpis">')
for k, cls in (('>180天', 'r'), ('90-180天', 'o'), ('30-90天', 'p'), ('<30天', 'g')):
    mg.append('<div class="kpi %s"><div class="v">%d</div><div class="l">%s 未关闭</div></div>' % (cls, buckets[k], k))
mg.append('</div>')
mg.append('<h3 style="font-size:13px;color:#1a5fa8;margin:10px 0 5px">责任人 × 挂起量（按最长挂起天数排序）</h3>')
mg.append('<table><tr><th>责任人</th><th style="width:90px">未关闭</th><th style="width:110px">最长挂起</th></tr>')
for who, st in sorted(owner_stat.items(), key=lambda x: -x[1]['oldest'])[:14]:
    tcls = 't-red' if st['oldest'] > 180 else ('t-org' if st['oldest'] > 90 else 't-blue')
    mg.append('<tr><td>%s</td><td>%d</td><td><span class="tag %s">%d 天</span></td></tr>' % (esc(who), st['n'], tcls, st['oldest']))
mg.append('</table>')
mg.append('<h3 style="font-size:13px;color:#1a5fa8;margin:12px 0 5px">挂起最久的 15 条</h3>')
mg.append('<table><tr><th style="width:60px">天数</th><th style="width:96px">创建</th><th>客诉</th><th style="width:150px">客户</th><th style="width:70px">责任人</th><th style="width:70px">详情</th></tr>')
for c in comp_rows[:15]:
    dif = 't-red' if c['days'] > 180 else ('t-org' if c['days'] > 90 else 't-blue')
    mg.append('<tr><td><span class="tag %s">%d</span></td><td>%s</td><td>%s<br><span class="tag t-grey">%s</span></td><td>%s</td><td>%s</td><td><a href="https://project.feishu.cn/br-shgl/khts/detail/%s" target="_blank">打开</a></td></tr>'
              % (dif, c['days'], esc(c['created']), esc(c['name'][:80]), esc(c['status'][:14]), esc(c['cust'][:30]), esc(c['who']), esc(c['id'])))
mg.append('</table></div>')
mg_html = ''.join(mg)

HTML = """<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>宝锐销售日报 __DATE__</title>
<style>__CSS__</style>
<script>__EDITJS__</script></head><body>
<div class="topbar">__LOGO__
<div class="brand">BIORI BIOTECH<small>宝锐生物</small></div>
<div class="ttl">📋 销售日报<span>__DATELINE__</span></div></div>
<div class="wrap">
<div class="kpis">
<div class="kpi"><div class="v">__ACT__</div><div class="l">昨日活动（条）</div></div>
<div class="kpi r"><div class="v">__CMP__</div><div class="l">未关闭客诉（条）</div></div>
<div class="kpi o"><div class="v">__ORD__</div><div class="l">近7天订单（单）</div></div>
<div class="kpi g"><div class="v">¥__AMT__万</div><div class="l">近7天金额（ERP）</div></div>
<div class="kpi p"><div class="v">¥__MAMT__万</div><div class="l">__MLABEL__ 累计（ERP）</div></div>
<div class="kpi r"><div class="v">__OVER__</div><div class="l">超30天未闭环客诉</div></div>
</div>
<div class="warnbox">📄 <b>本页为完整版</b>：所有板块均为全量（原飞书卡片会按条数截断：客诉 12 / 进展 10 / 风险 8 / 关注 14）。数据源与口径见页脚。</div>
__BODY__
__MG__
<div class="card"><h2>📋 数据源与口径</h2><div class="foot">__FOOT__</div></div>
</div>
__TOOLBAR__
</body></html>"""

foot = '<br>'.join(esc(l.strip()) for l in lines[-10:] if l.strip() and not l.startswith('---')) or '—'
html = (HTML.replace('__CSS__', CSS)
        .replace('__LOGO__', ('<img src="%s" alt="BIORI">' % logo) if logo else '')
        .replace('__DATE__', REPORT_DATE).replace('__DATELINE__', meta['date_line'])
        .replace('__ACT__', meta['act']).replace('__CMP__', meta['complaint'])
        .replace('__ORD__', meta['orders']).replace('__AMT__', meta['amount'])
        .replace('__MAMT__', '%.1f' % (m_amt / 10000)).replace('__MLABEL__', use_month)
        .replace('__OVER__', str(buckets['>180天'] + buckets['90-180天'] + buckets['30-90天']))
        .replace('__BODY__', body_html).replace('__MG__', mg_html)
        .replace('__FOOT__', foot))
html = apply_edit(html, ls_key='baoyu-daily-brief-' + REPORT_DATE,
                  file_base='宝锐销售日报_' + REPORT_DATE,
                  editable_default=False,           # 日报是只读交付物：默认阅读模式，客诉/飞书原文等链接随手可点
                  hint_anchor='<div class="warnbox">')
out = os.path.join(OUTDIR, '宝锐销售日报_%s.html' % REPORT_DATE)
open(out, 'w', encoding='utf-8').write(html)
print('✅ %s  %.1f KB' % (out, os.path.getsize(out) / 1024))
print('   分区: 原文章节 %d → 渲染 %d ｜ 今日关注 %d 条 ｜ 客诉表 %d 条'
      % (len(sections), body_html.count('<div class="card">'), len(items_of(dict((t, c) for t, c, _ in sections).get('⚠️ 今日关注', []))), 0))
print('   管理视角: 本月 ¥%.1f万 ｜ 未关闭客诉 %d（>180天 %d）｜ ERP 至 %s ｜ 外链 %d'
      % (m_amt / 10000, len(open_comp), buckets['>180天'], erp_last, html.count('href="http')))

# ── 4) 归档 index + 部署 ──────────────────────────────────────
reports = sorted([f for f in os.listdir(OUTDIR) if f.startswith('宝锐销售日报_') and f.endswith('.html')], reverse=True)
idx = ['<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8"><title>宝锐销售日报 · 归档</title>',
       '<meta name="viewport" content="width=device-width,initial-scale=1"><style>',
       'body{margin:0;background:#eef2f7;font:14px/1.7 -apple-system,"PingFang SC",sans-serif;color:#1f2d3d}',
       '.wrap{max-width:760px;margin:22px auto;background:#fff;border-radius:12px;padding:20px 24px;box-shadow:0 2px 12px rgba(16,42,80,.1)}',
       'h1{font-size:18px;color:#12325b;margin:0 0 4px}.sub{color:#8a9bb0;font-size:12.5px;margin-bottom:14px}',
       'a{display:block;padding:9px 12px;border:1px solid #dbe6f3;border-radius:9px;margin-bottom:8px;color:#1a73c9;text-decoration:none}',
       'a:hover{background:#f5f9ff}.d{font-weight:700;color:#12325b}</style></head><body><div class="wrap">',
       '<h1>📋 宝锐销售日报 · 归档</h1><div class="sub">共 %d 期（最新在上）</div>' % len(reports)]
for f in reports[:60]:
    d = f.replace('宝锐销售日报_', '').replace('.html', '')
    idx.append('<a href="/daily/%s.html"><span class="d">%s</span> ｜ 完整版</a>' % (d, d))
idx.append('</div></body></html>')
open(os.path.join(OUTDIR, 'index.html'), 'w', encoding='utf-8').write(''.join(idx))

if DEPLOY:
    for src, dst in ((out, '/daily/%s.html' % REPORT_DATE),
                     (os.path.join(OUTDIR, 'index.html'), '/daily/index.html')):
        p = subprocess.run(['tcb', 'hosting', 'deploy', src, dst, '-e', ENV],
                           capture_output=True, text=True, timeout=300)
        print('   deploy %s → %s  rc=%d' % (os.path.basename(src), dst, p.returncode))
    print('   CDN: %s/daily/%s.html' % (CDN, REPORT_DATE))
