"""knowledge_writer.py — Auto-generate markdown knowledge base from trade data.

Phase 1: Generates Obsidian-compatible markdown files with YAML frontmatter
and [[wikilinks]] for bidirectional knowledge connections.

Files generated:
  memory/trades/{date}-{side}-{ticket}.md    — Per-trade notes
  memory/strategies/{strategy_id}.md          — Strategy performance pages
  memory/patterns/{pattern_name}.md           — Win pattern pages
  memory/lessons/{lesson_type}.md             — Loss lesson pages
  memory/INDEX.md                             — Auto-rebuilt MOC

Usage::

    from src.learning.knowledge_writer import write_trade_note, rebuild_index
    write_trade_note(features, outcome)
    rebuild_index()
"""

from __future__ import annotations

import os
import time as _time
from pathlib import Path
from typing import Any

import config_manager


def _memory_dir() -> Path:
    d = config_manager.get_data_dir().parent / "memory"
    d.mkdir(parents=True, exist_ok=True)
    return d


def _ensure_subdir(name: str) -> Path:
    d = _memory_dir() / name
    d.mkdir(parents=True, exist_ok=True)
    return d


def _slug(text: str) -> str:
    return "".join(c if c.isalnum() or c in "-_" else "-" for c in str(text)).strip("-")[:60]


# ═══════════════════════════════════════════════════════════════════════════════
# Trade Notes
# ═══════════════════════════════════════════════════════════════════════════════

def write_trade_note(entry_features: dict[str, Any], outcome: dict[str, Any]) -> str | None:
    """Generate a markdown note for a completed trade.

    Returns the file path on success, None on failure.
    """
    try:
        ef = {k: v for k, v in (entry_features or {}).items()}
        oc = {k: v for k, v in (outcome or {}).items()}

        ticket = ef.get("ticket", 0) or 0
        side = str(ef.get("side", "BUY") or "BUY").upper()
        opened = str(ef.get("opened_at", "") or "")
        date_str = opened[:10] if opened else _time.strftime("%Y-%m-%d")
        strategy_id = str(ef.get("strategy_id", "unknown") or "unknown")
        regime = str(ef.get("regime", "UNKNOWN") or "UNKNOWN").upper()
        session = str(ef.get("session", "UNKNOWN") or "UNKNOWN").upper()
        confidence = float(ef.get("confidence", 0) or 0)
        entry_price = float(ef.get("entry_price", 0) or 0)
        sl_price = float(ef.get("sl_price", 0) or 0)
        tp_price = float(ef.get("tp_price", 0) or 0)
        agent_dir = str(ef.get("agent_direction", "NEUTRAL") or "NEUTRAL").upper()
        agent_aligned = int(ef.get("agent_aligned", 0) or 0)
        adx = float(ef.get("adx_value", 0) or 0)
        vol_ratio = float(ef.get("vol_ratio", 1.0) or 1.0)

        pnl = float(oc.get("pnl", 0) or 0)
        pnl_atr = float(oc.get("pnl_atr", 0) or 0)
        is_win = int(oc.get("is_win", 0) or 0)
        sl_hit = int(oc.get("sl_hit", 0) or 0)
        tp_hit = int(oc.get("tp_hit", 0) or 0)
        duration = int(oc.get("duration_min", 0) or 0)
        exit_reason = str(oc.get("exit_reason", "") or "")
        capture = float(oc.get("capture_ratio", 0) or 0)

        win_marker = "WIN" if is_win else "LOSS"
        slug_id = f"{date_str}-{side}-{ticket or 'unknown'}"
        filename = f"{slug_id}.md"
        filepath = _ensure_subdir("trades") / filename

        tags = ["trade", win_marker.lower(), strategy_id, regime.lower(), session.lower()]
        if agent_aligned:
            tags.append("agent-aligned")
        else:
            tags.append("counter-agent")

        frontmatter = f"""---
ticket: {ticket}
date: {opened or date_str}
strategy: "[[strategies/{strategy_id}]]"
side: {side}
regime: "[[regimes/{regime}]]"
session: "[[{session}]]"
entry: {entry_price}
sl: {sl_price}
tp: {tp_price}
exit_price: {oc.get('entry_price', entry_price)}
pnl: {pnl}
pnl_atr: {pnl_atr}
is_win: {bool(is_win)}
confidence: {confidence}
agent_direction: "[[agents/{agent_dir}]]"
agent_aligned: {bool(agent_aligned)}
adx: {adx}
vol_ratio: {vol_ratio}
duration_min: {duration}
exit_reason: "{exit_reason}"
capture_ratio: {capture}
tags: [{', '.join(tags)}]
---

# Trade {date_str} {side} #{ticket or '?'}

## Outcome
- **{win_marker}**: {('+' if pnl > 0 else '')}${pnl:.2f} ({pnl_atr:+.2f} ATR)
- Duration: {duration}min | Exit: {exit_reason or 'unknown'}
- {'TP hit' if tp_hit else 'SL hit' if sl_hit else 'Manual/Time exit'}
- Capture ratio: {capture*100:.0f}%

## Context
- Strategy: [[strategies/{strategy_id}]] in [[regimes/{regime}]] regime
- Session: [[{session}]] | ADX: {adx:.1f} | Vol ratio: {vol_ratio:.2f}
- AI direction: [[agents/{agent_dir}]] ({"aligned" if agent_aligned else "counter"})
- Confidence: {confidence:.2f}

## Execution
- Entry: {entry_price} | SL: {sl_price} | TP: {tp_price}
"""

        filepath.write_text(frontmatter, encoding="utf-8")
        return str(filepath)
    except Exception:
        return None


# ═══════════════════════════════════════════════════════════════════════════════
# Strategy Pages
# ═══════════════════════════════════════════════════════════════════════════════

def update_strategy_page(strategy_id: str, stats: dict[str, Any]) -> str | None:
    """Update or create a strategy performance page."""
    try:
        sid = str(strategy_id or "unknown")
        filepath = _ensure_subdir("strategies") / f"{sid}.md"
        wr = float(stats.get("win_rate", 0) or 0)
        pf = float(stats.get("profit_factor", 0) or 0)
        trades = int(stats.get("total_trades", 0) or 0)
        streak = int(stats.get("loss_streak", 0) or 0)

        content = f"""---
strategy_id: {sid}
win_rate: {wr}
profit_factor: {pf}
total_trades: {trades}
loss_streak: {streak}
updated: {_time.strftime('%Y-%m-%dT%H:%M:%S')}
tags: [strategy, {sid}]
---

# {sid}

## Performance
- Win rate: {wr*100:.1f}% ({trades} trades)
- Profit factor: {pf:.2f}
- Current loss streak: {streak}

## Related
- Patterns: see [[patterns/]]
- Lessons: see [[lessons/]]
- Back to [[INDEX]]
"""
        filepath.write_text(content, encoding="utf-8")
        return str(filepath)
    except Exception:
        return None


# ═══════════════════════════════════════════════════════════════════════════════
# Pattern Pages
# ═══════════════════════════════════════════════════════════════════════════════

def write_pattern_note(pattern: dict[str, Any]) -> str | None:
    """Generate a markdown note for a discovered win pattern."""
    try:
        conditions = pattern.get("conditions", [])
        cond_str = "-".join(
            f"{c.get('feature','')}{c.get('op','')}{c.get('value','')}"
            for c in conditions
        )
        slug = _slug(f"WIN-{cond_str}")[:50]
        filepath = _ensure_subdir("patterns") / f"{slug}.md"

        wr = float(pattern.get("win_rate", 0) or 0)
        n = int(pattern.get("sample_size", 0) or 0)
        confidence = float(pattern.get("confidence", 0) or 0)

        cond_lines = "\n".join(
            f"- `{c.get('feature')} {c.get('op')} {c.get('value')}`"
            for c in conditions
        )

        content = f"""---
pattern_type: win
win_rate: {wr}
sample_size: {n}
confidence: {confidence}
discovered: {_time.strftime('%Y-%m-%dT%H:%M:%S')}
tags: [pattern, win, high-win-rate]
---

# Win Pattern: {slug.replace('-', ' ')}

## Conditions
{cond_lines}

## Stats
- Win rate: {wr*100:.1f}% (n={n})
- Confidence: {confidence:.2f}

## Action
When these conditions are met, signals from [[strategies/]] aligned with this pattern
should receive a confidence boost.

## Related
- Back to [[INDEX]]
- See all [[patterns/]]
"""
        filepath.write_text(content, encoding="utf-8")
        return str(filepath)
    except Exception:
        return None


# ═══════════════════════════════════════════════════════════════════════════════
# Lesson Pages
# ═══════════════════════════════════════════════════════════════════════════════

def write_lesson_note(lesson: dict[str, Any]) -> str | None:
    """Generate a markdown note for a discovered loss lesson."""
    try:
        lesson_type = str(lesson.get("lesson_type", "unknown") or "unknown")
        slug = _slug(lesson_type)[:50]
        filepath = _ensure_subdir("lessons") / f"{slug}.md"

        wr = float(lesson.get("win_rate", 0) or 0)
        loss_count = int(lesson.get("loss_count", 0) or 0)
        rule = str(lesson.get("avoidance_rule", "") or "")
        applied = int(lesson.get("applied", 0) or 0)
        total_pnl = float(lesson.get("total_pnl", 0) or 0)

        content = f"""---
lesson_type: {lesson_type}
win_rate: {wr}
loss_count: {loss_count}
total_pnl: {total_pnl}
applied: {bool(applied)}
discovered: {_time.strftime('%Y-%m-%dT%H:%M:%S')}
tags: [lesson, loss, avoid, {slug}]
---

# Lesson: {lesson.get('label', lesson_type)}

## Stats
- Win rate: {wr*100:.1f}%
- Cumulative loss: ${total_pnl:.2f} ({loss_count} losses)
- Status: {"ACTIVE — auto-avoiding" if applied else "PENDING — needs review"}

## Rule
> {rule}

## Related
- Back to [[INDEX]]
- See all [[lessons/]]
"""
        filepath.write_text(content, encoding="utf-8")
        return str(filepath)
    except Exception:
        return None


# ═══════════════════════════════════════════════════════════════════════════════
# Index (MOC)
# ═══════════════════════════════════════════════════════════════════════════════

def rebuild_index() -> str | None:
    """Rebuild memory/INDEX.md with links to all knowledge base sections."""
    try:
        mem = _memory_dir()
        sections = {
            "strategies": "策略表现追踪",
            "trades": "交易记录",
            "patterns": "高胜率模式",
            "lessons": "亏损教训",
            "regimes": "市况知识",
            "agents": "AI判断记录",
            "daily": "每日总结",
            "weekly": "每周复盘",
        }

        lines = [
            "---",
            f"updated: {_time.strftime('%Y-%m-%dT%H:%M:%S')}",
            "system: GoldAutoTrade",
            "purpose: Knowledge Base Index (MOC)",
            "---",
            "",
            "# GoldAutoTrade 交易知识库",
            "",
            "> 自动维护 | 本地文件 | [[双向链接]] | Git 可版本控制",
            "",
            "## 快速导航",
            "",
        ]

        for folder, desc in sections.items():
            d = mem / folder
            if d.is_dir():
                files = list(d.glob("*.md"))
                count = len(files)
                lines.append(f"- [[{folder}/]] — {desc} ({count} 条记录)")
            else:
                lines.append(f"- {folder}/ — {desc} (待生成)")

        lines.extend([
            "",
            "## 最新条目",
            "",
        ])

        # Add 5 most recent trade files
        trades_dir = mem / "trades"
        if trades_dir.is_dir():
            recent = sorted(trades_dir.glob("*.md"), key=lambda p: p.stat().st_mtime, reverse=True)[:5]
            for f in recent:
                name = f.stem
                lines.append(f"- [[trades/{name}]]")

        if len(lines) <= 15:  # Only header, no content yet
            lines.append("_系统开始运行后，此区域自动更新最新交易和发现。_")

        (mem / "INDEX.md").write_text("\n".join(lines), encoding="utf-8")
        return str(mem / "INDEX.md")
    except Exception:
        return None
