"""
回测可视化 — 权益曲线 + 统计图表

生成HTML报告，包含:
1. 权益曲线（对数尺度）
2. 回撤曲线
3. 月度盈亏柱状图
4. 信号类型分布
"""

import os
import pandas as pd
import numpy as np
from typing import List


def generate_report_html(trades: list, initial_capital: float,
                         output_path: str = "backtest_report.html"):
    """
    从交易记录生成完整HTML回测报告

    Args:
        trades: List of TradeRecord
        initial_capital: 初始资金
        output_path: 输出HTML路径
    """
    if not trades:
        print("没有交易记录，无法生成报告")
        return

    df = pd.DataFrame([t.__dict__ for t in trades])

    # ===== 计算统计数据 =====
    total = len(df)
    wins = len(df[df['pnl_dollar'] > 0])
    losses = total - wins
    win_rate = wins / total * 100
    final_balance = df['balance_after'].iloc[-1]
    multiple = final_balance / initial_capital

    avg_win = df[df['pnl_dollar'] > 0]['pnl_pct'].mean() * 100 if wins > 0 else 0
    avg_loss = abs(df[df['pnl_dollar'] <= 0]['pnl_pct'].mean()) * 100 if losses > 0 else 1
    payoff = avg_win / avg_loss if avg_loss > 0 else 0

    gross_profit = df[df['pnl_dollar'] > 0]['pnl_dollar'].sum()
    gross_loss = abs(df[df['pnl_dollar'] <= 0]['pnl_dollar'].sum())
    pf = gross_profit / gross_loss if gross_loss > 0 else float('inf')

    equity = df['balance_after'].values
    peak = np.maximum.accumulate(equity)
    drawdown = (peak - equity) / peak * 100
    max_dd = drawdown.max()

    # ===== 权益曲线数据 =====
    equity_dates = [t.strftime('%Y-%m-%d') for t in pd.to_datetime(df['exit_time'])]
    equity_values = [round(v, 2) for v in equity]
    dd_values = [round(v, 2) for v in drawdown]

    # ===== 月度盈亏 =====
    df['month'] = pd.to_datetime(df['entry_time']).dt.tz_localize(None).dt.to_period('M').astype(str)
    monthly = df.groupby('month')['pnl_dollar'].sum()
    monthly_labels = list(monthly.index)
    monthly_values = [round(v, 2) for v in monthly.values]
    monthly_colors = ['#22c55e' if v >= 0 else '#ef4444' for v in monthly.values]

    # ===== 年度汇总 =====
    df['year'] = pd.to_datetime(df['entry_time']).dt.year
    yearly_rows = ""
    for year, grp in df.groupby('year'):
        n = len(grp)
        w = len(grp[grp['pnl_dollar'] > 0])
        l = n - w
        pnl = grp['pnl_dollar'].sum()
        end_bal = grp['balance_after'].iloc[-1]
        wr = w / n * 100 if n > 0 else 0
        yearly_rows += f"""
        <tr>
            <td>{year}</td><td>{n}</td><td>{w}/{l}</td>
            <td>{wr:.1f}%</td>
            <td class="{'positive' if pnl>=0 else 'negative'}">${pnl:,.0f}</td>
            <td>${end_bal:,.0f}</td>
        </tr>"""

    # ===== 币种汇总 =====
    coin_rows = ""
    for coin, grp in df.groupby('coin'):
        n = len(grp)
        w = len(grp[grp['pnl_dollar'] > 0])
        pnl = grp['pnl_dollar'].sum()
        wr = w / n * 100 if n > 0 else 0
        coin_rows += f"""
        <tr>
            <td>{coin.replace('USDT','')}</td><td>{n}</td>
            <td>{wr:.1f}%</td>
            <td class="{'positive' if pnl>=0 else 'negative'}">${pnl:,.0f}</td>
        </tr>"""

    # ===== 信号类型汇总 =====
    type_rows = ""
    for stype, grp in df.groupby('signal_type'):
        n = len(grp)
        w = len(grp[grp['pnl_dollar'] > 0])
        pnl = grp['pnl_dollar'].sum()
        wr = w / n * 100 if n > 0 else 0
        type_rows += f"""
        <tr>
            <td>{stype}</td><td>{n}</td>
            <td>{wr:.1f}%</td>
            <td class="{'positive' if pnl>=0 else 'negative'}">${pnl:,.0f}</td>
        </tr>"""

    html = f"""<!DOCTYPE html>
<html lang="zh">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>浪浪AI 回测报告</title>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.4.1/chart.umd.min.js"></script>
<style>
* {{ margin: 0; padding: 0; box-sizing: border-box; }}
body {{ font-family: -apple-system, 'Segoe UI', sans-serif; background: #0a0a0a; color: #e0e0e0; }}
.container {{ max-width: 1200px; margin: 0 auto; padding: 20px; }}
h1 {{ text-align: center; color: #f0b429; margin: 30px 0 10px; font-size: 2em; }}
.subtitle {{ text-align: center; color: #888; margin-bottom: 30px; }}
.stats-grid {{
    display: grid; grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
    gap: 15px; margin-bottom: 30px;
}}
.stat-card {{
    background: #1a1a2e; border-radius: 12px; padding: 18px;
    border: 1px solid #333;
}}
.stat-card .label {{ color: #888; font-size: 0.85em; margin-bottom: 5px; }}
.stat-card .value {{ font-size: 1.5em; font-weight: bold; color: #f0b429; }}
.chart-container {{ background: #1a1a2e; border-radius: 12px; padding: 20px; margin-bottom: 20px; border: 1px solid #333; }}
.chart-container h3 {{ color: #f0b429; margin-bottom: 15px; }}
table {{ width: 100%; border-collapse: collapse; margin-bottom: 20px; }}
th {{ background: #1a1a2e; color: #f0b429; padding: 10px; text-align: left; border-bottom: 2px solid #333; }}
td {{ padding: 8px 10px; border-bottom: 1px solid #222; }}
tr:hover {{ background: #1a1a2e; }}
.positive {{ color: #22c55e; }}
.negative {{ color: #ef4444; }}
.table-container {{ background: #111; border-radius: 12px; padding: 20px; margin-bottom: 20px; border: 1px solid #333; }}
.table-container h3 {{ color: #f0b429; margin-bottom: 15px; }}
canvas {{ max-height: 350px; }}
</style>
</head>
<body>
<div class="container">
<h1>浪浪AI 回测报告</h1>
<p class="subtitle">BTC 10x + ETH/山寨 5x | 做多+做空 | 12币种</p>

<div class="stats-grid">
    <div class="stat-card"><div class="label">初始资金</div><div class="value">${initial_capital:,.0f}</div></div>
    <div class="stat-card"><div class="label">最终资金</div><div class="value">${final_balance:,.0f}</div></div>
    <div class="stat-card"><div class="label">收益倍数</div><div class="value">{multiple:,.0f}x</div></div>
    <div class="stat-card"><div class="label">总交易</div><div class="value">{total}笔</div></div>
    <div class="stat-card"><div class="label">胜率</div><div class="value">{win_rate:.1f}%</div></div>
    <div class="stat-card"><div class="label">盈亏比</div><div class="value">{payoff:.1f}</div></div>
    <div class="stat-card"><div class="label">PF</div><div class="value">{pf:.2f}</div></div>
    <div class="stat-card"><div class="label">最大回撤</div><div class="value">{max_dd:.1f}%</div></div>
</div>

<div class="chart-container">
    <h3>权益曲线 (对数尺度)</h3>
    <canvas id="equityChart"></canvas>
</div>

<div class="chart-container">
    <h3>回撤曲线</h3>
    <canvas id="ddChart"></canvas>
</div>

<div class="chart-container">
    <h3>月度盈亏</h3>
    <canvas id="monthlyChart"></canvas>
</div>

<div class="table-container">
    <h3>年度表现</h3>
    <table>
        <tr><th>年份</th><th>交易数</th><th>盈/亏</th><th>胜率</th><th>净盈亏</th><th>年末余额</th></tr>
        {yearly_rows}
    </table>
</div>

<div style="display:grid; grid-template-columns: 1fr 1fr; gap: 20px;">
    <div class="table-container">
        <h3>币种表现</h3>
        <table>
            <tr><th>币种</th><th>交易</th><th>胜率</th><th>净盈亏</th></tr>
            {coin_rows}
        </table>
    </div>
    <div class="table-container">
        <h3>信号类型</h3>
        <table>
            <tr><th>类型</th><th>交易</th><th>胜率</th><th>净盈亏</th></tr>
            {type_rows}
        </table>
    </div>
</div>

</div>

<script>
const eqCtx = document.getElementById('equityChart').getContext('2d');
new Chart(eqCtx, {{
    type: 'line',
    data: {{
        labels: {equity_dates},
        datasets: [{{
            label: '权益 ($)',
            data: {equity_values},
            borderColor: '#f0b429',
            backgroundColor: 'rgba(240,180,41,0.1)',
            fill: true,
            pointRadius: 1,
            borderWidth: 2,
        }}]
    }},
    options: {{
        responsive: true,
        scales: {{
            y: {{
                type: 'logarithmic',
                grid: {{ color: '#222' }},
                ticks: {{ color: '#888', callback: v => '$' + v.toLocaleString() }}
            }},
            x: {{ grid: {{ color: '#222' }}, ticks: {{ color: '#888', maxTicksLimit: 12 }} }}
        }},
        plugins: {{ legend: {{ labels: {{ color: '#e0e0e0' }} }} }}
    }}
}});

const ddCtx = document.getElementById('ddChart').getContext('2d');
new Chart(ddCtx, {{
    type: 'line',
    data: {{
        labels: {equity_dates},
        datasets: [{{
            label: '回撤 (%)',
            data: {dd_values},
            borderColor: '#ef4444',
            backgroundColor: 'rgba(239,68,68,0.15)',
            fill: true,
            pointRadius: 0,
            borderWidth: 1.5,
        }}]
    }},
    options: {{
        responsive: true,
        scales: {{
            y: {{
                reverse: true,
                grid: {{ color: '#222' }},
                ticks: {{ color: '#888', callback: v => v.toFixed(1) + '%' }}
            }},
            x: {{ grid: {{ color: '#222' }}, ticks: {{ color: '#888', maxTicksLimit: 12 }} }}
        }},
        plugins: {{ legend: {{ labels: {{ color: '#e0e0e0' }} }} }}
    }}
}});

const mCtx = document.getElementById('monthlyChart').getContext('2d');
new Chart(mCtx, {{
    type: 'bar',
    data: {{
        labels: {monthly_labels},
        datasets: [{{
            label: '月度盈亏 ($)',
            data: {monthly_values},
            backgroundColor: {monthly_colors},
            borderRadius: 4,
        }}]
    }},
    options: {{
        responsive: true,
        scales: {{
            y: {{ grid: {{ color: '#222' }}, ticks: {{ color: '#888', callback: v => '$'+v.toLocaleString() }} }},
            x: {{ grid: {{ color: '#222' }}, ticks: {{ color: '#888', maxTicksLimit: 20 }} }}
        }},
        plugins: {{ legend: {{ labels: {{ color: '#e0e0e0' }} }} }}
    }}
}});
</script>
</body>
</html>"""

    with open(output_path, 'w', encoding='utf-8') as f:
        f.write(html)
    print(f"报告已生成: {output_path}")
