from __future__ import annotations

from decimal import Decimal
from pathlib import Path

from jarvis_finance.config.settings import RuntimePaths, Settings
from jarvis_finance.crypto.manage import adjust_crypto_position, transfer_crypto_position
from jarvis_finance.dashboard import data as dashboard_data
from jarvis_finance.dashboard import main as dashboard_main
from jarvis_finance.dashboard.pages import page_00_command_center, page_04_crypto, page_05_wallets, page_08_reports
from jarvis_finance.reports.crypto_inventory import export_crypto_inventory_report
from test_dashboard_ux_sprint2 import FakeStreamlit, setup_multi_wallet_crypto_conn


def test_auto_streamlit_page_stubs_are_not_present() -> None:
    pages_dir = Path("src/jarvis_finance/dashboard/pages")
    numeric_pages = sorted(p.name for p in pages_dir.glob("[0-9][0-9]_*.py"))
    assert numeric_pages == []
    assert dashboard_main.get_user_navigation_titles() == ["Command Center", "Portfolio", "Crypto", "Wallets", "Position hinzufügen", "Reports"]
    assert "Watchlist" not in dashboard_main.get_user_navigation_titles()


def test_command_center_has_visible_add_position_action() -> None:
    conn = setup_multi_wallet_crypto_conn()
    st = FakeStreamlit(button_presses={"+ Position hinzufügen"})
    page_00_command_center.render(st, conn)
    buttons = [args[0] for name, args, _ in st.calls if name == "button" and args]
    assert "+ Position hinzufügen" in buttons
    assert st.session_state.get("jarvis_requested_page") == "Position hinzufügen"


def test_position_add_page_is_user_mode_wizard_without_technical_ids() -> None:
    from jarvis_finance.dashboard.pages import page_17_position_add

    conn = setup_multi_wallet_crypto_conn()
    st = FakeStreamlit()
    page_17_position_add.render(st, conn)
    text = "\n".join(str(arg) for _name, args, _kwargs in st.calls for arg in args)
    assert "Position hinzufügen" in text
    assert "Assetklasse wählen" in text
    assert "Instrument suchen" in text
    assert "instrument_id" not in text
    assert "catalog_entry_id" not in text
    assert "source_id" not in text


def test_read_only_disables_coin_and_wallet_action_buttons_but_edit_mode_enables() -> None:
    conn = setup_multi_wallet_crypto_conn()
    st = FakeStreamlit()
    st.session_state["jarvis_edit_mode"] = False
    page_04_crypto.render(st, conn)
    action_buttons = [kwargs for name, args, kwargs in st.calls if name == "button" and args and args[0] == "Bestand hinzufügen"]
    assert action_buttons and action_buttons[0].get("disabled") is True
    assert any("Bearbeiten aktivieren" in str(args[0]) for name, args, _ in st.calls if name in {"info", "caption", "warning"} and args)

    st2 = FakeStreamlit()
    st2.session_state["jarvis_edit_mode"] = True
    page_04_crypto.render(st2, conn)
    action_buttons2 = [kwargs for name, args, kwargs in st2.calls if name == "button" and args and args[0] == "Bestand hinzufügen"]
    assert action_buttons2 and action_buttons2[0].get("disabled") is False

    st3 = FakeStreamlit()
    st3.session_state["jarvis_edit_mode"] = False
    page_05_wallets.render(st3, conn)
    wallet_buttons = [kwargs for name, args, kwargs in st3.calls if name == "button" and args and args[0] == "Coin in Wallet hinzufügen"]
    assert wallet_buttons and wallet_buttons[0].get("disabled") is True


def test_crypto_selection_is_saved_and_actions_prefill_manage_context() -> None:
    conn = setup_multi_wallet_crypto_conn()
    st = FakeStreamlit(button_presses={"Bestand korrigieren"})
    st.session_state["jarvis_edit_mode"] = True
    page_04_crypto.render(st, conn)
    assert st.session_state["selected_crypto_asset_id"]
    assert st.session_state["crypto_manage_action"] == "Bestand korrigieren"
    assert st.session_state["crypto_manage_asset_id"] == st.session_state["selected_crypto_asset_id"]


def test_correction_and_transfer_flows_write_audit_events() -> None:
    conn = setup_multi_wallet_crypto_conn()
    eth = next(row for row in dashboard_data.get_crypto_coin_summary(conn) if row["symbol"] == "ETH")
    wallets = dashboard_data.get_crypto_coin_wallet_details(conn, eth["_asset_id"])
    adjust = adjust_crypto_position(
        conn,
        asset_id=eth["_asset_id"],
        wallet_id=wallets[0]["_wallet_id"],
        new_quantity_text="2.5",
        effective_date="2026-02-02",
        note="manual review correction",
        confirm=True,
    )
    assert adjust.transaction_id
    transfer = transfer_crypto_position(
        conn,
        asset_id=eth["_asset_id"],
        from_wallet_id=wallets[0]["_wallet_id"],
        to_wallet_id=wallets[1]["_wallet_id"],
        quantity_text="0.5",
        fee_quantity_text="0",
        effective_date="2026-02-03",
        note="rebalance wallets",
        confirm=True,
    )
    assert transfer.transaction_id
    audit_actions = {r["action"] for r in dashboard_data.get_audit_events(conn, limit=20)}
    assert "crypto_manual_adjustment" in audit_actions
    assert "crypto_transfer" in audit_actions


def test_report_button_generates_runtime_report(tmp_path: Path) -> None:
    conn = setup_multi_wallet_crypto_conn()
    settings = Settings(
        base_currency="CHF",
        runtime_paths=RuntimePaths.from_base(tmp_path),
    )
    result = export_crypto_inventory_report(conn, settings=settings, preferred_format="html", renderer=None)
    assert Path(result["file_path"]).exists()
    assert str(result["file_path"]).startswith(str(tmp_path))
    assert result["format"] == "html"


def test_reports_page_has_real_crypto_report_action_not_dead_button(tmp_path: Path) -> None:
    conn = setup_multi_wallet_crypto_conn()
    st = FakeStreamlit(button_presses={"Crypto-Report erzeugen"})
    st.session_state["jarvis_reports_runtime_dir"] = str(tmp_path)
    page_08_reports.render(st, conn)
    assert any(name == "success" for name, _args, _kwargs in st.calls)
    assert conn.execute("SELECT COUNT(*) AS c FROM reports").fetchone()["c"] == 1


def test_price_update_button_uses_provider_service_only_on_click(monkeypatch) -> None:
    conn = setup_multi_wallet_crypto_conn()
    calls = {"n": 0}

    class Provider:
        def __init__(self, **kwargs) -> None:
            pass
        def get_crypto_prices(self, coingecko_ids, currency="CHF"):
            calls["n"] += 1
            return {cid: __import__("jarvis_finance.market.providers", fromlist=["PriceQuote"]).PriceQuote(cid, currency, Decimal("1")) for cid in coingecko_ids}

    monkeypatch.setattr(page_04_crypto, "CoinGeckoClient", Provider)
    st = FakeStreamlit()
    page_04_crypto.render(st, conn)
    assert calls["n"] == 0

    old = conn.execute("SELECT asset_id FROM crypto_assets WHERE symbol='BTC'").fetchone()["asset_id"]
    conn.execute("DELETE FROM crypto_prices WHERE asset_id=?", (old,))
    st_click = FakeStreamlit(button_presses={"Preise aktualisieren"})
    page_04_crypto.render(st_click, conn)
    assert calls["n"] == 1
    assert any(name == "success" and "Preisupdate abgeschlossen" in args[0] for name, args, _ in st_click.calls if args)


def test_crypto_sort_filter_and_chart_order() -> None:
    conn = setup_multi_wallet_crypto_conn()
    rows = dashboard_data.get_crypto_coin_summary(conn, sort_by="Wert absteigend")
    values = [Decimal(row["_sort_value_chf"]) for row in rows]
    assert values == sorted(values, reverse=True)
    assert [row["label"] for row in dashboard_data.get_crypto_coin_allocation_chart(conn)] == ["Ethereum", "Bitcoin"]
    assert dashboard_data.get_crypto_coin_summary(conn, search="bit")[0]["symbol"] == "BTC"
    assert all(row["status"] == "Aktuell" for row in dashboard_data.get_crypto_coin_summary(conn, status_filter="Aktuell", price_max_age_seconds=999999999))


def test_user_mode_hides_internal_ids_and_import_notes() -> None:
    conn = setup_multi_wallet_crypto_conn()
    eth = next(row for row in dashboard_data.get_crypto_coin_summary(conn) if row["symbol"] == "ETH")
    conn.execute("UPDATE crypto_wallets SET notes=?", ("productive initial crypto snapshot import 2025-12-31; no wallet address from XLSX",))
    detail = dashboard_data.get_crypto_coin_wallet_details(conn, eth["_asset_id"])
    assert "notiz" not in detail[0] or detail[0]["notiz"] == ""
    visible = dashboard_data.visible_user_columns(detail[0])
    assert "_asset_id" not in visible and "_wallet_id" not in visible


def test_alert_text_resolves_instrument_name_and_ticker() -> None:
    conn = setup_multi_wallet_crypto_conn()
    conn.execute(
        "INSERT INTO instruments(instrument_id, asset_class, name, ticker, isin, currency, created_at) VALUES ('instrument-US9229087690','etf','Vanguard US Total Stock Market ETF','VTI','US9229087690','USD','now')"
    )
    conn.execute(
        "INSERT INTO alerts(alert_id, priority, category, entity_type, entity_id, rule_id, message, status, created_at, last_seen_at, occurrence_count, dedup_key, fingerprint) VALUES ('a1','warnung','fx','instrument','instrument-US9229087690','missing_fx','missing_fx','active','now','now',1,'d','f')"
    )
    card = dashboard_data.get_alert_cards(conn, limit=1)[0]
    assert "Vanguard US Total Stock Market ETF (VTI)" in card["title"]
    assert "instrument-US" not in card["title"]


def test_watchlist_hidden_from_user_mode_until_functional() -> None:
    assert "Watchlist" not in dashboard_main.get_user_navigation_titles()
