#!/usr/bin/env python3
"""
report_generator.py — 可视化报表生成引擎

职责:
  1. 生成 HTML 仪表盘 (交易绩效、实时状态)
  2. 生成 Markdown 日报/周报
  3. 生成 JSON 格式的 API 消费数据
  4. 生成 CSV 导出供下游系统使用

报表类型:
  - dashboard.html: 实时交易仪表盘 (滚动窗口)
  - daily_report.md: 每日交易报告
  - performance.json: 绩效指标 API 数据
  - export.csv: 数据导出
"""
from __future__ import annotations

import csv
import io
import json
import os
import time
from collections import defaultdict
from dataclasses import asdict, dataclass
from datetime import datetime, timedelta, timezone
from pathlib import Path
from typing import Any, Optional

from src.data_consume.analytics_engine import (
    AnalyticsEngine,
    BatchReport,
    RollingStats,
    get_engine,
)
from src.data_consume.data_cleaner import CLEANED_DATA_DIR

# ── 配置 ─────────────────────────────────────────────────────────

APP_DIR = Path(__file__).resolve().parent.parent.parent
REPORTS_DIR = APP_DIR / "reports"
DASHBOARD_DIR = APP_DIR / "data" / "consumed" / "dashboard"
EXPORT_DIR = APP_DIR / "data" / "consumed" / "export"

DASHBOARD_TEMPLATE = """
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>LobsterQuant — 交易监控</title>
<style>
  *{box-sizing:border-box;margin:0;padding:0}
  body{font-family:'SF Mono',Monaco,Consolas,monospace;background:#0d1117;color:#c9d1d9;min-height:100vh;padding:20px}
  .header{display:flex;align-items:center;justify-content:space-between;margin-bottom:24px;border-bottom:1px solid #30363d;padding-bottom:16px}
  .header h1{color:#58a6ff;font-size:20px;letter-spacing:1px}
  .header .ts{color:#8b949e;font-size:12px}
  .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(200px,1fr));gap:16px;margin-bottom:24px}
  .card{background:#161b22;border:1px solid #30363d;border-radius:8px;padding:16px}
  .card .label{font-size:11px;color:#8b949e;text-transform:uppercase;letter-spacing:0.5px;margin-bottom:6px}
  .card .value{font-size:28px;font-weight:700;color:#e6edf3}
  .card .value.positive{color:#3fb950}
  .card .value.negative{color:#f85149}
  .card .value.neutral{color:#58a6ff}
  .card .sub{font-size:11px;color:#6e7681;margin-top:4px}
  .section{margin-bottom:24px}
  .section h2{font-size:14px;color:#e6edf3;margin-bottom:12px;padding-bottom:8px;border-bottom:1px solid #21262d}
  .metrics{display:grid;grid-template-columns:repeat(auto-fit,minmax(250px,1fr));gap:12px}
  .metric-item{background:#161b22;border:1px solid #30363d;border-radius:6px;padding:12px;display:flex;justify-content:space-between;align-items:center}
  .metric-item .key{color:#8b949e;font-size:13px}
  .metric-item .val{color:#e6edf3;font-size:14px;font-weight:600}
  .chart-placeholder{background:#161b22;border:1px solid #30363d;border-radius:8px;padding:20px;text-align:center;color:#484f58;font-size:13px;height:200px;display:flex;align-items:center;justify-content:center}
  .grid2{display:grid;grid-template-columns:1fr 1fr;gap:16px}
  .insight-item{padding:10px 12px;background:#161b22;border-radius:6px;border-left:3px solid #30363d;margin-bottom:8px}
  .insight-item.warn{border-left-color:#d29922}
  .insight-item.alert{border-left-color:#f85149}
  .insight-item .title{font-size:13px;color:#e6edf3;margin-bottom:4px}
  .insight-item .desc{font-size:12px;color:#8b949e}
  .footer{margin-top:40px;padding-top:16px;border-top:1px solid #30363d;color:#484f58;font-size:11px;text-align:center}
</style>
</head>
<body>
<div class="header">
  <h1>LOBSTERQUANT 交易监控</h1>
  <div class="ts">更新时间: {updated_at}</div>
</div>

<div class="grid">
  <div class="card">
    <div class="label">窗口交易数</div>
    <div class="value neutral">{event_count}</div>
    <div class="sub">最近 {window_min} 分钟</div>
  </div>
  <div class="card">
    <div class="label">窗口盈亏</div>
    <div class="value {pnl_class}">{total_pnl_str}</div>
    <div class="sub">W: {wins} / L: {losses}</div>
  </div>
  <div class="card">
    <div class="label">平均置信度</div>
    <div class="value neutral">{avg_confidence:.0%}</div>
    <div class="sub">阈值参考: 68%</div>
  </div>
  <div class="card">
    <div class="label">执行延迟</div>
    <div class="value {latency_class}">{avg_latency_ms:.0f}ms</div>
    <div class="sub">P95: {p95_latency_ms:.0f}ms</div>
  </div>
</div>

<div class="section">
  <h2>绩效指标</h2>
  <div class="metrics">
    <div class="metric-item"><span class="key">胜率</span><span class="val">{win_rate:.1%}</span></div>
    <div class="metric-item"><span class="key">平均盈亏</span><span class="val">{avg_pnl:.2f}</span></div>
    <div class="metric-item"><span class="key">最大单笔盈利</span><span class="val positive">{max_pnl:.2f}</span></div>
    <div class="metric-item"><span class="key">最大单笔亏损</span><span class="val negative">{min_pnl:.2f}</span></div>
    <div class="metric-item"><span class="key">盈亏比</span><span class="val">{profit_factor:.2f}</span></div>
    <div class="metric-item"><span class="key">夏普比</span><span class="val">{sharpe_ratio:.2f}</span></div>
  </div>
</div>

<div class="grid2">
  <div class="section">
    <h2>市场状态分布</h2>
    <div class="metrics">
      <div class="metric-item"><span class="key">趋势</span><span class="val">{trend_count}</span></div>
      <div class="metric-item"><span class="key">震荡</span><span class="val">{range_count}</span></div>
      <div class="metric-item"><spanspan class="key">波动</span><span class="val">{volatile_count}</span></div>
    </div>
  </div>
  <div class="section">
    <h2>交易方向分布</h2>
    <div class="metrics">
      <div class="metric-item"><span class="key">做多 (BUY)</span><span class="val">{buy_count}</span></div>
      <div class="metric-item"><span class="key">做空 (SELL)</span><span class="val">{sell_count}</span></div>
    </div>
  </div>
</div>

<div class="section">
  <h2>实时洞察</h2>
  {insights_html}
</div>

<div class="footer">
  LobsterQuant 交易监控系统 · 自动生成 · 禁止手动修改
</div>
</body>
</html>
"""


# ── 数据类 ───────────────────────────────────────────────────────

@dataclass
class DashboardData:
    """仪表盘数据"""
    updated_at: str
    window_stats: RollingStats
    daily_report: Optional[BatchReport] = None
    insights: list[dict] = None


# ── HTML 仪表盘生成 ──────────────────────────────────────────────

class DashboardGenerator:
    """HTML 仪表盘生成器"""

    def __init__(self):
        self.template = DASHBOARD_TEMPLATE

    def generate(self, data: DashboardData) -> str:
        ws = data.window_stats
        daily = data.daily_report
        insights = data.insights or []

        pnl_class = "positive" if ws.total_pnl >= 0 else "negative"
        latency_class = "positive" if ws.avg_latency_ms < 500 else "negative" if ws.avg_latency_ms > 1000 else "neutral"

        win_rate = ws.wins / ws.event_count if ws.event_count > 0 else 0
        profit_factor = 0.0
        sharpe_ratio = 0.0
        avg_pnl = 0.0
        if daily:
            if daily.avg_loss > 0:
                profit_factor = daily.profit_factor
            sharpe_ratio = daily.sharpe_ratio
            avg_pnl = daily.avg_pnl

        trend_count = ws.regime_counts.get("TRENDING", 0)
        range_count = ws.regime_counts.get("RANGING", 0)
        volatile_count = ws.regime_counts.get("VOLATILE", 0)
        buy_count = ws.side_counts.get("BUY", 0)
        sell_count = ws.side_counts.get("SELL", 0)

        insights_html = ""
        if insights:
            for ins in insights[:5]:
                sev = ins.get("severity", "INFO").lower()
                cls = "warn" if sev == "warn" else "alert" if sev == "alert" else ""
                insights_html += f"""
                <div class="insight-item {cls}">
                  <div class="title">[{ins.get('severity','')}] {ins.get('title','')}</div>
                  <div class="desc">{ins.get('description','')}</div>
                </div>"""
        else:
            insights_html = '<div class="insight-item"><div class="desc">暂无预警</div></div>'

        # 计算 P95 延迟 (简化估计)
        p95_latency_ms = ws.avg_latency_ms * 1.5

        html = self.template.format(
            updated_at=data.updated_at,
            event_count=ws.event_count,
            window_min=ws.window_min,
            total_pnl_str=f"${ws.total_pnl:+.2f}" if ws.total_pnl != 0 else "$0.00",
            pnl_class=pnl_class,
            wins=ws.wins,
            losses=ws.losses,
            avg_confidence=ws.avg_confidence,
            avg_latency_ms=ws.avg_latency_ms,
            p95_latency_ms=p95_latency_ms,
            latency_class=latency_class,
            win_rate=win_rate,
            avg_pnl=avg_pnl,
            max_pnl=ws.max_pnl,
            min_pnl=ws.min_pnl,
            profit_factor=profit_factor,
            sharpe_ratio=sharpe_ratio,
            trend_count=trend_count,
            range_count=range_count,
            volatile_count=volatile_count,
            buy_count=buy_count,
            sell_count=sell_count,
            insights_html=insights_html,
        )

        return html

    def save(self, html: str, path: Path) -> None:
        DASHBOARD_DIR.mkdir(parents=True, exist_ok=True)
        path.parent.mkdir(parents=True, exist_ok=True)
        path.write_text(html, encoding="utf-8")


# ── Markdown 报告生成 ────────────────────────────────────────────

class MarkdownReportGenerator:
    """Markdown 格式日报/周报生成器"""

    def generate_daily_report(self, report: BatchReport, tenant_id: str = "default") -> str:
        ts = datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M UTC")
        wr_pct = report.win_rate * 100
        pnl_str = f"+${report.total_pnl:.2f}" if report.total_pnl >= 0 else f"-${abs(report.total_pnl):.2f}"

        md = f"""# LobsterQuant 每日交易报告

**生成时间**: {ts}
**租户**: `{tenant_id}`
**统计周期**: {report.period_start[:10]} ~ {report.period_end[:10]}

---

## [STATS] 核心绩效指标

| 指标 | 数值 | 状态 |
|------|------|------|
| 总交易数 | {report.total_events} | {'[OK]' if report.total_events > 0 else '[WARN]️ 无数据'} |
| 胜率 | {wr_pct:.1f}% | {'[OK] 达标' if wr_pct >= 50 else '[WARN]️ 待优化'} |
| 总盈亏 | {pnl_str} | {'[GREEN] 盈利' if report.total_pnl >= 0 else '[RED] 亏损'} |
| 盈亏比 | {report.profit_factor:.2f} | {'[OK] 良好' if report.profit_factor >= 1.5 else '[WARN]️ 观察'} |
| 夏普比 | {report.sharpe_ratio:.2f} | {'[OK] 正夏普' if report.sharpe_ratio > 0 else '[WARN]️ 负夏普'} |
| 最大回撤 | ${report.max_drawdown:.2f} | {'[OK] 安全' if report.max_drawdown < 500 else '[WARN]️ 风险'} |

## [UP] 盈亏分析

- **平均盈亏**: ${report.avg_pnl:+.2f}
- **平均盈利**: ${report.avg_win:+.2f}
- **平均亏损**: -${report.avg_loss:.2f}
- **盈亏比**: {report.profit_factor:.2f}

## [HIT] 交易质量

| 指标 | 数值 |
|------|------|
| 平均置信度 | {report.avg_confidence:.0%}
| 平均执行延迟 | {report.avg_latency_ms:.0f}ms
| 平均持仓时长 | {report.avg_duration_min:.0f} 分钟
| 快速执行率 | {report.execution_quality.get('fast_execution_pct', 0)}%

## [STATS] 市场状态分布

| Regime | 交易数 | 占比 |
|--------|--------|------|
"""
        for regime, count in sorted(report.regime_distribution.items()):
            pct = count / report.total_events * 100 if report.total_events > 0 else 0
            md += f"| {regime} | {count} | {pct:.0f}% |\n"

        md += f"""
## [CLOCK] 交易时段分布

"""
        for hour, count in sorted(report.hourly_distribution.items()):
            bar = "█" * min(count, 50)
            md += f"| {hour:02d}:00 | {count:4d} | {bar} |\n"

        md += f"""
## [LIST] 方向分布

| 方向 | 交易数 |
|------|--------|
| BUY (做多) | {report.side_distribution.get('BUY', 0)} |
| SELL (做空) | {report.side_distribution.get('SELL', 0)} |

---

*此报告由 LobsterQuant 系统自动生成 · {ts}*
"""
        return md

    def save_daily_report(self, report: BatchReport, tenant_id: str = "default") -> Path:
        REPORTS_DIR.mkdir(parents=True, exist_ok=True)
        date_str = datetime.now(timezone.utc).strftime("%Y%m%d")
        path = REPORTS_DIR / f"daily_report_{tenant_id}_{date_str}.md"
        path.write_text(self.generate_daily_report(report, tenant_id), encoding="utf-8")
        return path


# ── JSON API 数据生成 ────────────────────────────────────────────

class JSONExporter:
    """生成 API 消费数据"""

    def export_performance(self, report: BatchReport, tenant_id: str = "default") -> dict:
        return {
            "tenant_id": tenant_id,
            "generated_at": datetime.now(timezone.utc).isoformat(),
            "period": {"start": report.period_start, "end": report.period_end},
            "summary": {
                "total_trades": report.total_events,
                "win_rate": round(report.win_rate, 4),
                "total_pnl": round(report.total_pnl, 2),
                "profit_factor": round(report.profit_factor, 4),
                "sharpe_ratio": round(report.sharpe_ratio, 4),
                "max_drawdown": round(report.max_drawdown, 2),
            },
            "averages": {
                "pnl": round(report.avg_pnl, 2),
                "win": round(report.avg_win, 2),
                "loss": round(report.avg_loss, 2),
                "confidence": round(report.avg_confidence, 4),
                "latency_ms": round(report.avg_latency_ms, 1),
                "duration_min": round(report.avg_duration_min, 1),
            },
            "execution_quality": report.execution_quality,
            "distributions": {
                "regime": report.regime_distribution,
                "side": report.side_distribution,
                "hourly": report.hourly_distribution,
            },
        }

    def export_realtime(self, stats: RollingStats) -> dict:
        return {
            "updated_at": stats.updated_at,
            "window_min": stats.window_min,
            "event_count": stats.event_count,
            "wins": stats.wins,
            "losses": stats.losses,
            "win_rate": round(stats.wins / stats.event_count, 4) if stats.event_count > 0 else 0,
            "total_pnl": round(stats.total_pnl, 2),
            "avg_confidence": round(stats.avg_confidence, 4),
            "avg_latency_ms": round(stats.avg_latency_ms, 1),
            "regime_counts": stats.regime_counts,
            "side_counts": stats.side_counts,
        }

    def save_performance(self, report: BatchReport, tenant_id: str = "default") -> Path:
        EXPORT_DIR.mkdir(parents=True, exist_ok=True)
        ts = datetime.now(timezone.utc).strftime("%Y%m%d_%H%M%S")
        path = EXPORT_DIR / f"performance_{tenant_id}_{ts}.json"
        path.write_text(json.dumps(self.export_performance(report, tenant_id), ensure_ascii=False, indent=2), encoding="utf-8")
        return path


# ── CSV 导出 ─────────────────────────────────────────────────────

class CSVExporter:
    """CSV 数据导出"""

    def export_trades(self, events: list[dict], path: Path) -> Path:
        EXPORT_DIR.mkdir(parents=True, exist_ok=True)
        path.parent.mkdir(parents=True, exist_ok=True)

        fieldnames = [
            "ts", "tenant_hash", "sequence", "side", "symbol",
            "volume", "entry_price", "exit_price", "pnl",
            "regime", "confidence", "execution_latency_ms",
            "order_type", "duration_min", "spread_pips", "atr_value",
        ]

        with open(path, "w", newline="", encoding="utf-8") as f:
            writer = csv.DictWriter(f, fieldnames=fieldnames, extrasaction="ignore")
            writer.writeheader()
            for e in events:
                writer.writerow(e)

        return path


# ── 主类 ─────────────────────────────────────────────────────────

class ReportGenerator:
    """统一报表生成器"""

    def __init__(self):
        self.dashboard = DashboardGenerator()
        self.markdown = MarkdownReportGenerator()
        self.json_exporter = JSONExporter()
        self.csv_exporter = CSVExporter()
        self.engine = get_engine()

    def generate_all(
        self,
        report: BatchReport | None = None,
        realtime_stats: RollingStats | None = None,
        tenant_id: str = "default",
        insights: list[dict] | None = None,
    ) -> dict[str, Path]:
        """生成所有类型的报表"""
        results = {}

        # 实时仪表盘
        if realtime_stats is None:
            realtime_stats = self.engine.batch.realtime.get_stats()

        dashboard_data = DashboardData(
            updated_at=datetime.now(timezone.utc).strftime("%Y-%m-%d %H:%M:%S UTC"),
            window_stats=realtime_stats,
            daily_report=report,
            insights=insights,
        )
        html = self.dashboard.generate(dashboard_data)
        dashboard_path = DASHBOARD_DIR / f"dashboard_{tenant_id}.html"
        self.dashboard.save(html, dashboard_path)
        results["dashboard"] = dashboard_path

        # Markdown 日报
        if report is None:
            report = self.engine.run_batch_analysis(tenant_id=tenant_id)
        md_path = self.markdown.save_daily_report(report, tenant_id)
        results["daily_report"] = md_path

        # JSON 导出
        json_path = self.json_exporter.save_performance(report, tenant_id)
        results["performance_json"] = json_path

        return results

    def generate_csv_export(
        self,
        start_date: datetime | None = None,
        end_date: datetime | None = None,
        tenant_id: str = "default",
    ) -> Path:
        """导出 CSV 格式的交易数据"""
        if end_date is None:
            end_date = datetime.now(timezone.utc)
        if start_date is None:
            start_date = end_date - timedelta(days=7)

        events = self.engine._load_cleaned_events(start_date, end_date, tenant_id)
        ts = start_date.strftime("%Y%m%d")
        te = end_date.strftime("%Y%m%d")
        csv_path = EXPORT_DIR / f"trades_{tenant_id}_{ts}_{te}.csv"
        return self.csv_exporter.export_trades(events, csv_path)


# ── 便捷函数 ─────────────────────────────────────────────────────

_generator: ReportGenerator | None = None


def get_generator() -> ReportGenerator:
    global _generator
    if _generator is None:
        _generator = ReportGenerator()
    return _generator


def generate_dashboard(tenant_id: str = "default") -> Path:
    engine = get_engine()
    stats = engine.batch.realtime.get_stats()
    gen = get_generator()
    results = gen.generate_all(realtime_stats=stats, tenant_id=tenant_id)
    return results.get("dashboard")


# ── 主入口 ───────────────────────────────────────────────────────

if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(description="Report generator")
    parser.add_argument("--type", choices=["dashboard", "daily", "csv", "all"], default="all")
    parser.add_argument("--tenant", default="default")
    parser.add_argument("--days", type=int, default=7, help="CSV export days")
    args = parser.parse_args()

    gen = get_generator()
    if args.type == "dashboard":
        path = generate_dashboard(args.tenant)
        print(f"Dashboard: {path}")
    elif args.type == "daily":
        report = gen.engine.run_batch_analysis(tenant_id=args.tenant)
        path = gen.markdown.save_daily_report(report, args.tenant)
        print(f"Daily report: {path}")
    elif args.type == "csv":
        end = datetime.now(timezone.utc)
        start = end - timedelta(days=args.days)
        path = gen.generate_csv_export(start, end, args.tenant)
        print(f"CSV export: {path}")
    else:
        results = gen.generate_all(tenant_id=args.tenant)
        for name, path in results.items():
            print(f"  {name}: {path}")