from datetime import date
from decimal import Decimal
import json

import pytest
from django.utils import timezone

from invest.models import BenchmarkPrice, CashFlow, Portfolio, PortfolioSnapshot, Stock, Transaction
from invest.services import get_performance_chart_data
from links.models import Post, Tag


@pytest.mark.django_db
def test_transaction_price_fields_are_optional(api_client):
    portfolio = Portfolio.objects.create(name="Agent Test")

    response = api_client.post(
        "/api/invest/transactions/",
        {
            "portfolio": portfolio.id,
            "action": "BUY",
            "stock_code": "NVDA",
            "quantity": "2",
            "date": "2026-06-13",
        },
        format="json",
    )

    assert response.status_code == 201
    tx = Transaction.objects.get()
    assert tx.price_per_share is None
    assert tx.currency == "USD"
    assert tx.fee is None
    assert response.data["price_per_share"] is None


@pytest.mark.django_db
def test_transaction_accepts_optional_price_currency_and_fee(api_client):
    portfolio = Portfolio.objects.create(name="Agent Test")

    response = api_client.post(
        "/api/invest/transactions/",
        {
            "portfolio": portfolio.id,
            "action": "BUY",
            "stock_code": "NVDA",
            "quantity": "2",
            "price_per_share": "100.25",
            "currency": "USD",
            "fee": "1.50",
            "date": "2026-06-13",
        },
        format="json",
    )

    assert response.status_code == 201
    tx = Transaction.objects.get()
    assert tx.price_per_share == Decimal("100.250000")
    assert tx.fee == Decimal("1.500000")
    assert response.data["price_per_share"] == "100.250000"
    assert response.data["fee"] == "1.500000"


@pytest.mark.django_db
def test_cashflow_api_records_external_deposits(api_client):
    portfolio = Portfolio.objects.create(name="Agent Test")

    response = api_client.post(
        "/api/invest/cashflows/",
        {
            "portfolio": portfolio.id,
            "flow_type": "DEPOSIT",
            "amount": "2500.00",
            "currency": "USD",
            "date": "2026-06-13",
            "source": "salary",
            "note": "monthly contribution",
        },
        format="json",
    )

    assert response.status_code == 201
    flow = CashFlow.objects.get()
    assert flow.signed_amount == Decimal("2500.00")
    assert response.data["signed_amount"] == "2500.00"


@pytest.mark.django_db
def test_agent_summary_reports_cash_adjusted_return_and_concentration(api_client, monkeypatch):
    portfolio = Portfolio.objects.create(name="Agent Test")
    Stock.objects.create(portfolio=portfolio, stock_code="AAA", quantity=Decimal("10"))
    Stock.objects.create(portfolio=portfolio, stock_code="BBB", quantity=Decimal("5"))
    CashFlow.objects.create(
        portfolio=portfolio,
        flow_type=CashFlow.FLOW_DEPOSIT,
        amount=Decimal("1000.00"),
        currency="USD",
        date=date(2026, 6, 1),
    )

    prices = {"AAA": 100.0, "BBB": 20.0}
    monkeypatch.setattr("invest.services.get_current_price", lambda ticker: prices[ticker])

    response = api_client.get("/api/invest/agent/summary/")

    assert response.status_code == 200
    payload = response.json()
    assert payload["total_value"] == 1100.0
    assert payload["performance"]["net_external_cash_flow"] == 1000.0
    assert payload["performance"]["cash_adjusted_gain"] == 100.0
    assert payload["risk"]["top_1_weight"] == pytest.approx(0.9091, rel=1e-3)
    assert payload["risk"]["max_position"]["stock_code"] == "AAA"
    assert payload["risk"]["concentration_level"] == "HIGH"


@pytest.mark.django_db
def test_performance_endpoint_excludes_deposits_from_gain(api_client):
    portfolio = Portfolio.objects.create(name="Agent Test")
    PortfolioSnapshot.objects.create(
        portfolio=portfolio,
        captured_at=timezone.make_aware(timezone.datetime(2026, 6, 1, 8, 0)),
        total_value=Decimal("1000.00"),
    )
    CashFlow.objects.create(
        portfolio=portfolio,
        flow_type=CashFlow.FLOW_DEPOSIT,
        amount=Decimal("500.00"),
        currency="USD",
        date=date(2026, 6, 8),
    )
    PortfolioSnapshot.objects.create(
        portfolio=portfolio,
        captured_at=timezone.make_aware(timezone.datetime(2026, 6, 15, 8, 0)),
        total_value=Decimal("1700.00"),
    )
    BenchmarkPrice.objects.create(ticker="QQQ", date=date(2026, 6, 1), close=Decimal("100.00"))
    BenchmarkPrice.objects.create(ticker="QQQ", date=date(2026, 6, 8), close=Decimal("110.00"))
    BenchmarkPrice.objects.create(ticker="QQQ", date=date(2026, 6, 15), close=Decimal("120.00"))

    response = api_client.get("/api/invest/performance/?start=2026-06-01&end=2026-06-15")

    assert response.status_code == 200
    payload = response.json()
    assert payload["start_value"] == 1000.0
    assert payload["end_value"] == 1700.0
    assert payload["net_external_cash_flow"] == 500.0
    assert payload["cash_adjusted_gain"] == 200.0
    assert payload["simple_return"] == pytest.approx(0.1333, rel=1e-3)
    assert payload["benchmarks"]["QQQ"]["end_value"] == pytest.approx(1745.45, rel=1e-3)


@pytest.mark.django_db
def test_dashboard_shows_agent_first_metrics(client, monkeypatch):
    portfolio = Portfolio.objects.create(name="Agent Test")
    Stock.objects.create(portfolio=portfolio, stock_code="AAA", quantity=Decimal("10"))
    CashFlow.objects.create(
        portfolio=portfolio,
        flow_type=CashFlow.FLOW_DEPOSIT,
        amount=Decimal("1000.00"),
        currency="USD",
        date=date(2026, 6, 1),
    )
    monkeypatch.setattr("invest.services.get_current_price", lambda ticker: 110.0)

    response = client.get("/invest/")

    assert response.status_code == 200
    content = response.content.decode()
    assert "Net Contributions" in content
    assert "Investment Gain" in content
    assert "Top 5 Concentration" in content
    assert "Same-cashflow Benchmark" in content


@pytest.mark.django_db
def test_performance_chart_data_has_percentage_and_value_modes():
    portfolio = Portfolio.objects.create(name="Agent Test")
    PortfolioSnapshot.objects.create(
        portfolio=portfolio,
        captured_at=timezone.make_aware(timezone.datetime(2026, 1, 1, 8, 0)),
        total_value=Decimal("1000.00"),
    )
    PortfolioSnapshot.objects.create(
        portfolio=portfolio,
        captured_at=timezone.make_aware(timezone.datetime(2026, 1, 8, 8, 0)),
        total_value=Decimal("1100.00"),
    )
    BenchmarkPrice.objects.create(ticker="SPY", date=date(2026, 1, 1), close=Decimal("100.00"))
    BenchmarkPrice.objects.create(ticker="SPY", date=date(2026, 1, 8), close=Decimal("110.00"))
    BenchmarkPrice.objects.create(ticker="QQQ", date=date(2026, 1, 1), close=Decimal("200.00"))
    BenchmarkPrice.objects.create(ticker="QQQ", date=date(2026, 1, 8), close=Decimal("220.00"))

    payload = json.loads(get_performance_chart_data())

    assert payload["baseline_date"] == "2026-01-01"
    assert set(payload["modes"]) == {"percentage", "value"}
    assert payload["modes"]["percentage"]["unit"] == "percent"
    assert payload["modes"]["value"]["unit"] == "currency"
    value_datasets = {dataset["label"]: dataset["data"] for dataset in payload["modes"]["value"]["datasets"]}
    assert value_datasets["All Portfolios"] == [1000.0, 1100.0]
    assert value_datasets["S&P 500 benchmark"] == [1000.0, 1100.0]
    assert value_datasets["QQQ benchmark"] == [1000.0, 1100.0]


@pytest.mark.django_db
def test_dashboard_links_posts_tagged_invest_or_investment(client):
    invest_tag = Tag.objects.create(name="invest", slug="invest")
    investment_tag = Tag.objects.create(name="investment", slug="investment")
    other_tag = Tag.objects.create(name="life", slug="life")
    invest_post = Post.objects.create(
        title="Weekly investment report",
        summary="AI generated market and portfolio notes",
        content="details",
    )
    invest_post.tags.add(invest_tag)
    legacy_post = Post.objects.create(
        title="Legacy investment report",
        summary="Saved before the invest tag standard existed",
        content="details",
    )
    legacy_post.tags.add(investment_tag)
    other_post = Post.objects.create(title="Cooking note", summary="not shown", content="details")
    other_post.tags.add(other_tag)

    response = client.get("/invest/")

    assert response.status_code == 200
    content = response.content.decode()
    assert "Investment Reports" in content
    assert "Weekly investment report" in content
    assert "Legacy investment report" in content
    assert invest_post.get_absolute_url() in content
    assert legacy_post.get_absolute_url() in content
    assert "Cooking note" not in content
    assert "View all posts tagged invest" in content
