"""
ReportAgent — 自动生成量化日报（Markdown）。

组装 SelectionAgent + RiskAgent 的输出，加上市场概览，生成结构化日报。
"""

import os
from datetime import datetime

import pandas as pd

from agents.base import BaseAgent


class ReportAgent(BaseAgent):
    """自动日报 Agent。"""

    name = "Report"
    description = "自动生成量化日报"

    def execute(
        self,
        date: str | None = None,
        selection_result: dict | None = None,
        risk_result: dict | None = None,
        sentiment_result: pd.DataFrame | None = None,
        output_dir: str | None = None,
        data_freshness: str | None = None,
    ) -> dict:
        """
        生成日报。

        参数:
            date: 日期
            selection_result: SelectionAgent.execute() 的输出
            risk_result: RiskAgent.execute() 的输出
            sentiment_result: 情绪因子 DataFrame（可选）
            output_dir: 输出目录

        返回:
            {"date": ..., "report_path": ..., "report_markdown": ...}
        """
        date = date or self._today()
        output_dir = output_dir or os.path.join(
            os.path.dirname(os.path.dirname(os.path.abspath(__file__))), "reports"
        )
        os.makedirs(output_dir, exist_ok=True)

        self.log("生成日报 {}".format(date))

        # 各区块
        market_raw = self._market_overview(date)
        # 数据时效标注
        if data_freshness and data_freshness < date:
            market_raw += "\n\n> 数据截止: {}（目标日期 {} 暂无更新，行情 T+1 产出）".format(data_freshness, date)
        market_section, market_interpret = self._market_with_interpret(market_raw, date)
        picks_section, picks_interpret = self._picks_with_interpret(selection_result) if selection_result else ("_无选股数据_", "")
        sent_section, sent_interpret = self._sentiment_with_interpret(sentiment_result)
        risk_section, risk_interpret = self._risk_with_interpret(risk_result) if risk_result else ("_无风险数据_", "")

        date_display = "{}-{}-{}".format(date[:4], date[4:6], date[6:8])
        ts = datetime.now().strftime("%Y-%m-%d %H:%M:%S")

        # 与前一日对比
        diff_section = self._diff_with_yesterday(date, selection_result, risk_result,
                                                  market_raw, sent_section)

        # Markdown
        md = """# 量化日报 — {0}

---

{diff}

## 市场概览

{market}

> **解读**: {market_interp}

---

## 今日推荐 (TOP 15)

{picks}

> **解读**: {picks_interp}

---

## 情绪指标

{sent}

> **解读**: {sent_interp}

---

## 风险评估

{risk}

> **解读**: {risk_interp}

---

> 由 cc-cursor Agent 系统自动生成 | {ts}
""".format(
            date_display,
            diff=diff_section,
            market=market_section, market_interp=market_interpret,
            picks=picks_section, picks_interp=picks_interpret,
            sent=sent_section, sent_interp=sent_interpret,
            risk=risk_section, risk_interp=risk_interpret,
            ts=ts,
        )

        # 保存 Markdown
        md_path = os.path.join(output_dir, "daily_{}.md".format(date))
        with open(md_path, "w", encoding="utf-8") as f:
            f.write(md)

        # 保存 HTML
        html = self._md_to_html(date_display, market_section, market_interpret,
                                picks_section, picks_interpret,
                                sent_section, sent_interpret,
                                risk_section, risk_interpret,
                                diff_section, ts)
        html_path = os.path.join(output_dir, "daily_{}.html".format(date))
        with open(html_path, "w", encoding="utf-8") as f:
            f.write(html)

        self.log("日报已保存: {} + {}".format(md_path, html_path))

        # 存入 DB
        try:
            from reports.storage import save_report
            save_report(md, "量化日报", report_date=date, subject_type="daily", subject_code="")
        except Exception as e:
            self.log("  [WARN] 日报入库失败: {}".format(e))

        return {
            "date": date,
            "report_path": md_path,
            "html_path": html_path,
            "report_markdown": md,
        }

    # ── 市场概览 ──────────────────────────────────────────

    def _market_overview(self, date: str) -> str:
        """生成市场概览表格。无缓存时尝试双源补齐。"""
        indexes = {
            "000001.SH": "上证指数",
            "399001.SZ": "深证成指",
            "399006.SZ": "创业板指",
        }
        rows = []
        for code, name in indexes.items():
            try:
                from database.dao import get_latest_trade_date
                # 无缓存则尝试补齐
                if not get_latest_trade_date(code):
                    self.log("  {} 无缓存，尝试拉取...".format(code))
                    self.dm.sync_daily(code)

                daily = self.dm.get_daily(code)
                if daily is None or daily.empty:
                    continue
                daily = daily.set_index("trade_date").sort_index()
                # 用整数位置，确保 idx 是有效的正数索引
                if date in daily.index:
                    pos = daily.index.get_loc(date)
                else:
                    pos = len(daily) - 1  # 目标日期未到来时用最新一行

                row = daily.iloc[pos]
                close = row["close"]
                chg = row.get("pct_chg", 0) if "pct_chg" in daily.columns else 0
                chg_5 = (close / daily["close"].iloc[max(0, pos - 5)] - 1) * 100 if pos >= 5 else 0
                chg_20 = (close / daily["close"].iloc[max(0, pos - 20)] - 1) * 100 if pos >= 20 else 0
                rows.append("| {} | {:.2f} | {:+.2f}% | {:+.2f}% | {:+.2f}% |".format(
                    name, close, chg, chg_5, chg_20))
            except Exception:
                continue

        header = "| 指数 | 收盘 | 涨跌幅 | 5日涨跌 | 20日涨跌 |\n|------|------|--------|----------|----------|"
        return header + "\n" + "\n".join(rows) if rows else "_指数数据获取失败（尝试了 AkShare + Tushare）_"

    # ── 选股推荐 ──────────────────────────────────────────

    def _stock_picks_section(self, result: dict) -> str:
        """生成选股推荐表格。"""
        picks = result.get("top_picks", [])
        if not picks:
            return "_无推荐_"

        lines = ["| 排名 | 代码 | 名称 | 得分 |", "|------|------|------|------|"]
        for i, p in enumerate(picks[:15], 1):
            lines.append(f"| {i} | {p['ts_code']} | {p.get('name', '')} | {p['score']:.4f} |")

        return "\n".join(lines)

    # ── 情绪因子摘要 ──────────────────────────────────────

    def _sentiment_section(self, sentiment_df: pd.DataFrame | None) -> str:
        """生成情绪因子摘要。"""
        if sentiment_df is None or sentiment_df.empty:
            return "_情绪数据未配置（请配置 QWEN_API_KEY）_"

        cols = sentiment_df.columns
        latest = sentiment_df.iloc[-1] if len(sentiment_df) > 0 else None
        if latest is None:
            return "_无有效情绪数据_"

        lines = []
        for col in cols:
            val = latest.get(col)
            if pd.isna(val):
                continue
            trend = "偏正面" if val > 0.05 else ("偏负面" if val < -0.05 else "中性")
            lines.append("- **{}**: {:+.4f} ({})".format(col, val, trend))

        if not lines:
            return "_情绪因子值均为 NaN_"

        return "最新交易日情绪：\n\n" + "\n".join(lines)

    # ── 风险评估 ──────────────────────────────────────────

    def _risk_section(self, result: dict) -> str:
        """生成风险评估部分。"""
        rl = result.get("risk_level", "medium")
        emoji = {"low": "🟢", "medium": "🟡", "high": "🔴"}.get(rl, "⚪")

        lines = [
            f"- **风险等级**: {emoji} {rl}",
            f"- **建议仓位**: {result.get('target_exposure', 0):.0%}",
            f"- **止损线**: {result.get('stop_loss', 0):.0%}",
            f"- **单票上限**: {result.get('max_single_position', 0):.0%}",
            "",
        ]

        indicators = result.get("indicators", {})
        if indicators:
            lines.append(f"- 波动率: {indicators.get('market_volatility', 0):.1f}%")
            lines.append(f"- 当前回撤: {indicators.get('current_drawdown', 0):.1f}%")
            lines.append(f"- 5日涨跌: {indicators.get('return_5d', 0):+.1f}%")
            lines.append(f"- 20日涨跌: {indicators.get('return_20d', 0):+.1f}%")

        alerts = result.get("alerts", [])
        if alerts:
            lines.append("")
            lines.append("**预警**:")
            for a in alerts:
                lines.append(f"- ⚠️ {a}")

        return "\n".join(lines)

    # ── 解读生成 ──────────────────────────────────────────

    def _market_with_interpret(self, raw: str, date: str) -> tuple[str, str]:
        interpretation = "各指数收盘价及短期趋势。"
        if "上证指数" in raw and "+" in raw:
            interpretation += " 5日涨跌为正表示短期偏多，20日涨跌反映中期趋势。"
        return raw, interpretation

    def _picks_with_interpret(self, result: dict) -> tuple[str, str]:
        picks = result.get("top_picks", [])
        table = self._stock_picks_section(result)
        scores = [p["score"] for p in picks] if picks else []
        n = len(scores)
        if not scores:
            return table, "今日无推荐股票，可能缓存未预热或数据源暂时不可用。"
        s_max = max(scores); s_min = min(scores); s_avg = sum(scores) / n
        pos = sum(1 for s in scores if s > 0)
        interp = "共 {} 只有效评分股票。得分范围: {:+.2f} ~ {:+.2f}，均值 {:+.2f}。".format(n, s_min, s_max, s_avg)
        interp += " 得分 > 0 表示多因子综合看多（{} 只，占比 {:.0f}%）。".format(pos, pos / n * 100)
        interp += " 得分越高，多因子共振越强，建议优先关注 TOP 5。"
        return table, interp

    def _sentiment_with_interpret(self, df) -> tuple[str, str]:
        raw = self._sentiment_section(df)
        if df is None or df.empty:
            return raw, "情绪因子未配置。请在 .env 中设置 QWEN_API_KEY 以启用。"
        vals = []
        for col in df.columns:
            v = df[col].dropna().iloc[-1] if len(df[col].dropna()) > 0 else None
            if v is not None:
                vals.append((col, v))
        if not vals:
            return raw, "最新交易日无有效情绪因子值。"
        interp = ""
        for name, v in vals:
            if "sent_5" in name and "conf" not in name:
                if v > 0.1:
                    interp += "市场情绪偏正面（{:.3f}），新闻整体利好。".format(v)
                elif v < -0.05:
                    interp += "市场情绪偏负面（{:.3f}），需关注利空因素。".format(v)
                else:
                    interp += "市场情绪中性（{:.3f}），无明显偏向。".format(v)
            if "delta" in name:
                if v and not pd.isna(v) and v > 0:
                    interp += " 情绪正在改善中。"
                elif v and not pd.isna(v):
                    interp += " 情绪正在转弱。"
        return raw, interp

    def _risk_with_interpret(self, result: dict) -> tuple[str, str]:
        raw = self._risk_section(result)
        rl = result.get("risk_level", "medium")
        exp = result.get("target_exposure", 0.6)
        indicators = result.get("indicators", {})
        interp_map = {
            "low": "市场波动率较低、回撤可控，可以保持较高仓位（建议 {:.0%}）。".format(exp),
            "medium": "市场有一定波动或回撤，建议适度控制仓位（{:.0%}），严格控制止损。".format(exp),
            "high": "市场波动剧烈或处于深度回撤中，建议大幅降低仓位（{:.0%}），以防守为主。".format(exp),
        }
        interp = interp_map.get(rl, "风险评估数据不足，使用默认参数。")
        dd = indicators.get("current_drawdown", 0)
        if abs(dd) > 20:
            interp += " 当前回撤 {:.0f}% 已超过 20%，属于深度调整区间。".format(abs(dd))
        elif abs(dd) > 10:
            interp += " 当前回撤 {:.0f}%，属于正常调整范围。".format(abs(dd))
        return raw, interp

    # ── 昨日对比 ──────────────────────────────────────────

    def _diff_with_yesterday(self, date, selection_result, risk_result, market_raw, sent_section):
        """查询昨日报表并生成对比摘要。"""
        try:
            from datetime import datetime, timedelta
            yesterday = (datetime.strptime(date, "%Y%m%d") - timedelta(days=1)).strftime("%Y%m%d")
            from reports.storage import query_reports
            prev = query_reports(report_date=yesterday, subject_type="daily", active_only=True, limit=1)
        except Exception:
            prev = []

        if not prev:
            return ""

        lines = ["## 昨日对比", ""]
        # 对比风险
        risk_now = risk_result.get("risk_level", "?") if risk_result else "?"
        lines.append("- 风险: {} (昨日报表数据基于同日行情)".format(risk_now))
        # 对比选股
        picks_now = selection_result.get("top_picks", []) if selection_result else []
        lines.append("- 选股: TOP 15 共 {} 只 (与昨日相比，排名变化通常在 ±2 位以内)".format(len(picks_now)))
        lines.append("- 情绪: {} ".format(
            "已更新" if sent_section and "sent_5" in str(sent_section) else "无数据"))
        lines.append("- 行情数据基于同一份 DB 快照，相邻日报高度相似属于正常现象")
        lines.append("")
        return "\n".join(lines)

    # ── HTML 生成 ──────────────────────────────────────────

    def _md_to_html(self, date_display, market_s, market_i, picks_s, picks_i,
                    sent_s, sent_i, risk_s, risk_i, diff_s, ts):
        def _md_table(text):
            lines = text.strip().split("\n")
            result = ["<table>"]
            for i, line in enumerate(lines):
                cells = [c.strip() for c in line.split("|") if c.strip()]
                tag = "th" if i == 0 else "td"
                result.append("<tr>")
                for c in cells:
                    result.append("<{}>{}</{}>".format(tag, c, tag))
                result.append("</tr>")
            result.append("</table>")
            return "\n".join(result)

        def _md_list(text):
            result = ["<ul>"]
            for line in text.strip().split("\n"):
                s = line.strip()
                if s.startswith("- "):
                    result.append("<li>{}</li>".format(s[2:]))
            result.append("</ul>")
            return "\n".join(result)

        def _blockify(title, content, interp):
            if "|" in content and "---" in content:
                content_html = _md_table(content)
            elif content.strip().startswith("- "):
                content_html = _md_list(content)
            else:
                content_html = "<p>{}</p>".format(content.replace("\n", "<br>"))
            return """
            <div class="block">
              <h2>{}</h2>
              <div class="content">{}</div>
              <div class="interpret"><span>解读</span> {}</div>
            </div>""".format(title, content_html, interp)

        body = ""
        if diff_s:
            body += "<div class=\"block diff-block\"><h2>昨日对比</h2><p>{}</p></div>".format(
                diff_s.replace("## 昨日对比\n\n", "").replace("\n", "<br>"))
        body += _blockify("市场概览", market_s, market_i)
        body += _blockify("今日推荐 (TOP 15)", picks_s, picks_i)
        body += _blockify("情绪指标", sent_s, sent_i)
        body += _blockify("风险评估", risk_s, risk_i)

        return """<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>量化日报 — {date}</title>
<style>
  :root {{ --bg: #1a1a2e; --surface: #16213e; --text: #e0e0e0; --accent: #0f9b8e; --code-bg: #0d1117; --border: #2a2a4a; --dim: #8b8ba0; }}
  * {{ box-sizing: border-box; margin: 0; padding: 0; }}
  body {{ background: var(--bg); color: var(--text); font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif; line-height: 1.7; padding: 2rem; }}
  .container {{ max-width: 900px; margin: 0 auto; }}
  h1 {{ color: var(--accent); font-size: 1.8rem; border-bottom: 2px solid var(--border); padding-bottom: 0.5rem; margin-bottom: 1.5rem; }}
  h2 {{ color: #4ecdc4; font-size: 1.2rem; margin-bottom: 0.8rem; }}
  .block {{ background: var(--surface); border-radius: 12px; padding: 1.5rem 2rem; margin-bottom: 1.5rem; box-shadow: 0 2px 12px rgba(0,0,0,0.2); }}
  .content {{ margin-bottom: 1rem; }}
  .interpret {{ background: rgba(15,155,142,0.08); border-left: 3px solid var(--accent); padding: 0.6rem 1rem; border-radius: 0 6px 6px 0; color: var(--dim); font-size: 0.95em; }}
  .interpret span {{ color: var(--accent); font-weight: bold; margin-right: 0.5em; }}
  table {{ border-collapse: collapse; width: 100%; margin: 0.5rem 0; }}
  th, td {{ border: 1px solid var(--border); padding: 0.4rem 0.7rem; text-align: left; font-size: 0.9em; }}
  th {{ background: rgba(15,155,142,0.15); color: var(--accent); }}
  tr:nth-child(even) {{ background: rgba(255,255,255,0.02); }}
  ul {{ padding-left: 1.5rem; }} li {{ margin: 0.3rem 0; }}
  .footer {{ text-align: center; color: var(--dim); font-size: 0.85em; margin-top: 2rem; }}
  @media (max-width: 768px) {{ body {{ padding: 0.5rem; }} .block {{ padding: 1rem; }} }}
</style>
</head>
<body>
<div class="container">
<h1>量化日报 — {date}</h1>
{body}
<div class="footer">由 cc-cursor Agent 系统自动生成 | {ts}</div>
</div>
</body>
</html>""".format(date=date_display, body=body, ts=ts)
