#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""生成邮件版复盘报告 HTML — 全内联样式 + table 布局,QQ/163 邮箱兼容(无CSS变量/flex/grid)"""
import re, html

MD = "/root/.hermes/skills/qianxiaodou-engine/data/reports/report_20260813.md"
OUT = "/root/web_reports/report_20260813_email.html"

INK, SUB, FAINT = "#1a1a1a", "#6b7280", "#9ca3af"
LINE, LINE2, UP, DOWN, GOLD, BLUE = "#e5e5e5", "#d9d9d9", "#d03030", "#0a7d4d", "#b8893f", "#2f6f9f"
BGSOFT, BGWARN = "#faf8f5", "#fdf6ef"
F_SANS = '"Helvetica Neue",Arial,"PingFang SC","Microsoft YaHei",sans-serif'
F_SONG = 'Georgia,"Songti SC","SimSun",serif'

def esc(s):
    return html.escape(s)

def style(s):
    return f' style="{s}"'

def box(w="100%", p="0", bg="#fff", align=None, border=None):
    s = f"width:{w};padding:{p};background:{bg}"
    if border: s += f";border:{border}"
    a = f' align="{align}"' if align else ""
    return f"<table width='{w}' cellpadding='0' cellspacing='0' border='0'{a}{style(s)}"

def tr(s, bg=None, bd=None):
    st = ""
    if bg: st += f"background:{bg};"
    if bd: st += f"border-bottom:{bd};"
    return f"<tr{style(st)}>{s}</tr>"

def td(s, w=None, p="10px", align=None, valign="top", color=INK, fs=14, fw=400, ff=None,
       bd=None, bg=None, extra=None, wrap=True):
    st = f"font-family:{ff or F_SANS};font-size:{fs}px;font-weight:{fw};color:{color};"
    st += f"padding:{p};text-align:{align or 'left'};vertical-align:{valign};"
    if w: st += f"width:{w};"
    if bd: st += f"border-bottom:{bd};"
    if bg: st += f"background:{bg};"
    if not wrap: st += "white-space:nowrap;"
    if extra: st += extra
    return f"<td{style(st)}>{s}</td>"

def span(s, color=None, fw=None, fs=None, ff=None, extra=None):
    st = f"font-family:{ff or F_SANS};"
    if color: st += f"color:{color};"
    if fw: st += f"font-weight:{fw};"
    if fs: st += f"font-size:{fs}px;"
    if extra: st += extra
    return f"<span{style(st)}>{s}</span>"

# ── 数字着色: A股 涨红跌绿 ───────────────────────────
NUM_RE = re.compile(r"([+-])([\d,]+\.?\d*)(%)?")
def colorize(s, base=INK, fs=14, fw=400):
    """把 s 中的 +/-数字% 染红(正)/绿(负); +数字无%也染红"""
    out, last = [], 0
    for m in NUM_RE.finditer(s):
        out.append(esc(s[last:m.start()]))
        sign, num, pct = m.group(1), m.group(2), m.group(3)
        c = UP if sign == "+" else DOWN
        out.append(span(esc(m.group(0)), color=c, fw=fw, fs=fs, ff=F_SANS))
        last = m.end()
    out.append(esc(s[last:]))
    return "".join(out)

def cell_text(s, fs=14, fw=400, color=INK, numalign=False):
    """单元格内容: 着色 + 数字右对齐处理"""
    s = s.strip()
    if s == "-":
        return span("-", color=FAINT)
    return colorize(s, base=color, fs=fs, fw=fw)

def render_table(rows, widths=None, aligns=None, num_cols=(), fs=13.5, head_bg=None,
                 head_color=SUB, first_bold_cols=()):
    """rows: list[list[str]]; 首行为表头"""
    ncol = len(rows[0])
    hd = rows[0]
    body = []
    hcells = ""
    for j, htxt in enumerate(hd):
        al = (aligns[j] if aligns else "left")
        hcells += td(esc(htxt), w=(widths[j] if widths else None), p="7px 10px",
                     align=al, color=head_color, fs=11.5, fw=600, bd=f"1.5px solid {INK}",
                     bg=head_bg, extra="letter-spacing:.08em;font-family:"+F_SANS)
    body.append(tr(hcells))
    for i, row in enumerate(rows[1:]):
        cells = ""
        for j, v in enumerate(row):
            al = (aligns[j] if aligns else "left")
            is_num = j in num_cols
            fw = 700 if (j in first_bold_cols or is_num) else 400
            if is_num:
                cells += td(cell_text(v, fs=fs, fw=fw), p="8px 10px", align=al,
                            bd=f"1px solid {LINE}",
                            extra="font-family:"+F_SANS+";font-variant-numeric:tabular-nums;white-space:nowrap")
            else:
                cells += td(cell_text(v, fs=fs, fw=fw), p="8px 10px", align=al, bd=f"1px solid {LINE}")
        body.append(tr(cells))
    return "<table cellpadding='0' cellspacing='0' border='0' width='100%' style='width:100%;border-collapse:collapse;'>" \
           + "".join(body) + "</table>"

def list_items(items, numbers=None, gap=7):
    """通用列表: items=[(bold, text)]; numbers=None 无编号, 或 [] 每项给编号字符串"""
    rows = []
    for i, (b, t) in enumerate(items):
        hdr = ""
        if numbers is not None:
            n = numbers[i] if numbers else str(i+1)
            hdr = td(span(n, color=GOLD, fw=800, fs=15, ff=F_SANS), w="34px", p="0 8px 0 0")
        bold = f"<b{style(f'color:{INK};font-weight:700')}>{esc(b)}</b>" if b else ""
        txt = colorize(t) if t else ""
        rows.append(tr(hdr + td(bold + txt, p=f"2px 0 {gap}px")))
    return f"<table cellpadding='0' cellspacing='0' border='0' width='100%'>{''.join(rows)}</table>"

def badge(txt, color, bg):
    return span(txt, color=color, fw=700, fs=11, extra=f"background:{bg};border:1px solid {color};" 
        + "border-radius:999px;padding:2px 9px;letter-spacing:.08em")

def trans_block(text):
    """传导段落: 浅金底 + 左边框"""
    return (f"<table cellpadding='0' cellspacing='0' border='0' width='100%' style='margin:4px 0 12px'>"
            f"<tr><td style='padding:8px 12px;background:{BGWARN};border-left:3px solid {GOLD};"
            f"border-radius:0 8px 8px 0;font-size:13px;line-height:1.65;color:#3d3d3d'>"
            + span("→ 传导 ", color=GOLD, fw=800, fs=12) + colorize(text, fs=13) + "</td></tr></table>")

def sec_header(no, cn, en):
    """模块标题: 大编号 + 中文 + 英文"""
    return (f"<table cellpadding='0' cellspacing='0' border='0' width='100%' style='margin:30px 0 14px'>"
            f"<tr><td style='padding-bottom:10px;border-bottom:2px solid {INK}'>"
            f"<table cellpadding='0' cellspacing='0' border='0' width='100%'><tr>"
            f"<td width='54' style='font-family:{F_SANS};font-size:40px;font-weight:800;color:{INK};line-height:1'>"
            f"{esc(no)}</td>"
            f"<td style='font-family:{F_SONG};font-size:22px;font-weight:700;color:{INK};letter-spacing:.06em'>{esc(cn)}</td>"
            f"<td align='right' style='font-family:{F_SANS};font-size:9.5px;color:{FAINT};letter-spacing:.28em'>{esc(en)}</td>"
            f"</tr></table></td></tr></table>")

def sub_label(t):
    return (f"<table cellpadding='0' cellspacing='0' border='0' width='100%' style='margin:16px 0 8px'>"
            f"<tr><td style='font-family:{F_SANS};font-size:12px;font-weight:700;color:{GOLD};"
            f"letter-spacing:.24em'>{esc(t)}</td></tr></table>")

def para(t, fs=13.5, mb=10, color="#3d3d3d", p="0", bold_prefix=None, bg=None, bd_left=None, radius="0"):
    st = f"font-size:{fs}px;line-height:1.7;color:{color};margin-bottom:{mb}px;padding:{p}"
    if bg: st += f";background:{bg}"
    if bd_left: st += f";border-left:3px solid {bd_left};border-radius:{radius}"
    pre = f"<b style='color:{INK}'>{esc(bold_prefix)}</b>" if bold_prefix else ""
    return f"<table cellpadding='0' cellspacing='0' border='0' width='100%'><tr><td{style(st)}>{pre}{colorize(t, fs=fs)}</td></tr></table>"

# ═══════════════ 解析 md ═══════════════
raw = open(MD, encoding="utf-8").read()
lines = raw.split("\n")
i = 0
sections = []  # (level, title, content_lines)
cur = {"no": None, "title": None, "lines": []}

def flush():
    if cur["title"] is not None:
        sections.append((cur["no"], cur["title"], cur["lines"]))
    cur["lines"] = []

for ln in lines:
    m = re.match(r"^## (.*)$", ln)
    if m:
        flush()
        t = m.group(1)
        mm = re.match(r"模块 (\d):(.*)", t)
        cur["no"], cur["title"] = (mm.group(1), mm.group(2).strip()) if mm else (None, t)
        continue
    cur["lines"].append(ln)
flush()

# 头部 blockquote 区(行情行) — 在第一段
bq_lines = []
for ln in lines:
    if ln.startswith(">"):
        bq_lines.append(ln[1:].strip())
    elif bq_lines:
        break

# 摘要区: 在 bq 之后 "复盘摘要:" 到 "---"
sum_start = lines.index("复盘摘要:") if "复盘摘要:" in lines else 0
sum_end = next(k for k in range(sum_start+1, len(lines)) if lines[k].strip() == "---")
summary_raw = [l for l in lines[sum_start+1:sum_end] if l.strip()]
summary_items = []
for l in summary_raw:
    m = re.match(r"^(\d+)\.\s*(.*)$", l.strip())
    if m:
        body = m.group(2)
        if ":" in body:
            t, rest = body.split(":", 1)
            summary_items.append((t.strip(), rest.strip()))
        else:
            summary_items.append(("", body))

# ── 头部信息: blockquote 每行是 "|" 分隔多条 "键:值" ──
BQ = {}
for _l in bq_lines:
    for part in _l.split("|"):
        part = part.strip()
        if ":" in part:
            k, v = part.split(":", 1)
            BQ[k.strip()] = v.strip()
        else:
            m = re.match(r"^(\S+)\s+(.*)$", part)
            if m:
                BQ[m.group(1)] = m.group(2).strip()

season = BQ.get("行情", "")           # 春→夏
score = BQ.get("综合评分", "")         # 70分
panic = BQ.get("恐慌度", "")           # 10/100
pos = BQ.get("仓位上限", "")           # 半仓
zt, dt = BQ.get("涨停", ""), BQ.get("跌停", "")
ratio = BQ.get("涨跌比", "")
amount = BQ.get("两市成交", "")        # 25509亿
vol_txt = BQ.get("放量", "")           # +18.5%
mainline = BQ.get("主线", "")
hi = re.search(r"最高(\d+)板", " ".join(bq_lines))
first_ratio = re.search(r"首板占比(\d+%)", " ".join(bq_lines))

# 摘要 → 01-05
KPI = [
    ("70/100", "综合评分", "春→夏过渡", INK),
    ("10", "恐慌度", "10/100 · 无风险", DOWN),
    (pos, "仓位上限", "执行 3-5 成", GOLD),
    (zt, "涨停", "昨 96 → −34", UP),
    (dt, "跌停", "昨 0 → +4", DOWN),
    ("2.55", "两市成交", "万亿 · 放量+18.5%", INK),
    ("21%", "涨跌比", "1143 : 4317", UP),
    ("5板", "最高板", "首板占比63%", GOLD),
]

# ═══════════════ 渲染 ═══════════════
H = []
H.append('<!DOCTYPE html><html lang="zh-CN"><head><meta charset="utf-8">')
H.append('<meta name="viewport" content="width=device-width,initial-scale=1">')
H.append(f"<title>钱小兜市场复盘 · 2026.08.13</title></head><body style='margin:0;padding:0;background:#fff'>")
H.append(box(p="24px 12px", align="center"))
H.append(box(w="600", align="center", border=0))

# ── Masthead ──
H.append(f"<table cellpadding='0' cellspacing='0' border='0' width='100%' style='border-top:4px double {INK};border-bottom:1px solid {INK};padding:22px 0 14px;margin:0 0 26px'>")
H.append(f"<tr><td align='center' style='font-family:{F_SANS};font-size:11px;letter-spacing:.4em;color:{SUB};padding-bottom:12px'>钱 小 兜 · 每 日 市 场 复 盘</td></tr>")
H.append(f"<tr><td align='center' style='font-family:{F_SONG};font-size:48px;font-weight:700;color:{INK};letter-spacing:.14em;text-indent:.14em;line-height:1.15'>市场复盘</td></tr>")
H.append(f"<tr><td align='center' style='font-family:{F_SANS};font-size:12.5px;color:{SUB};letter-spacing:.06em;padding-top:10px'>"
         f"修复波段中的放量分歧日 · 医药与机器人的双线叙事 · 情绪中枢从夏级回落</td></tr>")
H.append(f"<tr><td style='padding-top:12px'><table cellpadding='0' cellspacing='0' border='0' width='100%' style='border-top:1px solid {LINE}'>"
         f"<tr><td style='padding-top:10px;font-family:{F_SANS};font-size:11.5px;color:{SUB}'>2026年8月13日 · 星期四</td>"
         f"<td align='center' style='padding-top:10px;font-family:{F_SANS};font-size:11.5px;color:{SUB}'>行情状态 · " + span(season, color=GOLD, fw=800) + "</td>"
         f"<td align='right' style='padding-top:10px;font-family:{F_SANS};font-size:11.5px;color:{SUB}'>数据来源 · WeStock · 通达信MCP · 腾讯行情</td>"
         f"</tr></table></td></tr></table>")

# ── KPI  strip: 4+4 ──
def kpi_card(v, l, s, c):
    return (f"<td align='center' style='width:25%;padding:4px'>"
            f"<table cellpadding='0' cellspacing='0' border='0' width='100%' style='border:1px solid {LINE2};border-radius:10px;background:{BGSOFT}'>"
            f"<tr><td align='center' style='padding:10px 4px 4px;font-family:{F_SANS};font-size:24px;font-weight:800;color:{c};line-height:1.2;font-variant-numeric:tabular-nums'>{esc(v)}</td></tr>"
            f"<tr><td align='center' style='padding:0 2px;font-family:{F_SANS};font-size:10.5px;color:{SUB};letter-spacing:.12em'>{esc(l)}</td></tr>"
            f"<tr><td align='center' style='padding:1px 2px 8px;font-family:{F_SANS};font-size:9.5px;color:{FAINT}'>{esc(s)}</td></tr>"
            f"</table></td>")

H.append(f"<table cellpadding='0' cellspacing='0' border='0' width='100%' style='border:1px solid {LINE2};border-radius:12px;margin:0 0 4px'>")
for r in (0, 1):
    cells = "".join(kpi_card(*k) for k in KPI[r*4:(r+1)*4])
    H.append(f"<tr><td style='padding:0'><table cellpadding='0' cellspacing='0' border='0' width='100%'>{tr(cells)}</table></td></tr>")
H.append("</table>")

# ── 摘要 ──
H.append(sec_header("00", "复盘摘要", "EXECUTIVE SUMMARY"))
abstract_rows = []
for idx, (t, rest) in enumerate(summary_items):
    n = f"{idx+1:02d}"
    col1 = td(span(n, color=GOLD, fw=800, fs=17), w="40px", p="2px 8px 8px 0")
    inner = ""
    if t:
        inner += f"<b style='color:{INK};font-size:14.5px'>{esc(t)}</b>"
    inner += f"<span style='font-size:13.5px'> {colorize(rest, fs=13.5)}</span>"
    abstract_rows.append(tr(col1 + td(inner, p="2px 0 8px")))
H.append(f"<table cellpadding='0' cellspacing='0' border='0' width='100%'>{''.join(abstract_rows)}</table>")

# ── 各模块 ──
SEC_EN = {"0": "MACRO NEWS & DRIVERS", "1": "MARKET STATE", "2": "LIMIT-UP STRUCTURE",
          "3": "CAPITAL FLOW", "4": "DUAL TRACK", "5": "VERIFICATION", "6": "TOMORROW PLAN"}

def md_table(text):
    """md 表格块 → rows"""
    rows = []
    for l in text.split("\n"):
        l = l.strip()
        if l.startswith("|"):
            cells = [c.strip() for c in l.strip("|").split("|")]
            if all(re.fullmatch(r":?-{2,}:?", c) for c in cells):
                continue
            rows.append(cells)
    return rows

def is_table(lines):
    return any(l.strip().startswith("|") for l in lines) and any(l.strip().startswith("|") and "---" in l or False for l in lines)

for no, title, ls in sections:
    if no is None:
        continue
    H.append(sec_header(no, title, SEC_EN.get(no, "")))
    buf = []
    for l in ls:
        s = l.strip()
        if not s or s == "---":
            continue
        buf.append(s)
    sub = None  # 当前小节标题 (外围/内因/结论/...)
    tl = None   # 当前 table 缓存
    j = 0
    while j < len(buf):
        s = buf[j]
        if s.startswith("#### "):
            H.append(sub_label(s[5:].strip()))
        elif s.startswith("### "):
            H.append(sub_label(s[4:].strip().upper()))
        elif s.startswith("|"):
            tl = [s]
            j += 1
            while j < len(buf) and buf[j].startswith("|"):
                tl.append(buf[j]); j += 1
            rows = md_table("\n".join(tl))
            ncol = len(rows[0]) if rows else 0
            # 表头分离行已过滤; 按列数/标题找数字列
            hdr0 = rows[0] if rows else []
            num_cols = tuple(k for k, h in enumerate(hdr0)
                             if re.search(r"涨跌幅|涨幅|封单|成交|恐慌|收盘|扣分|净流入|昨|今|变化|当日|数值|价格", h))
            aligns = ["left"] * ncol
            for k, h in enumerate(hdr0):
                if re.search(r"涨跌幅|涨幅|收盘|成交|恐慌|扣分|净流入|封单|数值", h):
                    aligns[k] = "right"
            H.append(render_table(rows, num_cols=num_cols, aligns=aligns))
            continue
        elif s.startswith("> "):
            # 页脚免责声明或引用
            H.append(para(s[2:], fs=11.5, color=SUB, mb=16, p="18px 0 0", ))
        elif s.startswith("- ["):
            pass
        elif re.match(r"^\d+\.\s", s) or s.startswith("- "):
            items = []
            while j < len(buf) and (re.match(r"^\d+\.\s", buf[j]) or buf[j].startswith("- ")):
                l2 = buf[j].strip()
                if l2.startswith("- "):
                    body = l2[2:]
                    items.append((None, body))
                else:
                    m = re.match(r"^\d+\.\s(.*)$", l2)
                    body = m.group(1)
                    if ":" in body and len(body.split(":", 1)[0]) < 18:
                        t, rest = body.split(":", 1)
                        items.append((t.strip() + ":", rest.strip()))
                    else:
                        items.append((None, body))
                j += 1
            H.append(list_items(items))
            continue
        else:
            # 普通段落: 传导/归因/解读/对比
            if s.startswith("归因判断:"):
                H.append(para(s.split(":", 1)[1], fs=13, p="10px 14px", bg=BGSOFT,
                              bd_left=INK, radius="0 8px 8px 0", bold_prefix="归因判断 · "))
            elif " → 传导:" in s:
                b, t = s.split(" → 传导:", 1)
                H.append(para(b, fs=13.5, mb=6))
                H.append(trans_block(t))
            elif s.startswith("**对比**"):
                H.append(para(s.replace("**", ""), fs=13, p="8px 12px", bg=BGSOFT, mb=12,
                              bold_prefix="对比 · "))
            elif s.startswith("趋势特征:") or s.startswith("结构解读:") or s.startswith("主线解读:") \
                 or s.startswith("封单结构:") or s.startswith("风险轨判断:") or s.startswith("综合双轨:") \
                 or s.startswith("方向准确率:") or s.startswith("双轨结论") or s.startswith("综合双轨"):
                key = s.split(":", 1)[0] + " · "
                H.append(para(s.split(":", 1)[1], fs=13.5, mb=12, bold_prefix=key))
            else:
                H.append(para(s, fs=13.5, mb=12))
        j += 1

# ── 页脚 ──
H.append(f"<table cellpadding='0' cellspacing='0' border='0' width='100%' style='margin-top:30px;border-top:2px solid {INK}'>")
H.append(f"<tr><td align='center' style='padding:14px 0 4px;font-family:{F_SANS};font-size:11px;color:{SUB}'>"
         f"钱小兜分析引擎 V10 · 数据来源:WeStock + 通达信MCP + 腾讯行情HTTP · 2026.08.13</td></tr>")
H.append(f"<tr><td align='center' style='padding:2px 0 18px;font-family:{F_SANS};font-size:10.5px;color:{FAINT}'>"
         f"免责声明:以上分析基于公开数据,不构成投资建议。市场有风险,投资需谨慎。</td></tr></table>")
H.append("</table></table></body></html>")

open(OUT, "w", encoding="utf-8").write("".join(H))
print("OK ->", OUT, len("".join(H)), "bytes")