#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""生成「客户归属待放权确认」清单（自包含 HTML + MD，零外链）。

数据源：.q3scratch/owner_fix_targets.json 的 unowned（46 家「无归属但有订单」）。
风险说明：这 46 家现为【无归属】→ 仅 admin/gm 可见；一旦确认归属，
          /api/sandept 会向所列销售员【开放该客户行的可见性】——属权限变更，故须用户挑选。

产出：
  ~/Desktop/Hermes输出-工作类/客户归属待放权确认_20261006.html
  ~/Desktop/Hermes输出-工作类/客户归属待放权确认_20261006.md
"""
import io, os, sys, json, html
SD = '/Users/liuxinyuan/Desktop/Hermes输出-工作类/脚本'
sys.path.insert(0, SD)
import brand_kit as BK

W = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
SRC = f'{W}/.q3scratch/owner_fix_targets.json'
OUT_HTML = f'{W}/客户归属待放权确认_20261006.html'
OUT_MD = f'{W}/客户归属待放权确认_20261006.md'
DATE = '2026-10-06'

T = json.load(io.open(SRC, encoding='utf-8'))
rows = T['unowned']
rows.sort(key=lambda r: -r['amt_total'])
total_amt = round(sum(r['amt_total'] for r in rows), 2)


def top3(sb):
    items = list(sb.items())[:3]
    return ' / '.join(f'{k} {v}' for k, v in items)


# ── MD ────────────────────────────────────────────────────────────────────────
md = []
md.append(f'# 客户归属待放权确认（46 家 · {DATE}）\n')
md.append('> **风险提示**：以下 46 家客户现为【无归属】状态 —— 在销售工作台 `/api/sandept` 中**仅 admin/gm 可见**。')
md.append('> 若按下表确认归属，则对应销售员将**获得该客户行的可见性（放权）**，属**权限变更**，请逐行挑选后确认。')
md.append(f'> 合计 **{len(rows)} 家 / {total_amt} 万**（2026 年 1-9 月实际，非赠品·标准销售订单·已审核）。\n')
md.append('**主下单人判定规则**：对每个客户，在其 2026 年非赠品·标准销售订单里，按「归并后的销售员」汇总金额，取金额最大者为归属人；单值归属，不拆分。\n')
md.append('| # | 客户 | 2026金额(万) | 实际主下单人 | 下单拆解(前3) | 放权后可见者(销售员+部门) | 是否已选 |')
md.append('|---:|---|---:|---|---|---|---|')
for i, r in enumerate(rows, 1):
    md.append(f"| {i} | {r['cust']} | {r['amt_total']} | {r['dominant']} | {top3(r['sp_breakdown'])} "
              f"| {r['dominant']}（{r['dominant_dept']}） | ☐ |")
md.append('')
md.append('---')
md.append(f'*勾选后导出 JSON（仅含被勾选客户 → 归属人）。本清单**未部署、未上传**。*  ')
md.append('*宝锐生物 · 守护生命健康，缔造美好生活*')
io.open(OUT_MD, 'w', encoding='utf-8').write('\n'.join(md) + '\n')

# ── HTML ──────────────────────────────────────────────────────────────────────
payload = [{'i': i, 'cust': r['cust'], 'amt': r['amt_total'], 'owner': r['dominant'],
            'dept': r['dominant_dept'], 'sb': top3(r['sp_breakdown'])}
           for i, r in enumerate(rows, 1)]

TR = []
for r in payload:
    TR.append(
        f'<tr data-owner="{html.escape(r["owner"])}" data-amt="{r["amt"]}">'
        f'<td class="num">{r["i"]}</td>'
        f'<td class="cust">{html.escape(r["cust"])}</td>'
        f'<td class="num amt">{r["amt"]:.2f}</td>'
        f'<td><b>{html.escape(r["owner"])}</b></td>'
        f'<td class="sb">{html.escape(r["sb"])}</td>'
        f'<td><b>{html.escape(r["owner"])}</b>（{html.escape(r["dept"])}）</td>'
        f'<td class="pick"><input type="checkbox" class="rowck"></td>'
        f'</tr>')

HTML = f'''<!DOCTYPE html>
<html lang="zh-CN"><head><meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>客户归属待放权确认（46 家）· {DATE}</title>
<style>
*{{box-sizing:border-box}}
body{{margin:0;background:#f4f7fc;color:#1f2d3d;
 font-family:-apple-system,BlinkMacSystemFont,"PingFang SC","Microsoft YaHei",sans-serif;font-size:14px}}
.wrap{{max-width:1560px;margin:0 auto;padding:20px 22px 40px}}
h1{{font-size:21px;margin:6px 0 4px;color:#0d47a1}}
.sub{{color:#5f6368;font-size:12.5px;margin-bottom:14px}}
.risk{{background:#fff7e6;border:1px solid #ffd591;border-left:5px solid #fa8c16;
 border-radius:8px;padding:13px 16px;margin:12px 0 16px;line-height:1.75;color:#613400}}
.risk b{{color:#d4380d}}
.rule{{background:#e8f1fe;border:1px solid #b9d3f7;border-radius:8px;padding:11px 15px;
 margin:0 0 16px;color:#1a4a8a;line-height:1.7}}
.bar{{position:sticky;top:0;z-index:5;background:#fff;border:1px solid #dbe4f0;border-radius:10px;
 padding:11px 15px;margin-bottom:12px;display:flex;flex-wrap:wrap;gap:10px;align-items:center;
 box-shadow:0 2px 8px rgba(13,71,161,.08)}}
.bar button{{border:0;border-radius:7px;padding:8px 15px;font-size:13.5px;cursor:pointer;
 font-weight:600;font-family:inherit}}
.b-primary{{background:#1565c0;color:#fff}} .b-primary:hover{{background:#0d47a1}}
.b-ghost{{background:#eef3fb;color:#1565c0;border:1px solid #c5d8f2}}
.b-ghost:hover{{background:#e0ebfa}}
.stat{{margin-left:auto;font-size:13px;color:#455a64}}
.stat b{{color:#1565c0;font-size:15px}}
table{{width:100%;border-collapse:separate;border-spacing:0;background:#fff;
 border:1px solid #dbe4f0;border-radius:10px;overflow:hidden}}
thead th{{background:#1565c0;color:#fff;font-weight:600;
 padding:11px 10px;text-align:left;font-size:13px;white-space:nowrap}}
tbody td{{padding:9px 10px;border-bottom:1px solid #eef2f8;vertical-align:middle;line-height:1.5}}
tbody tr:nth-child(even){{background:#fafcff}}
tbody tr:hover{{background:#eef5ff}}
td.num{{text-align:right;font-variant-numeric:tabular-nums;color:#546e7a;white-space:nowrap}}
td.amt{{font-weight:700;color:#0d47a1}}
td.cust{{font-weight:600;color:#1a2b3c;max-width:330px}}
td.sb{{color:#607d8b;font-size:12.5px}}
td.pick{{text-align:center;width:64px}}
td.pick input{{width:18px;height:18px;cursor:pointer;accent-color:#1565c0}}
tbody tr.sel{{background:#e5f0ff !important;box-shadow:inset 3px 0 0 #1565c0}}
.foot-note{{margin-top:16px;color:#78909c;font-size:12px;line-height:1.8}}
@media(max-width:900px){{td.cust,td.sb{{max-width:none;white-space:normal}}thead th{{top:0}}}}
</style></head><body>

<div class="wrap">
  <h1>客户归属待放权确认 · 46 家</h1>
  <div class="sub">生成日期 {DATE} ｜ 数据源：ERP 销售订单_20260101-20260930（2026 年 1-9 月实际，非赠品·标准销售订单·已审核）</div>

  <div class="risk">
    <b>⚠️ 风险提示（权限变更）</b>：以下 <b>{len(rows)} 家</b>客户现为【无归属】状态 —— 在销售工作台
    <code>/api/sandept</code> 中<b>仅 admin / gm 可见</b>，任何普通销售员/管理者都看不到。<br>
    若在此确认归属，则所列销售员将<b>获得该客户行的可见性（放权）</b> —— 这是<b>可见范围从 admin-only 的扩大</b>，
    属权限变更。请逐行挑选、确认后再由管理员落库（本页<b>仅供挑选</b>，<b>不部署、不上传</b>）。<br>
    合计金额：<b>{len(rows)} 家 / {total_amt} 万</b>（未放权 → 仍仅 admin/gm 可见）。
  </div>

  <div class="rule">
    <b>主下单人判定规则</b>：对每个客户，在其 2026 年【非赠品 · 标准销售订单 · 已审核】里，
    按「归并后的销售员」（黄明月/张立娅/刘新元 → 刘子研；离职人员 → 区域经理）汇总金额，
    <b>取金额最大者为归属人</b>；客户级归属为单值，整户归该人，<b>不拆分</b>。
  </div>

  <div class="bar">
    <button class="b-primary" id="btn-exp">⬇ 导出 JSON（仅被勾选）</button>
    <button class="b-ghost" id="btn-all">全选</button>
    <button class="b-ghost" id="btn-none">清空</button>
    <div class="stat">已选 <b id="sc">0</b> 家 ／ 金额 <b id="sa">0.00</b> 万 ｜ 全表 {len(rows)} 家 / {total_amt} 万</div>
  </div>

  <table>
    <thead><tr>
      <th class="num">#</th><th>客户</th><th class="num">2026 金额(万)</th><th>实际主下单人</th>
      <th>下单拆解(前3)</th><th>放权后谁会看见（销售员+部门）</th><th class="pick">是否已选</th>
    </tr></thead>
    <tbody id="tb">
{chr(10).join(TR)}
    </tbody>
  </table>

  <div class="foot-note">
    · 勾选后点击「导出 JSON」下载 <code>owner_release_selected_*.json</code>，内容 = 被勾选客户 → 归属人（含金额与计数）。<br>
    · 本清单为**待确认**状态：未部署、未上传、未改动 <code>数据/三部门逐月_2026.json</code>。确认后需另行落库。
  </div>
</div>

<script>
(function(){{
  var tb=document.getElementById('tb'), sc=document.getElementById('sc'), sa=document.getElementById('sa');
  function upd(){{
    var n=0,a=0;
    Array.prototype.forEach.call(tb.querySelectorAll('tr'),function(tr){{
      var ck=tr.querySelector('.rowck'), hit=ck&&ck.checked;
      tr.classList.toggle('sel',!!hit);
      if(hit){{n++;a+=parseFloat(tr.getAttribute('data-amt'))||0;}}
    }});
    sc.textContent=n; sa.textContent=a.toFixed(2);
    return {{n:n,a:a}};
  }}
  tb.addEventListener('change',function(e){{ if(e.target.classList.contains('rowck')) upd(); }});
  document.getElementById('btn-all').onclick=function(){{ Array.prototype.forEach.call(tb.querySelectorAll('.rowck'),function(c){{c.checked=true;}}); upd(); }};
  document.getElementById('btn-none').onclick=function(){{ Array.prototype.forEach.call(tb.querySelectorAll('.rowck'),function(c){{c.checked=false;}}); upd(); }};
  document.getElementById('btn-exp').onclick=function(){{
    var sel=[];
    Array.prototype.forEach.call(tb.querySelectorAll('tr'),function(tr){{
      var ck=tr.querySelector('.rowck');
      if(ck&&ck.checked){{
        sel.push({{cust:tr.querySelector('.cust').textContent, owner:tr.getAttribute('data-owner'),
                   amt:parseFloat(tr.getAttribute('data-amt'))}});
      }}
    }});
    if(!sel.length){{ alert('尚未勾选任何客户'); return; }}
    var amt=sel.reduce(function(s,x){{return s+(x.amt||0);}},0);
    var map={{}}; sel.forEach(function(x){{map[x.cust]=x.owner;}});
    var out={{generated:new Date().toISOString(), source:'客户归属待放权确认_{DATE}',
             note:'被勾选客户 → 归属人（放权确认稿；未部署）', count:sel.length,
             amount:Math.round(amt*100)/100, map:map, selected:sel}};
    var blob=new Blob([JSON.stringify(out,null,2)],{{type:'application/json;charset=utf-8'}});
    var a=document.createElement('a'); a.href=URL.createObjectURL(blob);
    a.download='owner_release_selected_'+new Date().toISOString().slice(0,10)+'.json';
    document.body.appendChild(a); a.click();
    setTimeout(function(){{URL.revokeObjectURL(a.href);a.remove();}},400);
  }};
  upd();
}})();
</script>
</body></html>'''

HTML = BK.inject(HTML, theme='light', tag='销售工作台 · 权限治理',
                 footer_extra=f'客户归属待放权确认 · {DATE} · 待确认（未部署）')
io.open(OUT_HTML, 'w', encoding='utf-8').write(HTML)

# ── 自检 ──────────────────────────────────────────────────────────────────────
import re
txt = io.open(OUT_HTML, encoding='utf-8').read()
bad_ext = re.findall(r'(?:src|href)\s*=\s*["\']https?://', txt)
v = BK.verify(txt)
n_ck = txt.count('class="rowck"')
print(f"HTML={OUT_HTML}  {len(txt)} 字节")
print(f"  行数(勾选框)={n_ck}  外链={len(bad_ext)}  brand自检={v}")
print(f"  全部通过={n_ck == len(rows) == 46 and not bad_ext and all(v.values())}")
print(f"MD={OUT_MD}  {os.path.getsize(OUT_MD)} 字节")
