from __future__ import annotations

from decimal import Decimal

from jarvis_finance.dashboard import data as dashboard_data
from jarvis_finance.dashboard.views import page_00_command_center, page_03_equities_etfs, page_17_position_add
from jarvis_finance.equity.manage import add_manual_position_from_catalog, create_manual_catalog_entry, ensure_standard_broker_accounts
from jarvis_finance.fx.rates import upsert_fx_rate
from jarvis_finance.ledger.cash import calculate_cash_balances
from jarvis_finance.market_data.prices import MockEquityPriceProvider, store_market_price
from jarvis_finance.storage.database import connect_memory
from jarvis_finance.storage.migrations import apply_migrations
from test_dashboard_ux_sprint2 import FakeStreamlit, setup_multi_wallet_crypto_conn


def _visible_text(st: FakeStreamlit) -> str:
    parts: list[str] = []
    for _name, args, kwargs in st.calls:
        parts.extend(str(arg) for arg in args)
        parts.extend(str(value) for value in kwargs.values())
    return "\n".join(parts)


def setup_equity_conn():
    conn = connect_memory()
    apply_migrations(conn)
    ensure_standard_broker_accounts(conn)
    account_id = conn.execute("SELECT account_id FROM accounts ORDER BY account_id LIMIT 1").fetchone()["account_id"]
    catalog_id = create_manual_catalog_entry(
        conn,
        asset_class="stock",
        name="Synthetic Valued AG",
        currency="USD",
        isin="US0000000001",
        ticker="SYNV",
        exchange="NASDAQ",
        provider="fmp",
        provider_symbol="SYNV",
        note="synthetic catalog entry for MVP regression",
    )
    return conn, account_id, catalog_id


def test_manual_position_uses_cached_historical_fx_without_manual_rate() -> None:
    conn, account_id, catalog_id = setup_equity_conn()
    upsert_fx_rate(conn, base_currency="USD", quote_currency="CHF", rate_date="2026-01-15", rate=Decimal("0.91"), provider="mock", rate_type="close")
    conn.commit()

    result = add_manual_position_from_catalog(
        conn,
        catalog_entry_id=catalog_id,
        account_id=account_id,
        position_type="initial_snapshot",
        quantity_text="2",
        trade_date="2026-01-15",
        currency="USD",
        cost_basis_original_text="100",
        fx_status="ok",
        note="synthetic position with cached FX",
        confirm=True,
    )

    tx = conn.execute("SELECT fx_rate_to_chf, fx_source, fx_status, gross_amount_chf FROM transactions WHERE transaction_id=?", (result.transaction_id,)).fetchone()
    assert tx["fx_rate_to_chf"] == "0.91"
    assert tx["fx_source"] == "cache:mock"
    assert tx["fx_status"] == "ok"
    assert tx["gross_amount_chf"] == "91.0000"


def test_position_add_user_mode_hides_internal_fx_status_values_for_foreign_currency() -> None:
    conn, _account_id, catalog_id = setup_equity_conn()
    search_result = conn.execute("SELECT * FROM instrument_catalog_entries WHERE catalog_entry_id=?", (catalog_id,)).fetchone()
    selected = {
        "catalog_entry_id": catalog_id,
        "name": search_result["name"],
        "isin": search_result["isin"],
        "ticker": search_result["ticker"],
        "exchange": search_result["exchange"],
        "currency": "USD",
        "data_source": "local",
        "trust_status": "Bestätigt",
    }
    st = FakeStreamlit(
        inputs={
            "position_add_asset_choice": "Aktie",
            "position_add_search_results": {"results": [selected], "warnings": []},
            "position_add_selected_index": 0,
            "position_add_pos_currency": "USD",
            "position_add_fx_handling": "Automatisch aus Cache/Provider holen",
        },
    )
    st.session_state["position_add_search_results"] = {"results": [selected], "warnings": []}
    st.session_state["position_add_selected_index"] = 0

    page_17_position_add.render(st, conn)

    text = _visible_text(st)
    assert "FX-Status" not in text
    for internal in ["not_needed", "manual_override_required", "manual_override"]:
        assert internal not in text
    assert "Automatisch aus Cache/Provider holen" in text


def test_cash_asset_choice_renders_cash_entry_and_writes_audited_snapshot() -> None:
    conn = connect_memory()
    apply_migrations(conn)
    ensure_standard_broker_accounts(conn)
    st = FakeStreamlit(
        button_presses={"Cash speichern"},
        inputs={
            "position_add_asset_choice": "Cash",
            "position_add_cash_account": "Anderes Konto · Manual Portfolio · CHF",
            "position_add_cash_currency": "CHF",
            "position_add_cash_amount": "123.45",
            "position_add_cash_date": "2026-01-31",
            "position_add_cash_note": "synthetic cash balance",
            "position_add_cash_confirm": True,
        },
    )

    page_17_position_add.render(st, conn)

    text = _visible_text(st)
    assert "Cash erfassen" in text
    assert "Cash gespeichert" in text
    balances = calculate_cash_balances(conn)
    assert any(balance.amount_original == Decimal("123.45") and balance.currency == "CHF" for balance in balances.balances.values())


def test_equity_page_refreshes_valuation_from_mapped_provider(monkeypatch) -> None:
    conn, account_id, catalog_id = setup_equity_conn()
    upsert_fx_rate(conn, base_currency="USD", quote_currency="CHF", rate_date="2026-01-15", rate=Decimal("0.90"), provider="mock", rate_type="close")
    add_manual_position_from_catalog(
        conn,
        catalog_entry_id=catalog_id,
        account_id=account_id,
        position_type="initial_snapshot",
        quantity_text="2",
        trade_date="2026-01-15",
        currency="USD",
        cost_basis_original_text="100",
        note="synthetic unvalued position",
        confirm=True,
    )
    monkeypatch.setattr(page_03_equities_etfs, "equity_price_provider_by_name", lambda _name: MockEquityPriceProvider({"SYNV": "50"}, currency="USD"))

    st = FakeStreamlit(button_presses={"Bewertung aktualisieren"})
    page_03_equities_etfs.render(st, conn)

    text = _visible_text(st)
    assert "Bewertung aktualisieren" in text
    assert "Bewertung aktualisiert" in text
    assert conn.execute("SELECT COUNT(*) AS c FROM market_prices WHERE close='50'").fetchone()["c"] == 1


def test_command_center_price_refresh_updates_crypto_and_equity(monkeypatch) -> None:
    conn, account_id, catalog_id = setup_equity_conn()
    upsert_fx_rate(conn, base_currency="USD", quote_currency="CHF", rate_date="2026-01-15", rate=Decimal("0.90"), provider="mock", rate_type="close")
    add_manual_position_from_catalog(
        conn,
        catalog_entry_id=catalog_id,
        account_id=account_id,
        position_type="initial_snapshot",
        quantity_text="1",
        trade_date="2026-01-15",
        currency="USD",
        cost_basis_original_text="100",
        note="synthetic cc position",
        confirm=True,
    )
    monkeypatch.setattr(page_00_command_center, "CoinGeckoClient", lambda **_kwargs: type("Provider", (), {"get_crypto_prices": lambda self, ids, currency='CHF': {}})())
    monkeypatch.setattr(page_00_command_center, "equity_price_provider_by_name", lambda _name: MockEquityPriceProvider({"SYNV": "42"}, currency="USD"))

    st = FakeStreamlit(button_presses={"Preise aktualisieren"})
    page_00_command_center.render(st, conn)

    assert "Aktien/ETF" in _visible_text(st)
    assert conn.execute("SELECT COUNT(*) AS c FROM market_prices WHERE close='42'").fetchone()["c"] == 1


def test_crypto_detail_exposes_cache_source_and_timestamp_without_raw_ids() -> None:
    conn = setup_multi_wallet_crypto_conn()
    eth = next(row for row in dashboard_data.get_crypto_coin_summary(conn) if row["symbol"] == "ETH")

    detail = dashboard_data.get_crypto_coin_detail(conn, eth["_asset_id"], price_max_age_seconds=999999999)

    assert detail["preisquelle"] == "synthetic-cache"
    assert detail["cache_status"] == "lokaler Cache"
    assert "asset_id" not in detail
    assert "coingecko_id" not in detail
