import csv
import os
from collections import defaultdict
from datetime import datetime


BASE_DIR = os.path.dirname(os.path.abspath(__file__))
HISTORY_PATH = os.path.join(BASE_DIR, "trades_history.csv")
REPORTS_DIR = os.path.join(BASE_DIR, "reports")


def _to_float(value):
    try:
        if value is None or value == "":
            return None
        return float(value)
    except Exception:
        return None


def _parse_ts(value):
    try:
        return datetime.strptime(value, "%Y-%m-%d %H:%M:%S")
    except Exception:
        return None


def _norm_note(value):
    return (value or "").strip().upper()


def load_rows(csv_path):
    if not os.path.exists(csv_path):
        return []

    rows = []
    with open(csv_path, "r", newline="", encoding="utf-8") as f:
        reader = csv.DictReader(f)
        for row in reader:
            row["side"] = (row.get("side") or "").strip().upper()
            row["notes"] = row.get("notes") or ""
            row["pnl"] = _to_float(row.get("pnl"))
            row["pnl_pct"] = _to_float(row.get("pnl_pct"))
            row["cost"] = _to_float(row.get("cost"))
            row["timestamp_dt"] = _parse_ts(row.get("timestamp"))
            rows.append(row)
    return rows


def summarize(rows):
    buys = [r for r in rows if r["side"] == "BUY"]
    sells = [r for r in rows if r["side"] == "SELL"]
    closes = [r for r in sells if r["pnl_pct"] is not None]

    win_rows = [r for r in closes if r["pnl_pct"] > 0]
    loss_rows = [r for r in closes if r["pnl_pct"] < 0]
    flat_rows = [r for r in closes if r["pnl_pct"] == 0]

    avg_pnl_pct = sum(r["pnl_pct"] for r in closes) / len(closes) if closes else 0.0
    avg_pnl_usd = sum((r["pnl"] or 0.0) for r in closes) / len(closes) if closes else 0.0
    total_pnl_usd = sum((r["pnl"] or 0.0) for r in closes)

    notes_count = defaultdict(int)
    for r in sells:
        notes_count[_norm_note(r["notes"])] += 1

    by_symbol = defaultdict(lambda: {
        "sells": 0,
        "closes": 0,
        "wins": 0,
        "losses": 0,
        "flat": 0,
        "sum_pnl_pct": 0.0,
        "sum_pnl_usd": 0.0,
        "emergency": 0,
    })
    for r in sells:
        symbol = r.get("symbol") or "UNKNOWN"
        s = by_symbol[symbol]
        s["sells"] += 1
        note = _norm_note(r["notes"])
        if "EMERGENCY" in note:
            s["emergency"] += 1

        if r["pnl_pct"] is not None:
            s["closes"] += 1
            s["sum_pnl_pct"] += r["pnl_pct"]
            s["sum_pnl_usd"] += (r["pnl"] or 0.0)
            if r["pnl_pct"] > 0:
                s["wins"] += 1
            elif r["pnl_pct"] < 0:
                s["losses"] += 1
            else:
                s["flat"] += 1

    by_hour = defaultdict(lambda: {
        "closes": 0,
        "wins": 0,
        "losses": 0,
        "sum_pnl_pct": 0.0,
        "sum_pnl_usd": 0.0,
    })
    for r in closes:
        dt = r.get("timestamp_dt")
        if not dt:
            continue
        h = dt.hour
        t = by_hour[h]
        t["closes"] += 1
        t["sum_pnl_pct"] += r["pnl_pct"]
        t["sum_pnl_usd"] += (r["pnl"] or 0.0)
        if r["pnl_pct"] > 0:
            t["wins"] += 1
        elif r["pnl_pct"] < 0:
            t["losses"] += 1

    return {
        "rows": len(rows),
        "buys": len(buys),
        "sells": len(sells),
        "closes": len(closes),
        "wins": len(win_rows),
        "losses": len(loss_rows),
        "flat": len(flat_rows),
        "win_rate": (len(win_rows) / len(closes) * 100.0) if closes else 0.0,
        "avg_pnl_pct": avg_pnl_pct,
        "avg_pnl_usd": avg_pnl_usd,
        "total_pnl_usd": total_pnl_usd,
        "notes_count": dict(notes_count),
        "by_symbol": by_symbol,
        "by_hour": by_hour,
    }


def write_symbol_csv(path, by_symbol):
    fields = [
        "symbol", "sells", "closes", "wins", "losses", "flat", "win_rate_pct",
        "avg_pnl_pct", "sum_pnl_usd", "emergency_sells"
    ]
    rows = []
    for symbol, s in by_symbol.items():
        closes = s["closes"]
        rows.append({
            "symbol": symbol,
            "sells": s["sells"],
            "closes": closes,
            "wins": s["wins"],
            "losses": s["losses"],
            "flat": s["flat"],
            "win_rate_pct": round((s["wins"] / closes * 100.0), 2) if closes else 0.0,
            "avg_pnl_pct": round((s["sum_pnl_pct"] / closes), 4) if closes else 0.0,
            "sum_pnl_usd": round(s["sum_pnl_usd"], 4),
            "emergency_sells": s["emergency"],
        })

    rows.sort(key=lambda x: (x["avg_pnl_pct"], -x["sells"]), reverse=True)
    with open(path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fields)
        writer.writeheader()
        writer.writerows(rows)


def write_hour_csv(path, by_hour):
    fields = ["hour", "closes", "wins", "losses", "win_rate_pct", "avg_pnl_pct", "sum_pnl_usd"]
    rows = []
    for hour, h in by_hour.items():
        closes = h["closes"]
        rows.append({
            "hour": hour,
            "closes": closes,
            "wins": h["wins"],
            "losses": h["losses"],
            "win_rate_pct": round((h["wins"] / closes * 100.0), 2) if closes else 0.0,
            "avg_pnl_pct": round((h["sum_pnl_pct"] / closes), 4) if closes else 0.0,
            "sum_pnl_usd": round(h["sum_pnl_usd"], 4),
        })
    rows.sort(key=lambda x: x["hour"])

    with open(path, "w", newline="", encoding="utf-8") as f:
        writer = csv.DictWriter(f, fieldnames=fields)
        writer.writeheader()
        writer.writerows(rows)


def write_markdown(path, summary):
    notes_sorted = sorted(summary["notes_count"].items(), key=lambda x: x[1], reverse=True)
    symbol_sorted = sorted(
        summary["by_symbol"].items(),
        key=lambda kv: (kv[1]["sum_pnl_pct"] / kv[1]["closes"] if kv[1]["closes"] else -999, kv[1]["sells"]),
        reverse=True,
    )
    top_symbols = symbol_sorted[:10]

    lines = []
    lines.append("# Reporte de Rendimiento del Bot")
    lines.append("")
    lines.append(f"Generado: {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
    lines.append("")
    lines.append("## Resumen Global")
    lines.append("")
    lines.append(f"- Registros totales: {summary['rows']}")
    lines.append(f"- BUY: {summary['buys']}")
    lines.append(f"- SELL: {summary['sells']}")
    lines.append(f"- Cierres con PnL: {summary['closes']}")
    lines.append(f"- Win rate: {summary['win_rate']:.2f}%")
    lines.append(f"- PnL promedio (%): {summary['avg_pnl_pct']:.2f}%")
    lines.append(f"- PnL promedio (USD): ${summary['avg_pnl_usd']:.2f}")
    lines.append(f"- PnL total (USD): ${summary['total_pnl_usd']:.2f}")
    lines.append("")
    lines.append("## Tipos de Salida")
    lines.append("")
    for note, count in notes_sorted:
        label = note if note else "SIN_NOTA"
        lines.append(f"- {label}: {count}")

    lines.append("")
    lines.append("## Top Simbolos (por pnl_pct promedio)")
    lines.append("")
    lines.append("| Symbol | Sells | Closes | WinRate | Avg PnL % | Emergency |")
    lines.append("|---|---:|---:|---:|---:|---:|")
    for symbol, s in top_symbols:
        closes = s["closes"]
        avg = (s["sum_pnl_pct"] / closes) if closes else 0.0
        wr = (s["wins"] / closes * 100.0) if closes else 0.0
        lines.append(
            f"| {symbol} | {s['sells']} | {closes} | {wr:.1f}% | {avg:.2f}% | {s['emergency']} |"
        )

    lines.append("")
    lines.append("## Recomendaciones Automaticas")
    lines.append("")

    emergency_count = summary["notes_count"].get("EMERGENCY SELL", 0)
    if emergency_count > 0:
        lines.append(
            f"- Se detectaron {emergency_count} ventas de emergencia. Mantener ALLOW_EMERGENCY_SELL=false y priorizar reintentos de OCO."
        )

    if summary["win_rate"] < 45:
        lines.append("- Win rate bajo: reducir exposicion en pares fuera de whitelist y operar solo con confirmacion de tendencia.")
    else:
        lines.append("- Win rate razonable: mantener filtros actuales y optimizar simbolos con peor pnl promedio.")

    lines.append("- Revisar reports/performance_by_symbol.csv para desactivar simbolos con peor historial.")
    lines.append("- Revisar reports/performance_by_hour.csv para evitar horas con peor rendimiento.")

    with open(path, "w", encoding="utf-8") as f:
        f.write("\n".join(lines) + "\n")


def main():
    os.makedirs(REPORTS_DIR, exist_ok=True)
    rows = load_rows(HISTORY_PATH)
    if not rows:
        print("No hay datos en trades_history.csv")
        return

    summary = summarize(rows)
    ts = datetime.now().strftime("%Y%m%d_%H%M%S")

    md_latest = os.path.join(REPORTS_DIR, "performance_report_latest.md")
    md_stamped = os.path.join(REPORTS_DIR, f"performance_report_{ts}.md")
    symbol_csv = os.path.join(REPORTS_DIR, "performance_by_symbol.csv")
    hour_csv = os.path.join(REPORTS_DIR, "performance_by_hour.csv")

    write_markdown(md_latest, summary)
    write_markdown(md_stamped, summary)
    write_symbol_csv(symbol_csv, summary["by_symbol"])
    write_hour_csv(hour_csv, summary["by_hour"])

    print("Reporte generado:")
    print(md_latest)
    print(md_stamped)
    print(symbol_csv)
    print(hour_csv)


if __name__ == "__main__":
    main()
