"""Tests for the Journal class."""

import json
import tempfile
from datetime import datetime, timedelta
from pathlib import Path

import pytest

from mistake_journal.journal import Journal
from mistake_journal.models import Trade


@pytest.fixture
def temp_journal():
    """Create a journal with a temporary data directory."""
    with tempfile.TemporaryDirectory() as tmpdir:
        yield Journal(data_dir=Path(tmpdir))


class TestLogTrade:
    def test_log_trade(self, temp_journal):
        """Log a trade and verify fields."""
        trade_id = temp_journal.log_trade(
            symbol="BTC",
            action="buy",
            price=50000,
            size=1000,
            emotion=4,
            reasoning="breakout pattern",
            had_plan=True,
            stop_loss_set=True,
            take_profit_set=True,
            tags=["fomo"],
            notes="test trade",
        )

        assert trade_id == 1
        trade = temp_journal.get_trade(trade_id)
        assert trade is not None
        assert trade.symbol == "BTC"
        assert trade.action == "buy"
        assert trade.entry_price == 50000
        assert trade.size == 1000
        assert trade.emotion_level == 4
        assert trade.reasoning == "breakout pattern"
        assert trade.had_plan is True
        assert trade.stop_loss_set is True
        assert trade.take_profit_set is True
        assert trade.tags == ["fomo"]
        assert trade.notes == "test trade"
        assert trade.is_closed is False


class TestCloseTrade:
    def test_close_trade(self, temp_journal):
        """Close trade and verify P&L calculation."""
        trade_id = temp_journal.log_trade(
            symbol="ETH",
            action="buy",
            price=2000,
            size=500,
        )

        trade = temp_journal.close_trade(
            trade_id,
            exit_price=2200,
            reflection="Good exit",
            lesson_learned="Patience pays",
        )

        assert trade is not None
        assert trade.is_closed is True
        assert trade.exit_price == 2200
        assert trade.reflection == "Good exit"
        assert trade.lesson_learned == "Patience pays"
        assert trade.pnl is not None
        assert trade.pnl > 0  # Profitable trade

    def test_close_nonexistent_trade(self, temp_journal):
        """Closing a nonexistent trade returns None."""
        result = temp_journal.close_trade(999, exit_price=100)
        assert result is None


class TestPnlCalculation:
    def test_pnl_calculation_buy(self, temp_journal):
        """Buy trade P&L: positive when price goes up."""
        trade_id = temp_journal.log_trade(
            symbol="BTC",
            action="buy",
            price=100,
            size=1000,  # $1000 position
        )
        trade = temp_journal.close_trade(trade_id, exit_price=110)

        # 10% gain on $1000 = $100 profit
        assert trade.pnl == pytest.approx(100, rel=0.01)
        assert trade.pnl_pct == pytest.approx(10, rel=0.01)

    def test_pnl_calculation_sell(self, temp_journal):
        """Sell/short trade P&L: positive when price goes down."""
        trade_id = temp_journal.log_trade(
            symbol="BTC",
            action="sell",
            price=100,
            size=1000,  # $1000 position
        )
        trade = temp_journal.close_trade(trade_id, exit_price=90)

        # 10% gain on short = $100 profit
        assert trade.pnl == pytest.approx(100, rel=0.01)
        assert trade.pnl_pct == pytest.approx(10, rel=0.01)

    def test_pnl_calculation_buy_loss(self, temp_journal):
        """Buy trade loss when price goes down."""
        trade_id = temp_journal.log_trade(
            symbol="ETH",
            action="buy",
            price=100,
            size=500,
        )
        trade = temp_journal.close_trade(trade_id, exit_price=80)

        # 20% loss on $500 = -$100
        assert trade.pnl == pytest.approx(-100, rel=0.01)
        assert trade.pnl_pct == pytest.approx(-20, rel=0.01)


class TestAnalyze:
    def test_analyze_empty(self, temp_journal):
        """Analysis with no trades."""
        analysis = temp_journal.analyze()

        assert analysis.total_trades == 0
        assert analysis.closed_trades == 0
        assert analysis.win_rate == 0
        assert analysis.avg_winner == 0
        assert analysis.avg_loser == 0

    def test_analyze_with_trades(self, temp_journal):
        """Analysis with sample data."""
        # Log and close some trades
        t1 = temp_journal.log_trade(symbol="BTC", action="buy", price=100, size=1000, tags=["fomo"])
        temp_journal.close_trade(t1, exit_price=110)  # Win

        t2 = temp_journal.log_trade(symbol="ETH", action="buy", price=100, size=1000, tags=["fomo"])
        temp_journal.close_trade(t2, exit_price=90)  # Loss

        t3 = temp_journal.log_trade(symbol="SOL", action="buy", price=100, size=1000)
        temp_journal.close_trade(t3, exit_price=120)  # Win

        analysis = temp_journal.analyze()

        assert analysis.total_trades == 3
        assert analysis.closed_trades == 3
        assert analysis.win_rate == pytest.approx(2/3, rel=0.01)
        assert analysis.avg_winner > 0
        assert analysis.avg_loser < 0


class TestGetTradesFilters:
    def test_get_trades_filters(self, temp_journal):
        """Test filtering (closed_only, open_only, symbol, tag)."""
        # Create some trades
        t1 = temp_journal.log_trade(symbol="BTC", action="buy", price=100, size=100, tags=["fomo"])
        temp_journal.close_trade(t1, exit_price=110)

        t2 = temp_journal.log_trade(symbol="ETH", action="buy", price=100, size=100, tags=["revenge"])

        t3 = temp_journal.log_trade(symbol="BTC", action="sell", price=100, size=100, tags=["fomo"])

        # Test closed_only
        closed = temp_journal.get_trades(closed_only=True)
        assert len(closed) == 1
        assert closed[0].id == t1

        # Test open_only
        open_trades = temp_journal.get_trades(open_only=True)
        assert len(open_trades) == 2

        # Test symbol filter
        btc_trades = temp_journal.get_trades(symbol="BTC")
        assert len(btc_trades) == 2

        # Test tag filter
        fomo_trades = temp_journal.get_trades(tag="fomo")
        assert len(fomo_trades) == 2

        revenge_trades = temp_journal.get_trades(tag="revenge")
        assert len(revenge_trades) == 1


class TestExport:
    def test_export_json(self, temp_journal):
        """JSON export."""
        temp_journal.log_trade(symbol="BTC", action="buy", price=50000, size=1000)
        temp_journal.log_trade(symbol="ETH", action="sell", price=3000, size=500)

        json_str = temp_journal.export_json()
        data = json.loads(json_str)

        assert len(data) == 2
        assert data[0]["symbol"] == "BTC"
        assert data[1]["symbol"] == "ETH"

    def test_export_json_to_file(self, temp_journal):
        """JSON export to file."""
        temp_journal.log_trade(symbol="BTC", action="buy", price=50000, size=1000)

        with tempfile.TemporaryDirectory() as tmpdir:
            output_path = Path(tmpdir) / "export.json"
            temp_journal.export_json(output_path)

            assert output_path.exists()
            data = json.loads(output_path.read_text())
            assert len(data) == 1

    def test_export_csv(self, temp_journal):
        """CSV export."""
        t1 = temp_journal.log_trade(symbol="BTC", action="buy", price=50000, size=1000, tags=["fomo", "no-plan"])
        temp_journal.close_trade(t1, exit_price=55000)

        csv_str = temp_journal.export_csv()

        assert "BTC" in csv_str
        assert "buy" in csv_str
        assert "50000" in csv_str
        assert "fomo;no-plan" in csv_str  # Tags joined with semicolon

    def test_export_csv_closed_only(self, temp_journal):
        """CSV export with closed_only filter."""
        t1 = temp_journal.log_trade(symbol="BTC", action="buy", price=50000, size=1000)
        temp_journal.close_trade(t1, exit_price=55000)

        temp_journal.log_trade(symbol="ETH", action="buy", price=3000, size=500)  # Open

        csv_str = temp_journal.export_csv(closed_only=True)

        assert "BTC" in csv_str
        assert "ETH" not in csv_str

    def test_export_csv_to_file(self, temp_journal):
        """CSV export to file."""
        temp_journal.log_trade(symbol="BTC", action="buy", price=50000, size=1000)

        with tempfile.TemporaryDirectory() as tmpdir:
            output_path = Path(tmpdir) / "export.csv"
            temp_journal.export_csv(output_path)

            assert output_path.exists()
            content = output_path.read_text()
            assert "BTC" in content


class TestGetTradesInPeriod:
    def test_get_trades_in_period(self, temp_journal):
        """Test filtering trades by date range."""
        # We can't easily set entry_date, so we test that current trades are included
        temp_journal.log_trade(symbol="BTC", action="buy", price=100, size=100)
        temp_journal.log_trade(symbol="ETH", action="buy", price=100, size=100)

        now = datetime.now()
        start = now - timedelta(days=1)
        end = now + timedelta(days=1)

        trades = temp_journal.get_trades_in_period(start, end)
        assert len(trades) == 2

        # Future period should return nothing
        future_start = now + timedelta(days=30)
        future_end = now + timedelta(days=60)
        trades = temp_journal.get_trades_in_period(future_start, future_end)
        assert len(trades) == 0
