#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""行业信息推送 v0.1 · 摘要生成器：把 items.jsonl 渲染成可读 digest（md）。

用法：intel_digest_v01.py [YYYYMMDD]（默认今天）
输出：数据/market_intel/runs/<date>_v01试跑.md  （另存 latest_digest.md）
"""
import json
import os
import re
import sys
from collections import Counter
from datetime import datetime, timedelta

W = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
RUNS = os.path.join(W, '数据/market_intel/runs')
DAY = sys.argv[1] if len(sys.argv) > 1 else datetime.now().strftime('%Y%m%d')
P = os.path.join(RUNS, DAY + '_items.jsonl')
items = [json.loads(l) for l in open(P, encoding='utf-8') if l.strip()]
status = json.load(open(os.path.join(RUNS, DAY + '_status.json'), encoding='utf-8'))['status']

CUT = (datetime.now() - timedelta(days=35)).strftime('%Y-%m-%d')
comp = [i for i in items if i['来源'].startswith('竞品官网')]
cust = [i for i in items if i['来源'] == '巨潮公告']
bid = [i for i in items if i['来源'].startswith(('全国公共资源交易', '检索源'))]


def clean(t):
    t = re.sub(r'\s+\d{1,4}\s+20\d{2}-\d{2}-\d{2}\s*$', '', t)   # 翌圣列表的浏览量+日期
    t = re.sub(r'\s+20\d{2}-\d{2}-\d{2}\s*$', '', t)            # 尾随日期
    t = re.sub(r'\s+\d{1,4}\s*$', '', t)
    return t.strip(' -｜|')


def dkey(it):
    d = it.get('日期') or ''
    m = re.search(r'20\d{2}-\d{2}-\d{2}', d)
    if m:
        return m.group(0)
    m = re.search(r'(20\d{2})[/-](\d{1,2})[/-](\d{1,2})', d)
    return '%s-%02d-%02d' % (m.group(1), int(m.group(2)), int(m.group(3))) if m else ''


for i in items:
    i['_d'] = dkey(i)
    i['标题'] = clean(i['标题'])
comp.sort(key=lambda x: x['_d'], reverse=True)
cust.sort(key=lambda x: x['_d'], reverse=True)

# 英文 slug 回填（罗氏 sitemap 取到的是 URL slug，非真实标题）
def backfill_slug(it):
    try:
        import requests
        from bs4 import BeautifulSoup
        r = requests.get(it['链接'], timeout=20, headers={'User-Agent': 'Mozilla/5.0 Chrome/131'})
        soup = BeautifulSoup(r.text, 'lxml')
        t = soup.title.get_text(strip=True) if soup.title else ''
        t = re.sub(r'\s*[|｜]\s*Roche.*$', '', t).strip()
        if t and len(t) > 8:
            it['标题'] = t[:150]
        m = re.search(r'(20\d{2}-\d{2}-\d{2})', r.text)
        if m:
            it['日期'] = m.group(1)
    except Exception:
        pass


for i in items:
    if not re.search(r'[\u4e00-\u9fff]', i['标题']) and i['来源'].startswith('竞品官网'):
        backfill_slug(i)

for i in items:
    i['业务相关'] = bool(re.search(r'注册证|批准|中标|合同|产能|专利|临床|GMP|收购|增资|投资|业绩|年报|半年报|季度|新品|上市|合作|签约|扩产|获证|CE|FDA|IVDR', i['标题']))

L = []
L.append('# 行业信息推送 · v0.1 试跑结果（%s）\n' % DAY)
L.append('> 版本：v0.1（试跑版，**只落档不推送**）｜生成：%s ｜条目：**%d** 条' % (
    datetime.now().strftime('%Y-%m-%d %H:%M'), len(items)))
L.append('> 覆盖：上市客户体系公告（巨潮 POST）+ 竞品官网新闻（6 家）+ 招投标（检索源兜底）\n')
L.append('---\n')

L.append('## 一、数据源状态\n')
L.append('| 路 | 源 | 结果 | 状态 |')
L.append('|---|---|---|---|')
for s in status:
    L.append('| %s | %s | %s | %s |' % (s[0].split()[0], s[0].replace(s[0].split()[0] + ' ', ''),
                                        s[1], {'live': '🟢 正常', 'pending': '🟡 待接入',
                                               'fail': '🔴 失败', 'skip': '⚪ 跳过',
                                               'empty': '🟠 空'}.get(s[2], s[2])))
L.append('')
L.append('**条目分布**：' + '；'.join('%s %d' % (k, v) for k, v in Counter(i['来源'] for i in items).most_common()) + '\n')

L.append('---\n')
RECENT = (datetime.now() - timedelta(days=14)).strftime('%Y-%m-%d')


def rank(it):
    pl = 1 if any('产品线' in t for t in it['标签']) else 0
    recent = 1 if it['_d'] >= RECENT else 0
    return (-(pl * 2 + recent), it['_d'])


L.append('## 二、本期要闻速览（竞品｜近 14 天 + 产品线相关优先）\n')
for it in sorted(comp, key=rank)[:10]:
    tags = (' `' + '/'.join(t.split(':')[1] for t in it['标签'][:3]) + '`') if it['标签'] else ''
    L.append('- **%s** %s %s' % (it['主体'], it['_d'] or '日期缺', it['标题'][:90]))
    L.append('  - %s%s' % (it['链接'], tags))
L.append('')

L.append('---\n')
L.append('## 三、竞品动态明细\n')
for brand in dict.fromkeys(i['主体'] for i in comp):
    grp = [i for i in comp if i['主体'] == brand]
    L.append('### %s（%d 条）\n' % (brand, len(grp)))
    for it in grp[:8]:
        tags = (' ｜' + '、'.join(t.split(':')[1] for t in it['标签'][:4])) if it['标签'] else ''
        L.append('- %s ｜ %s%s' % (it['_d'] or '—', it['标题'][:96], tags))
        L.append('  - %s' % it['链接'])
    L.append('')

L.append('---\n')
L.append('## 四、客户体系公告（巨潮，近 35 天内 + 业务相关）\n')
shown = 0
for nm in dict.fromkeys(i['主体'] for i in cust):
    grp = [i for i in cust if i['主体'] == nm]
    hot = [i for i in grp if i['_d'] >= CUT or i['标签']]
    if not hot:
        continue
    rel = [i for i in hot if i.get('业务相关')]
    noise = [i for i in hot if not i.get('业务相关')]
    L.append('### %s（业务相关 %d 条 ／ 常规公告 %d 条）\n' % (nm, len(rel), len(noise)))
    for it in rel[:6]:
        tags = (' ｜' + '、'.join(t.split(':')[1] for t in it['标签'][:3])) if it['标签'] else ''
        L.append('- ★ %s ｜ %s%s' % (it['_d'] or '—', it['标题'][:90], tags))
    if noise:
        L.append('- （常规公告 %d 条：%s）' % (len(noise), '；'.join(
            '%s %s' % (i['_d'], i['标题'][:22]) for i in noise[:3])))
    L.append('')
    shown += len(hot)
L.append('（体系公告命中 %d 条；完整 90 条见 items.jsonl）\n' % shown)

L.append('---\n')
L.append('## 五、招投标 / 采购信号\n')
for it in bid[:14]:
    L.append('- %s ｜ %s' % (it['标题'][:92], it['链接']))
L.append('')

L.append('---\n')
L.append('## 六、本期缺口与下期改进\n')
L.append('| 缺口 | 原因（实测） | 计划 |')
L.append('|---|---|---|')
L.append('| 招投标结构化数据 | ggzy 列表页 JS 渲染；`getTradList` 404；站内搜索 POST 返回 405；另有 captcha 端点 | v0.2 用浏览器会话复刻筛选请求，或改用省级平台 / 比地招标网 |')
L.append('| 迈迪安官网 | Cloudflare 挑战 403 | v0.2 试 sitemap / 展会页面 |')
L.append('| 宝生物新闻 | 官网结构待解析（takara.co.jp 首页取 0 条） | v0.2 定位其 news 列表路径 |')
L.append('| 客户体系公告噪声 | 巨潮返回最新 30 条，含大量非业务公告（限售、回购等） | v0.2 按 `category` 白名单过滤（定期报告 / 重大合同 / 中标） |')
L.append('')

open(os.path.join(RUNS, DAY + '_v01试跑.md'), 'w', encoding='utf-8').write('\n'.join(L))
open(os.path.join(RUNS, 'latest_digest.md'), 'w', encoding='utf-8').write('\n'.join(L))
print('✅ 摘要已生成')
print('   条目: %d（竞品 %d ／ 客户公告 %d ／ 招投标 %d）' % (len(items), len(comp), len(cust), len(bid)))
print('   竞品有日期条目:', sum(1 for i in comp if i['_d']))
print('   输出:', os.path.join(RUNS, DAY + '_v01试跑.md'))
