"""AI 财务分析服务 — 读取个股财务数据 → 构建专业提示词 → 流式调用 LLM。

职责: 拉取单只标的的 4 张财务表 → 转成紧凑 JSON → 拼装 CFA 分析师级系统提示词
       → 流式调用 OpenAI 兼容 API → 逐 chunk 吐给前端。

不知道: HTTP、前端、配置持久化。
"""
from __future__ import annotations

import json
import logging
from pathlib import Path
from typing import AsyncIterator

import polars as pl

from app.services.financial_sync import get_financial_df

logger = logging.getLogger(__name__)

# 最多注入的报告期数(最新 N 期),避免上下文爆炸 / token 浪费
_MAX_PERIODS = 4


def _load_stock_financials(data_dir: Path, symbol: str) -> dict[str, list[dict]]:
    """读取该标的的 4 张财务表,返回 {table: [records...]}(按 period_end 降序,截取最新 N 期)。

    数值统一做 NaN/Inf → null 清洗,保证 JSON 序列化不报错。
    """
    result: dict[str, list[dict]] = {}
    for table in ("metrics", "income", "balance_sheet", "cash_flow"):
        df = get_financial_df(data_dir, table)
        if df.is_empty():
            result[table] = []
            continue
        df = df.filter(pl.col("symbol") == symbol)
        if df.is_empty():
            result[table] = []
            continue
        # 按 period_end 降序,截取最新 N 期
        if "period_end" in df.columns:
            df = df.sort("period_end", descending=True).head(_MAX_PERIODS)
        # 清洗 NaN/Inf,转成 JSON 安全的 dict 列表
        rows = []
        for rec in df.to_dicts():
            clean = {}
            for k, v in rec.items():
                if k == "symbol":
                    continue  # 不需要重复回传 symbol
                if isinstance(v, float):
                    import math
                    clean[k] = None if not math.isfinite(v) else v
                else:
                    clean[k] = v
            rows.append(clean)
        result[table] = rows
    return result


def _summarize(fins: dict[str, list[dict]]) -> str:
    """生成一行业务摘要,便于 LLM 快速把握数据全貌(行数/期数)。"""
    parts = []
    for table in ("metrics", "income", "balance_sheet", "cash_flow"):
        rows = fins.get(table, [])
        if rows:
            periods = [r.get("period_end") for r in rows if r.get("period_end")]
            parts.append(f"{table}: {len(rows)}期 ({', '.join(str(p) for p in periods[:3])})")
        else:
            parts.append(f"{table}: 无数据")
    return " · ".join(parts)


# ================================================================
# 系统提示词 —— CFA 分析师级,九维分析框架
# ================================================================

_SYSTEM_PROMPT = """你是一位拥有 15 年 A 股投研经验的资深财务分析师(CFA + CPA),服务于专业机构投资者。你的任务是:基于提供的上市公司财务数据,产出一份**严谨、专业、可直接用于投资决策**的财务分析报告。

## 输出规范

用 **Markdown** 格式输出,严格遵循以下结构。不要输出任何 JSON 或代码块,直接输出 Markdown 正文。

### 1. 📌 核心摘要(1-2 句)
用一句话概括该公司的财务画像:盈利质量、成长动能、财务健康度的最关键判断。结尾用【综合评级:★★★☆☆】给出 1-5 星评级。

### 2. ✅ 亮点(2-3 条)
列出最值得关注的**积极信号**,每条用加粗短语领起,配数据支撑。例如盈利高增、ROE 持续提升、现金流充沛等。

### 3. ⚠️ 风险提示(2-3 条)
客观指出**潜在风险或值得警惕的信号**,例如应收激增、存货堆积、经营现金流与净利润背离、债务攀升等。宁可保守,不要回避。

### 4. 📊 分项诊断
用**表格**呈现各维度的诊断结论,列为「维度 / 关键指标 / 判断」。维度包括:
- **盈利能力**:ROE / ROA / 毛利率 / 净利率
- **成长性**:营收同比 / 净利润同比
- **偿债能力**:资产负债率 / 流动比率(用资产/负债估算)
- **现金流**:经营现金流净额 / 与净利润的匹配度
- **营运效率**:存货周转率等(有数据时)

每个判断给「优秀 / 良好 / 一般 / 偏弱 / 警惕」之一,并一句话说明依据。

### 5. 🎯 综合评估与展望
2-3 段总结:该公司当前的财务状态(优秀/稳健/承压/恶化)、核心驱动力、未来需重点跟踪的指标。**结尾给出"投资参考"**:从纯财务质量角度,该股属于(高质量蓝筹 / 稳健成长 / 周期波动 / 财务承压 / 高风险)中的哪一类。

## 分析准则(务必遵守)

1. **数据说话**:每个判断必须引用具体数值(如"营收同比 +28.5%"),严禁空泛套话
2. **纵向对比**:利用多期数据看趋势(改善/恶化),而非只看单期
3. **交叉验证**:经营现金流 vs 净利润(是否造血)、毛利率 vs 费用率(盈利结构)、负债 vs 资产(杠杆)
4. **行业常识**:对照 A 股常识判断水平(如 ROE>15% 优秀,资产负债率>70% 偏高,毛利率<20% 偏低)
5. **诚实中立**:数据不支持时直言"数据不足,无法判断",绝不编造或过度演绎
6. **简明有力**:避免冗长,用专业投资者能扫读的密度输出,总字数 800-1500 字

## 重要免责
报告末尾附一行:"> ⚠️ 本报告由 AI 基于公开财务数据生成,仅供参考,不构成任何投资建议。"

现在请基于下方数据进行分析。"""


def _build_user_prompt(fins: dict[str, list[dict]], symbol: str, focus: str) -> str:
    """构建用户消息:标的代码 + 数据 JSON + 可选关注点。"""
    data_json = json.dumps(fins, ensure_ascii=False, indent=2)
    lines = [
        f"标的标准代码: {symbol}",
        f"数据概览: {_summarize(fins)}",
        "",
        "以下是该标的最新财务数据(JSON 格式,金额单位为元,比率类指标为百分点):",
        "```json",
        data_json,
        "```",
    ]
    if focus.strip():
        lines.extend([
            "",
            f"本次分析请特别关注: {focus.strip()}",
        ])
    return "\n".join(lines)


async def analyze_financials_stream(
    data_dir: Path,
    symbol: str,
    focus: str = "",
) -> AsyncIterator[str]:
    """流式分析:yield 出每个文本 chunk。

    - 启动时先 yield 一条 {"type":"meta",...} 让前端显示数据摘要
    - 之后逐 chunk yield {"type":"delta","content":"..."}
    - 出错时 yield {"type":"error","message":"..."}
    - 结束 yield {"type":"done"}
    """
    # 1. 加载数据
    fins = _load_stock_financials(data_dir, symbol)
    total_rows = sum(len(v) for v in fins.values())
    if total_rows == 0:
        yield json.dumps({"type": "error", "message": f"标的 {symbol} 暂无任何财务数据,请先同步财务表"}, ensure_ascii=False)
        return

    # 2. meta
    yield json.dumps({
        "type": "meta",
        "symbol": symbol,
        "summary": _summarize(fins),
        "periods": total_rows,
    }, ensure_ascii=False)

    # 3. 调用 LLM 流式
    try:
        from app.services.ai_provider import stream_ai_text

        user_prompt = _build_user_prompt(fins, symbol, focus)
        async for delta in stream_ai_text(
            [
                {"role": "system", "content": _SYSTEM_PROMPT},
                {"role": "user", "content": user_prompt},
            ],
            temperature=0.4,
            max_tokens=4000,
        ):
            yield json.dumps({"type": "delta", "content": delta}, ensure_ascii=False)

    except Exception as e:  # noqa: BLE001
        logger.exception("AI financial analysis failed for %s: %s", symbol, e)
        yield json.dumps({"type": "error", "message": f"AI 分析失败: {e}"}, ensure_ascii=False)
        return

    yield json.dumps({"type": "done"}, ensure_ascii=False)
