"""Tests for cost basis / P&L, per-ticker page, manual entry forms, search."""
from datetime import date
from decimal import Decimal

import pytest

from invest.models import CashFlow, Portfolio, Stock, Transaction
from invest.services import get_cost_basis


@pytest.fixture
def portfolio():
    return Portfolio.objects.create(name="Test Broker")


@pytest.mark.django_db
def test_cost_basis_wac_buy_then_sell(portfolio, monkeypatch):
    monkeypatch.setattr("invest.services.get_current_price", lambda ticker: 150.0)
    Transaction.objects.create(
        portfolio=portfolio, action="BUY", stock_code="NVDA", quantity=Decimal("10"),
        price_per_share=Decimal("100"), date=date(2026, 5, 1),
    )
    Transaction.objects.create(
        portfolio=portfolio, action="BUY", stock_code="NVDA", quantity=Decimal("10"),
        price_per_share=Decimal("120"), date=date(2026, 5, 10),
    )
    Transaction.objects.create(
        portfolio=portfolio, action="SELL", stock_code="NVDA", quantity=Decimal("5"),
        price_per_share=Decimal("140"), date=date(2026, 6, 1),
    )

    result = get_cost_basis()
    rows = result["flattened"]
    assert len(rows) == 1
    row = rows[0]
    # WAC = (10*100 + 10*120) / 20 = 110; 卖 5 后剩 15，成本 = 15*110 = 1650
    assert row["stock_code"] == "NVDA"
    assert row["quantity"] == pytest.approx(15)
    assert row["avg_cost"] == pytest.approx(110.0)
    assert row["total_cost"] == pytest.approx(1650.0)
    assert row["current_value"] == pytest.approx(2250.0)
    assert row["unrealized_pnl"] == pytest.approx(600.0)
    assert row["pnl_pct"] == pytest.approx(600 / 1650 * 100, rel=1e-3)


@pytest.mark.django_db
def test_cost_basis_estimates_price_from_trade_date(portfolio, monkeypatch):
    monkeypatch.setattr("invest.services.get_current_price", lambda ticker: 200.0)
    # 交易无价格 → 用交易日历史收盘价估算
    monkeypatch.setattr(
        "invest.services._get_historical_price",
        lambda ticker, ref_date: 80.0,
    )
    Transaction.objects.create(
        portfolio=portfolio, action="BUY", stock_code="MU", quantity=Decimal("10"),
        date=date(2026, 5, 1),
    )

    row = get_cost_basis()["flattened"][0]
    assert row["avg_cost"] == pytest.approx(80.0)
    assert row["estimated"] is True
    assert row["unrealized_pnl"] == pytest.approx(1200.0)


@pytest.mark.django_db
def test_cost_basis_marks_incomplete_when_no_price_at_all(portfolio, monkeypatch):
    monkeypatch.setattr("invest.services.get_current_price", lambda ticker: 200.0)
    monkeypatch.setattr("invest.services._get_historical_price", lambda ticker, ref_date: None)
    Transaction.objects.create(
        portfolio=portfolio, action="BUY", stock_code="AAA", quantity=Decimal("10"),
        date=date(2026, 5, 1),
    )

    rows = get_cost_basis()["flattened"]
    assert len(rows) == 0  # 无价格可算 → 不产出成本行
    # 但明细仍标记 incomplete 供提示
    assert len(rows) == 0


@pytest.mark.django_db
def test_cost_basis_aggregates_per_portfolio(portfolio, monkeypatch):
    monkeypatch.setattr("invest.services.get_current_price", lambda ticker: 100.0)
    other = Portfolio.objects.create(name="Other Broker")
    Transaction.objects.create(
        portfolio=portfolio, action="BUY", stock_code="MRVL", quantity=Decimal("20"),
        price_per_share=Decimal("50"), date=date(2026, 5, 1),
    )
    Transaction.objects.create(
        portfolio=other, action="BUY", stock_code="MRVL", quantity=Decimal("30"),
        price_per_share=Decimal("60"), date=date(2026, 5, 2),
    )

    result = get_cost_basis()
    assert len(result["flattened"]) == 2  # 两个账户各自一行
    assert set(result["by_portfolio"].keys()) == {portfolio.id, other.id}


@pytest.mark.django_db
def test_stock_detail_page_shows_cross_account_pnl(client, portfolio, monkeypatch):
    monkeypatch.setattr("invest.services.get_current_price", lambda ticker: 120.0)
    other = Portfolio.objects.create(name="Other Broker")
    Stock.objects.create(portfolio=portfolio, stock_code="NVDA", quantity=Decimal("10"))
    Stock.objects.create(portfolio=other, stock_code="NVDA", quantity=Decimal("5"))
    Transaction.objects.create(
        portfolio=portfolio, action="BUY", stock_code="NVDA", quantity=Decimal("10"),
        price_per_share=Decimal("80"), date=date(2026, 5, 1),
    )
    Transaction.objects.create(
        portfolio=other, action="BUY", stock_code="NVDA", quantity=Decimal("5"),
        price_per_share=Decimal("100"), date=date(2026, 5, 2),
    )

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

    assert response.status_code == 200
    content = response.content.decode()
    assert "NVDA" in content
    # 总持仓 15、市值 1800、成本 1300、盈亏 500
    assert "$1,800" in content
    assert "$1,300" in content
    assert "+$500" in content
    assert "Test Broker" in content
    assert "Other Broker" in content


@pytest.mark.django_db
def test_manual_transaction_form_creates_record(client, portfolio):
    response = client.post(
        "/invest/transactions/new/",
        {
            "portfolio": portfolio.id,
            "action": "BUY",
            "stock_code": "nvda",
            "quantity": "3",
            "price_per_share": "90.5",
            "currency": "USD",
            "fee": "1.5",
            "date": "2026-07-30",
        },
    )

    assert response.status_code == 302  # redirect to dashboard
    tx = Transaction.objects.get()
    assert tx.stock_code == "NVDA"  # 大写化
    assert tx.source == "MANUAL"
    assert tx.confidence == Decimal("1.0")
    assert tx.price_per_share == Decimal("90.5")


@pytest.mark.django_db
def test_manual_cashflow_form_creates_record(client, portfolio):
    response = client.post(
        "/invest/cashflows/new/",
        {
            "portfolio": portfolio.id,
            "flow_type": "DEPOSIT",
            "amount": "2000",
            "currency": "AUD",
            "date": "2026-07-31",
            "note": "test deposit",
        },
    )

    assert response.status_code == 302
    flow = CashFlow.objects.get()
    assert flow.source == "MANUAL"
    assert flow.amount == Decimal("2000.00")
    assert flow.flow_type == "DEPOSIT"


@pytest.mark.django_db
def test_dashboard_transaction_search_filters(client, portfolio):
    Transaction.objects.create(
        portfolio=portfolio, action="BUY", stock_code="NVDA", quantity=Decimal("1"),
        price_per_share=Decimal("100"), date=date(2026, 7, 1),
    )
    Transaction.objects.create(
        portfolio=portfolio, action="BUY", stock_code="AMD", quantity=Decimal("1"),
        price_per_share=Decimal("200"), date=date(2026, 7, 2),
    )

    response = client.get("/invest/?q=amd")

    assert response.status_code == 200
    content = response.content.decode()
    assert "AMD" in content
    assert "NVDA" not in content
