"""Core journal functionality."""

import csv
import json
from datetime import datetime, timedelta
from pathlib import Path
from collections import Counter

from .models import Trade, MistakeAnalysis, MistakePattern


class Journal:
    """Trade journal for tracking and analyzing mistakes."""

    def __init__(self, data_dir: Path | None = None):
        self.data_dir = data_dir or Path.home() / ".mistake-journal"
        self.data_dir.mkdir(parents=True, exist_ok=True)
        self.trades_file = self.data_dir / "trades.json"
        self._trades: list[Trade] = []
        self._load()

    def _load(self) -> None:
        """Load trades from disk."""
        if self.trades_file.exists():
            data = json.loads(self.trades_file.read_text())
            self._trades = [Trade(**t) for t in data]

    def _save(self) -> None:
        """Save trades to disk."""
        data = [t.model_dump(mode="json") for t in self._trades]
        self.trades_file.write_text(json.dumps(data, indent=2, default=str))

    def _next_id(self) -> int:
        """Get next trade ID."""
        if not self._trades:
            return 1
        return max(t.id for t in self._trades) + 1

    def log_trade(
        self,
        symbol: str,
        action: str,
        price: float,
        size: float,
        emotion: int = 3,
        reasoning: str = "",
        had_plan: bool = False,
        stop_loss_set: bool = False,
        take_profit_set: bool = False,
        tags: list[str] | None = None,
        notes: str = "",
    ) -> int:
        """
        Log a new trade entry.

        Returns the trade ID.
        """
        trade = Trade(
            id=self._next_id(),
            symbol=symbol.upper(),
            action=action.lower(),
            entry_price=price,
            size=size,
            emotion_level=emotion,
            reasoning=reasoning,
            had_plan=had_plan,
            stop_loss_set=stop_loss_set,
            take_profit_set=take_profit_set,
            tags=tags or [],
            notes=notes,
        )
        self._trades.append(trade)
        self._save()
        return trade.id

    def close_trade(
        self,
        trade_id: int,
        exit_price: float,
        reflection: str = "",
        lesson_learned: str = "",
    ) -> Trade | None:
        """Close an open trade."""
        trade = self.get_trade(trade_id)
        if not trade:
            return None

        trade.exit_price = exit_price
        trade.exit_date = datetime.now()
        trade.reflection = reflection
        trade.lesson_learned = lesson_learned
        self._save()
        return trade

    def get_trade(self, trade_id: int) -> Trade | None:
        """Get a trade by ID."""
        for t in self._trades:
            if t.id == trade_id:
                return t
        return None

    def get_trades(
        self,
        closed_only: bool = False,
        open_only: bool = False,
        symbol: str | None = None,
        tag: str | None = None,
    ) -> list[Trade]:
        """Get trades with optional filters."""
        trades = self._trades

        if closed_only:
            trades = [t for t in trades if t.is_closed]
        if open_only:
            trades = [t for t in trades if not t.is_closed]
        if symbol:
            trades = [t for t in trades if t.symbol == symbol.upper()]
        if tag:
            trades = [t for t in trades if tag in t.tags]

        return trades

    def analyze(self) -> MistakeAnalysis:
        """Analyze trading patterns and mistakes."""
        closed = [t for t in self._trades if t.is_closed]

        if not closed:
            return MistakeAnalysis(
                total_trades=len(self._trades),
                closed_trades=0,
                win_rate=0,
                avg_winner=0,
                avg_loser=0,
            )

        winners = [t for t in closed if t.is_winner]
        losers = [t for t in closed if not t.is_winner]

        win_rate = len(winners) / len(closed) if closed else 0
        avg_winner = sum(t.pnl for t in winners) / len(winners) if winners else 0
        avg_loser = sum(t.pnl for t in losers) / len(losers) if losers else 0

        # Analyze mistakes by tag
        tag_trades: dict[str, list[Trade]] = {}
        for trade in closed:
            for tag in trade.tags:
                if tag not in tag_trades:
                    tag_trades[tag] = []
                tag_trades[tag].append(trade)

        top_mistakes = []
        for tag, trades in sorted(tag_trades.items(), key=lambda x: len(x[1]), reverse=True):
            tag_losers = [t for t in trades if not t.is_winner]
            if len(tag_losers) > 0:
                top_mistakes.append(MistakePattern(
                    tag=tag,
                    trade_count=len(trades),
                    loss_rate=len(tag_losers) / len(trades),
                    avg_loss=sum(t.pnl for t in tag_losers) / len(tag_losers) if tag_losers else 0,
                ))

        # Sort by loss rate
        top_mistakes.sort(key=lambda x: x.loss_rate, reverse=True)

        # Emotion analysis
        emotion_results: dict[int, list[bool]] = {}
        for trade in closed:
            if trade.emotion_level not in emotion_results:
                emotion_results[trade.emotion_level] = []
            emotion_results[trade.emotion_level].append(trade.is_winner)

        best_emotion = None
        worst_emotion = None
        if emotion_results:
            emotion_win_rates = {
                e: sum(results) / len(results)
                for e, results in emotion_results.items()
                if len(results) >= 3  # Minimum sample
            }
            if emotion_win_rates:
                best_emotion = max(emotion_win_rates, key=emotion_win_rates.get)
                worst_emotion = min(emotion_win_rates, key=emotion_win_rates.get)

        # Plan correlation
        with_plan = [t for t in closed if t.had_plan]
        without_plan = [t for t in closed if not t.had_plan]

        win_rate_with_plan = None
        win_rate_without_plan = None
        if len(with_plan) >= 3:
            win_rate_with_plan = sum(1 for t in with_plan if t.is_winner) / len(with_plan)
        if len(without_plan) >= 3:
            win_rate_without_plan = sum(1 for t in without_plan if t.is_winner) / len(without_plan)

        # Generate recommendations
        recommendations = []
        if top_mistakes:
            worst = top_mistakes[0]
            if worst.loss_rate > 0.7:
                recommendations.append(
                    f"Stop making '{worst.tag}' trades. Your loss rate is {worst.loss_rate*100:.0f}%."
                )

        if win_rate_with_plan and win_rate_without_plan:
            if win_rate_with_plan > win_rate_without_plan + 0.1:
                recommendations.append(
                    f"Always trade with a plan. Win rate: {win_rate_with_plan*100:.0f}% vs {win_rate_without_plan*100:.0f}% without."
                )

        return MistakeAnalysis(
            total_trades=len(self._trades),
            closed_trades=len(closed),
            win_rate=win_rate,
            avg_winner=avg_winner,
            avg_loser=avg_loser,
            top_mistakes=top_mistakes[:5],
            recommendations=recommendations,
            best_emotion_level=best_emotion,
            worst_emotion_level=worst_emotion,
            win_rate_with_plan=win_rate_with_plan,
            win_rate_without_plan=win_rate_without_plan,
        )

    def get_trades_in_period(
        self,
        start_date: datetime,
        end_date: datetime,
        closed_only: bool = False,
    ) -> list[Trade]:
        """Get trades within a date range."""
        trades = []
        for t in self._trades:
            if start_date <= t.entry_date <= end_date:
                if closed_only and not t.is_closed:
                    continue
                trades.append(t)
        return trades

    def export_json(self, file_path: Path | None = None) -> str:
        """Export trades to JSON. Returns JSON string, optionally writes to file."""
        data = [t.model_dump(mode="json") for t in self._trades]
        json_str = json.dumps(data, indent=2, default=str)
        if file_path:
            file_path.write_text(json_str)
        return json_str

    def export_csv(self, file_path: Path | None = None, closed_only: bool = False) -> str:
        """Export trades to CSV. Returns CSV string, optionally writes to file."""
        trades = self._trades if not closed_only else [t for t in self._trades if t.is_closed]

        if not trades:
            return ""

        fieldnames = [
            "id", "symbol", "action", "entry_price", "size", "entry_date",
            "emotion_level", "reasoning", "had_plan", "stop_loss_set",
            "take_profit_set", "tags", "notes", "exit_price", "exit_date",
            "reflection", "lesson_learned", "pnl", "pnl_pct"
        ]

        import io
        output = io.StringIO()
        writer = csv.DictWriter(output, fieldnames=fieldnames)
        writer.writeheader()

        for t in trades:
            row = {
                "id": t.id,
                "symbol": t.symbol,
                "action": t.action,
                "entry_price": t.entry_price,
                "size": t.size,
                "entry_date": t.entry_date.isoformat(),
                "emotion_level": t.emotion_level,
                "reasoning": t.reasoning,
                "had_plan": t.had_plan,
                "stop_loss_set": t.stop_loss_set,
                "take_profit_set": t.take_profit_set,
                "tags": ";".join(t.tags),
                "notes": t.notes,
                "exit_price": t.exit_price if t.exit_price else "",
                "exit_date": t.exit_date.isoformat() if t.exit_date else "",
                "reflection": t.reflection,
                "lesson_learned": t.lesson_learned,
                "pnl": t.pnl if t.pnl is not None else "",
                "pnl_pct": t.pnl_pct if t.pnl_pct is not None else "",
            }
            writer.writerow(row)

        csv_str = output.getvalue()
        if file_path:
            file_path.write_text(csv_str)
        return csv_str
