#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""增量提炼：把 runs/<date>_delta.json 里的条目提炼后追加进 enriched_first.jsonl。

沿用补跑脚本的安全做法：临时文件名按 URL 哈希唯一；PDF 只取首页；max_tokens 6000/8000。
"""
import json
import os
import re
import sys
import time
from concurrent.futures import ThreadPoolExecutor

import requests

W = '/Users/liuxinyuan/Desktop/Hermes输出-工作类'
RUNS = os.path.join(W, '数据/market_intel/runs')
DAY = '20261013'
CFG = os.path.expanduser('~/.hermes/profiles/zone-b/config.yaml')
KEY = re.search(r'base_url:\s*https://api\.deepseek\.com[\s\S]{0,200}?api_key:\s*(\S+)',
                open(CFG, encoding='utf-8').read()).group(1)
UA = ('Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 '
      '(KHTML, like Gecko) Chrome/131.0.0.0 Safari/537.36')
S = requests.Session()
S.headers.update({'User-Agent': UA})
PROMPT = """你是产业情报分析员。只依据正文提炼要点，输出 JSON（无多余文字）：
{"时间":"","地点":"","人物":"","主体":"","事件":"","关键事项":"","与我方关联":""}
规则：正文没有的写「未提及」；「事件」≤40字；「关键事项」用「；」分隔 2-4 条（每条≤25字）；
「与我方关联」站在分子诊断原料供应商（酶/预混料/冻干体系/引物探针）角度写一句判断，无则「无直接关联」。

标题：{t}
正文：{b}
"""


def fetch(u):
    try:
        if u.lower().endswith('.pdf'):
            r = S.get(u, timeout=45)
            p = os.path.join(os.environ.get('TMPDIR', '/tmp'), '_delta_%08d.pdf' % (abs(hash(u)) % 10 ** 8))
            open(p, 'wb').write(r.content)
            import fitz
            d = fitz.open(p)
            return re.sub(r'\s+', ' ', d[0].get_text())[:1600]
        r = S.get(u, timeout=25)
        r.encoding = r.apparent_encoding or r.encoding
        t = re.sub(r'(?s)<[^>]+>', ' ', re.sub(r'(?is)<(script|style|nav|footer|head)[^>]*>.*?</\1>', ' ', r.text))
        return re.sub(r'\s+', ' ', t)[:1600]
    except Exception:
        return ''


def run(i):
    b = fetch(i['链接'])
    prompt = PROMPT.replace('{t}', i['标题']).replace('{b}', b or (i.get('摘要') or '')[:600])
    for mt in (6000, 8000):
        try:
            r = requests.post('https://api.deepseek.com/chat/completions', timeout=150,
                              headers={'Authorization': 'Bearer ' + KEY, 'Content-Type': 'application/json'},
                              json={'model': 'deepseek-flash', 'temperature': 0, 'max_tokens': mt,
                                    'messages': [{'role': 'user', 'content': prompt}]})
            ch = (r.json().get('choices') or [{}])[0]
            c = (ch.get('message', {}).get('content') or '').strip()
            if not c:
                continue
            m = re.search(r'\{.*\}', re.sub(r'^```(?:json)?|```$', '', c, flags=re.M), re.S)
            if m:
                return json.loads(m.group(0))
        except Exception:
            time.sleep(2)
    return None


delta = json.load(open(os.path.join(RUNS, DAY + '_delta.json'), encoding='utf-8'))['comp']
print('增量条目: %d → 提炼中' % len(delta))
t0 = time.time()
with ThreadPoolExecutor(max_workers=4) as ex:
    outs = list(ex.map(run, delta))
recs = []
for it, o in zip(delta, outs):
    if o:
        it2 = dict(it)
        it2['要点'] = o
        recs.append(it2)
print('成功 %d / %d ｜ 用时 %.0f 秒' % (len(recs), len(delta), time.time() - t0))

F = os.path.join(RUNS, DAY + '_enriched_first.jsonl')
old = json.load(open(F, encoding='utf-8'))
have = {i['链接'] for i in old}
merged = old + [r for r in recs if r['链接'] not in have]
json.dump(merged, open(F, 'w', encoding='utf-8'), ensure_ascii=False, indent=1)
print('已合并 → enriched_first 共 %d 条（新增 %d）' % (len(merged), len(merged) - len(old)))
