#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""客户 2 年同比（集团口径）→ deck 用自包含 HTML 模块页（分析文字留空位）

输入：数据/客户2年同比_集团口径.json
输出：销售分析/客户2年同比_大客户部_集团口径.html
"""
import html as H
import io
import json
import os
import re
import sys

sys.path.insert(0, '/Users/liuxinyuan/Desktop/Hermes输出-工作类/脚本')
import brand_kit as BK  # noqa: E402

W = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
D = json.load(io.open(W + '/数据/客户2年同比_集团口径.json', encoding='utf-8'))
ROWS = D['rows']
DK = [r for r in ROWS if r['部门'] == '大客户部']
wan = lambda x: x / 10000.0
money = lambda x, d=1: format(x, ',.%df' % d)


def yoy_txt(r):
    if r['2025_1-9月'] < 1 and r['2026_1-9月'] > 0:
        return '新增', 'new'
    if r['2026_1-9月'] < 1 and r['2025_1-9月'] > 0:
        return '流失', 'lost'
    if r['同比'] is None:
        return '—', ''
    return '%+.1f%%' % r['同比'], ('up' if r['同比'] >= 0 else 'down')


t25 = sum(r['2025_1-9月'] for r in DK)
t26 = sum(r['2026_1-9月'] for r in DK)
yoy_dk = (t26 - t25) / t25 * 100
dk26_all = sum(r['2026_1-9月'] for r in ROWS)
G_UP = [r for r in DK if r['同比'] is not None and r['同比'] >= 30 and r['2025_1-9月'] > 0]
G_DOWN = [r for r in DK if r['同比'] is not None and r['同比'] <= -20 and r['2026_1-9月'] > 0]
G_NEW = [r for r in DK if r['2025_1-9月'] < 1 and r['2026_1-9月'] > 0]
G_LOST = [r for r in DK if r['2026_1-9月'] < 1 and r['2025_1-9月'] > 0]
G_UP.sort(key=lambda r: -r['增减额'])
G_DOWN.sort(key=lambda r: r['增减额'])
G_NEW.sort(key=lambda r: -r['2026_1-9月'])
G_LOST.sort(key=lambda r: -r['2025_1-9月'])

# 主表（Top20 + 其余）
top = DK[:20]
rest = DK[20:]
main = []
for i, r in enumerate(top, 1):
    y, cls = yoy_txt(r)
    main.append('<tr><td class="n">%d</td><td class="b">%s</td><td class="n">%s</td><td class="n">%s</td>'
                '<td class="n %s">%s</td><td class="n %s">%s</td><td class="n">%d→%d</td></tr>'
                % (i, H.escape(r['集团']), money(wan(r['2025_1-9月'])), money(wan(r['2026_1-9月'])),
                   cls, y, 'up' if r['增减额'] >= 0 else 'down', ('%+s' % money(wan(r['增减额']))) if r['增减额'] else '0.0',
                   r['2025实体数'], r['2026实体数']))
rest25 = sum(r['2025_1-9月'] for r in rest)
rest26 = sum(r['2026_1-9月'] for r in rest)
main.append('<tr><td class="n">—</td><td class="b">其余 %d 个集团/客户</td><td class="n">%s</td><td class="n">%s</td>'
            '<td class="n">%+.1f%%</td><td class="n %s">%+s</td><td class="n">—</td></tr>'
            % (len(rest), money(wan(rest25)), money(wan(rest26)), (rest26 - rest25) / rest25 * 100 if rest25 else 0,
               'up' if rest26 >= rest25 else 'down', money(wan(rest26 - rest25))))
main.append('<tr class="tot"><td class="n">—</td><td class="b">大客户部 合计</td><td class="n">%s</td><td class="n">%s</td>'
            '<td class="n %s">%+.1f%%</td><td class="n %s">%+s</td><td class="n">—</td></tr>'
            % (money(wan(t25)), money(wan(t26)), 'up' if yoy_dk >= 0 else 'down', yoy_dk,
               'up' if t26 >= t25 else 'down', money(wan(t26 - t25))))
main = ''.join(main)


def blocks(lst, amt_key, other_key, tail):
    return ''.join('<li><b>%s</b> <span class="o">%s</span> → <span class="n2">%s</span>'
                   '<span class="d %s">%s</span>%s</li>'
                   % (H.escape(r['集团']), money(wan(r[amt_key])), money(wan(r[other_key])),
                      'up' if r[other_key] >= r[amt_key] else 'down',
                      (('%+.1f%%' % r['同比']) if r['同比'] is not None else ''),
                      tail(r)) for r in lst)


# 双条对比图（Top12）
CH = [r for r in DK[:12]]
maxv = max(max(r['2025_1-9月'], r['2026_1-9月']) for r in CH) or 1
bars = []
for i, r in enumerate(CH):
    y = 10 + i * 30
    w1 = max(1.0, r['2025_1-9月'] / maxv * 620)
    w2 = max(1.0, r['2026_1-9月'] / maxv * 620)
    bars.append('<text x="0" y="%d" font-size="12" fill="#374151">%s</text>' % (y + 12, H.escape(r['集团'][:16])))
    bars.append('<rect x="150" y="%d" width="%.1f" height="9" rx="2" fill="#c7d2e0"></rect>' % (y, w1))
    bars.append('<rect x="150" y="%d" width="%.1f" height="9" rx="2" fill="#1a5276"></rect>' % (y + 12, w2))
    bars.append('<text x="%.1f" y="%d" font-size="10.5" fill="#6b7280">%s</text>' % (150 + w1 + 6, y + 8, money(wan(r['2025_1-9月']))))
    bars.append('<text x="%.1f" y="%d" font-size="10.5" fill="#1a5276" font-weight="700">%s</text>' % (150 + w2 + 6, y + 20, money(wan(r['2026_1-9月']))))
svg = ('<svg viewBox="0 0 1100 %d" width="100%%" height="%d" role="img" aria-label="集团两年对比">'
       '<text x="150" y="8" font-size="10.5" fill="#9aa3b2">浅色=2025 1-9月 ｜ 深色=2026 1-9月（单位：万元）</text>%s</svg>'
       % (10 + len(CH) * 30 + 12, 10 + len(CH) * 30 + 12, ''.join(bars)))

HTML = """<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>客户 2 年同比（集团口径）· 大客户部 · 2026 vs 2025</title>
<style>
*{box-sizing:border-box}
body{margin:0;background:#f4f6fa;font-family:-apple-system,"PingFang SC","Microsoft YaHei",sans-serif;color:#20242c}
.wrap{max-width:1260px;margin:0 auto;padding:18px 20px 44px}
h1{font-size:24px;margin:14px 0 6px}
.meta{color:#6b7280;font-size:12.5px;line-height:1.85;margin-bottom:14px}
.kpis{display:grid;grid-template-columns:repeat(5,1fr);gap:11px;margin:14px 0 18px}
.kpi{background:#fff;border:1px solid #e6eaf2;border-radius:10px;padding:12px 13px}
.kpi .l{font-size:12px;color:#6b7280}.kpi .v{font-size:20px;font-weight:800;margin-top:5px}
.kpi .s{font-size:11px;color:#9aa3b2;margin-top:3px}
.card{background:#fff;border:1px solid #e6eaf2;border-radius:10px;padding:16px 18px;margin-bottom:16px}
.card h2{font-size:15.5px;margin:0 0 12px;padding-left:9px;border-left:3px solid #1a5276}
table{width:100%;border-collapse:collapse;font-size:12.5px}
th{background:#1a5276;color:#fff;font-weight:600;padding:8px 9px;text-align:left;white-space:nowrap}
td{padding:8px 9px;border-bottom:1px solid #eef1f6}
td.n{text-align:right;font-variant-numeric:tabular-nums}
td.b{font-weight:700}
tr.tot td{background:#eef4fa;font-weight:800}
.up{color:#15803d;font-weight:700}.down{color:#b91c1c;font-weight:700}
.new{color:#1d4ed8;font-weight:700}.lost{color:#7f1d1d;font-weight:700}
.note{background:#fbfcfe;border:1px dashed #cbd5e1;border-radius:8px;padding:11px 13px;font-size:12px;color:#4b5563;line-height:1.9}
.g3{display:grid;grid-template-columns:repeat(3,1fr);gap:12px}
.g3 h3{font-size:13.5px;margin:2px 0 8px;color:#1a5276}
ul{list-style:none;padding:0;margin:0;font-size:12.5px}
li{padding:6px 8px;border-bottom:1px dashed #eef1f6;display:flex;gap:6px;flex-wrap:wrap;align-items:baseline}
li .o{color:#9aa3b2;text-decoration:line-through}
li .n2{font-weight:700}
li .d{font-size:11.5px;font-weight:700}
.slot{border:1.5px dashed #b9c4d4;background:#fcfdff;border-radius:8px;padding:14px 16px;min-height:104px}
.slot .t{font-size:12px;color:#8b96a8;margin-bottom:6px;font-weight:700}
@media(max-width:1150px){.kpis{grid-template-columns:repeat(3,1fr)}.g3{grid-template-columns:1fr}}
</style></head><body><div class="wrap">
<h1>客户 2 年同比（集团口径）· 大客户部</h1>
<div class="meta">
口径：<b>2026 年 1-9 月 vs 2025 年 1-9 月</b>（同期对比）｜ 归集：<b>有集团按集团、无集团按客户本体</b>（集团取 CRM 权威映射）<br>
数据源：ERP 销售订单 2026 主档 ＋ 2025 全年订单（标准销售订单 ＋ 非赠品 ＋ 剔除宝锐系）｜ 2025 逐月已与源文件透视表逐月对平 ｜ 生成 {{gen}}
</div>
<div class="kpis">
  <div class="kpi"><div class="l">大客户部 2026 1-9月</div><div class="v">{{t26}}</div><div class="s">万元 ｜ {{n_grp}} 个集团/客户</div></div>
  <div class="kpi"><div class="l">2025 同期</div><div class="v" style="color:#6b7280">{{t25}}</div><div class="s">万元</div></div>
  <div class="kpi"><div class="l">同比</div><div class="v {{yoy_cls}}">{{yoy}}</div><div class="s">增减 {{diff}} 万元</div></div>
  <div class="kpi"><div class="l">增长集团（≥+30%）</div><div class="v" style="color:#15803d">{{n_up}}</div><div class="s">合计 {{amt_up}} 万元</div></div>
  <div class="kpi"><div class="l">下滑集团（≤−20%）</div><div class="v" style="color:#b91c1c">{{n_down}}</div><div class="s">合计 {{amt_down}} 万元</div></div>
</div>
<div class="card"><h2>一、集团 2 年对比（Top 12）</h2>{{svg}}</div>
<div class="card"><h2>二、集团级 2 年同比明细（大客户部，万元）</h2>
<table><thead><tr><th style="width:34px">#</th><th>集团 / 客户</th><th style="text-align:right">2025 1-9月</th>
<th style="text-align:right">2026 1-9月</th><th style="text-align:right">同比</th><th style="text-align:right">增减额</th>
<th style="text-align:right">实体数</th></tr></thead><tbody>{{main}}</tbody></table></div>
<div class="card"><h2>三、结构变化：增长 / 下滑 / 新增 / 流失</h2>
<div class="g3">
  <div><h3>📈 增长（≥+30%）· {{n_up}} 个 · +{{amt_up}} 万</h3><ul>{{b_up}}</ul></div>
  <div><h3>📉 下滑（≤−20%）· {{n_down}} 个 · {{amt_down}} 万</h3><ul>{{b_down}}</ul></div>
  <div><h3>🆕 新增 · {{n_new}} 个 · +{{amt_new}} 万</h3><ul>{{b_new}}</ul>
       <h3 style="margin-top:12px">⚠️ 流失（2025 有 2026 无）· {{n_lost}} 个 · −{{amt_lost}} 万</h3><ul>{{b_lost}}</ul></div>
</div></div>
<div class="card"><h2>四、分析（留白 · 待填写）</h2>
<div class="slot"><div class="t">▍本期同比结论与归因（由部门负责人填写）</div></div>
<div class="slot" style="margin-top:12px;min-height:88px"><div class="t">▍下一步动作 / 责任人 / 时间节点（由部门负责人填写）</div></div>
</div>
<div class="card"><h2>五、口径说明</h2>
<div class="note">
① 期间：2026 年 1-9 月 vs 2025 年 1-9 月（<b>同期口径</b>，不含 10 月；2026 年 10 月为在途数据）；<br>
② 归集：<b>有集团的客户并入集团</b>（集团名取 CRM 权威映射），<b>无集团按客户本体</b>，逐条可回溯到实体数（"实体数"列 = 该集团当年的开票客户家数）；<br>
③ 金额口径：标准销售订单 ＋ 已审核 ＋ 非赠品 ＋ 剔除宝锐系关联交易，与工作台 ERP 口径一致；<br>
④ 大客户部合计 2026 1-9月 {{t26}} 万元，占全司同期 {{share}}%（全司 {{all26}} 万元）；<br>
⑤ 2025 数据自「2025全年订单.xlsx」，自算逐月金额已与源文件透视表逐月对平（差 0.00）。
</div></div>
</div><script>/* 静态自包含，无外部依赖 */</script></body></html>"""

tail_txt = lambda r: (' ｜ 实体 %d→%d' % (r['2025实体数'], r['2026实体数']))
VALS = {
    'gen': D['meta']['gen'] + ' 08:50',
    't26': money(wan(t26)), 't25': money(wan(t25)), 'n_grp': str(len(DK)),
    'yoy': '%+.1f%%' % yoy_dk, 'yoy_cls': 'up' if yoy_dk >= 0 else 'down', 'diff': ('%+s' % money(wan(t26 - t25))),
    'n_up': str(len(G_UP)), 'amt_up': money(wan(sum(r['增减额'] for r in G_UP))),
    'n_down': str(len(G_DOWN)), 'amt_down': money(wan(sum(r['增减额'] for r in G_DOWN))),
    'n_new': str(len(G_NEW)), 'amt_new': money(wan(sum(r['2026_1-9月'] for r in G_NEW))),
    'n_lost': str(len(G_LOST)), 'amt_lost': money(wan(sum(r['2025_1-9月'] for r in G_LOST))),
    'svg': svg, 'main': main,
    'b_up': blocks(G_UP, '2025_1-9月', '2026_1-9月', tail_txt),
    'b_down': blocks(G_DOWN, '2025_1-9月', '2026_1-9月', tail_txt),
    'b_new': blocks(G_NEW, '2025_1-9月', '2026_1-9月', tail_txt),
    'b_lost': blocks(G_LOST, '2026_1-9月', '2025_1-9月', tail_txt),
    'share': '%.1f' % (t26 / dk26_all * 100), 'all26': money(wan(dk26_all)),
}
page = HTML
for k, v in VALS.items():
    page = page.replace('{{%s}}' % k, str(v))
assert not re.findall(r'\{\{\w+\}\}', page), set(re.findall(r'\{\{\w+\}\}', page))
_t = re.sub(r'<[^>]+>', '', page)
for _bad in ('万 万', '万万元', '万元万', '万万'):
    assert _bad not in _t, '❌ 单位重复: %s' % _bad
page = BK.inject(page, theme='light', tag='营销中心 · 销售管理', footer_extra='数据源：ERP 销售订单（2026 主档 / 2025 全年订单）')
v = BK.verify(page)
assert all(v.values()), [k for k, ok in v.items() if not ok]
out = W + '/销售分析/客户2年同比_大客户部_集团口径.html'
io.open(out, 'w', encoding='utf-8').write(page)
print('✅ HTML:', out, '%.0f KB' % (os.path.getsize(out) / 1024))
print('   大客户部 2025 %s 万 → 2026 %s 万 ｜ 同比 %+.1f%% ｜ 集团/客户 %d 个'
      % (money(wan(t25)), money(wan(t26)), yoy_dk, len(DK)))
print('   增长 %d 个（+%s 万）｜ 下滑 %d 个（%s 万）｜ 新增 %d 个（+%s 万）｜ 流失 %d 个（−%s 万）'
      % (len(G_UP), VALS['amt_up'], len(G_DOWN), VALS['amt_down'], len(G_NEW), VALS['amt_new'], len(G_LOST), VALS['amt_lost']))
print('   品牌体检:', v)
