from __future__ import annotations

import pytest

from jarvis_finance.dashboard.pages import page_13_equity_manage
from jarvis_finance.equity.manage import add_manual_position_from_catalog, create_manual_catalog_entry
from jarvis_finance.market_data.catalog import (
    CatalogEntryInput,
    UnavailableCatalogLookupProvider,
    search_instruments,
    search_local_catalog,
    upsert_catalog_entry,
)
from jarvis_finance.storage.database import connect_memory
from jarvis_finance.storage.migrations import apply_migrations


class FakeTab:
    def __enter__(self): return self
    def __exit__(self, *args): return False


class FakeSt:
    def title(self, *args, **kwargs): pass
    def caption(self, *args, **kwargs): pass
    def warning(self, *args, **kwargs): pass
    def subheader(self, *args, **kwargs): pass
    def write(self, *args, **kwargs): pass
    def success(self, *args, **kwargs): pass
    def error(self, *args, **kwargs): pass
    def info(self, *args, **kwargs): pass
    def divider(self, *args, **kwargs): pass
    def dataframe(self, *args, **kwargs): pass
    def table(self, *args, **kwargs): pass
    def tabs(self, labels): return [FakeTab() for _ in labels]
    def selectbox(self, label, options, **kwargs): return options[0] if options else None
    def text_input(self, *args, **kwargs): return kwargs.get("value", "")
    def text_area(self, *args, **kwargs): return ""
    def date_input(self, *args, **kwargs): return "2025-12-31"
    def checkbox(self, *args, **kwargs): return False
    def expander(self, *args, **kwargs): return FakeTab()
    def button(self, *args, **kwargs): return False


def setup_conn():
    conn = connect_memory()
    apply_migrations(conn)
    conn.execute("INSERT INTO platforms(platform_id,name,platform_type,default_currency,created_at) VALUES('p1','Manual Broker','broker','CHF','now')")
    conn.execute("INSERT INTO accounts(account_id,platform_id,account_name,account_type,currency,created_at) VALUES('a1','p1','Depot','brokerage','CHF','now')")
    return conn


def add_catalog_entries(conn):
    first = upsert_catalog_entry(conn, CatalogEntryInput(asset_class='etf', name='Sample ETF CHF', isin='CH0000000001', ticker='SAMP', exchange='SWX', trading_currency='CHF', instrument_currency='CHF', provider='manual', provider_symbol='SAMP.SW', hedge_status='unknown', instrument_status='unknown', source_confidence='high'), note='test catalog entry')
    second = upsert_catalog_entry(conn, CatalogEntryInput(asset_class='etf', name='Sample ETF EUR', isin='CH0000000001', ticker='SAMP', exchange='XETRA', trading_currency='EUR', instrument_currency='EUR', provider='manual', provider_symbol='SAMP.DE', hedge_status='unhedged', instrument_status='active', source_confidence='high'), note='test catalog entry')
    return first, second


def test_local_instrument_search_by_isin() -> None:
    conn = setup_conn(); add_catalog_entries(conn)
    rows = search_local_catalog(conn, 'CH0000000001', 'etf')
    assert len(rows) == 2
    assert {r.exchange for r in rows} == {'SWX', 'XETRA'}


def test_local_instrument_search_by_symbol() -> None:
    conn = setup_conn(); add_catalog_entries(conn)
    rows = search_local_catalog(conn, 'SAMP')
    assert len(rows) == 2


def test_multiple_listings_same_isin_are_kept() -> None:
    conn = setup_conn(); add_catalog_entries(conn)
    count = conn.execute("SELECT COUNT(*) AS c FROM instrument_catalog_entries WHERE isin='CH0000000001'").fetchone()['c']
    assert count == 2


def test_symbol_without_exchange_is_not_unique_auto_mapping() -> None:
    conn = setup_conn()
    upsert_catalog_entry(conn, CatalogEntryInput(asset_class='etf', name='No Exchange ETF', isin='CH1', ticker='AMB', trading_currency='CHF', instrument_currency='CHF'), note='test')
    row = search_local_catalog(conn, 'AMB')[0]
    assert row.exchange is None
    assert row.confidence == 'low'


def test_manual_candidate_selection_writes_audit() -> None:
    conn = setup_conn(); cid, _ = add_catalog_entries(conn)
    result = add_manual_position_from_catalog(conn, catalog_entry_id=cid, account_id='a1', position_type='initial_snapshot', quantity_text='1.23', trade_date='2025-12-31', currency='CHF', cost_basis_original_text='100', fx_status='not_needed', note='manual selected candidate', confirm=True)
    assert result.transaction_id
    assert conn.execute("SELECT COUNT(*) AS c FROM audit_log WHERE action='manual_position_add'").fetchone()['c'] == 1


def test_missing_provider_symbol_creates_data_quality_hint() -> None:
    conn = setup_conn()
    cid = create_manual_catalog_entry(conn, asset_class='etf', name='No Provider ETF', isin='CH2', ticker='NOP', exchange='SWX', currency='CHF', provider_symbol='', note='manual catalog')
    add_manual_position_from_catalog(conn, catalog_entry_id=cid, account_id='a1', position_type='initial_snapshot', quantity_text='1', trade_date='2025-12-31', currency='CHF', cost_basis_original_text='10', fx_status='not_needed', note='manual add', confirm=True)
    assert conn.execute("SELECT COUNT(*) AS c FROM alerts WHERE rule_id='missing_provider_symbol' AND status='active'").fetchone()['c'] == 1


def test_hedge_status_unknown_creates_warning() -> None:
    conn = setup_conn(); cid, _ = add_catalog_entries(conn)
    add_manual_position_from_catalog(conn, catalog_entry_id=cid, account_id='a1', position_type='initial_snapshot', quantity_text='1', trade_date='2025-12-31', currency='CHF', cost_basis_original_text='10', fx_status='not_needed', note='manual add', confirm=True)
    assert conn.execute("SELECT COUNT(*) AS c FROM alerts WHERE rule_id='hedge_status_unknown' AND status='active'").fetchone()['c'] == 1


def test_manual_position_initial_snapshot_creates_ledger_transaction() -> None:
    conn = setup_conn(); cid, _ = add_catalog_entries(conn)
    add_manual_position_from_catalog(conn, catalog_entry_id=cid, account_id='a1', position_type='initial_snapshot', quantity_text='1.2345', trade_date='2025-12-31', currency='CHF', cost_basis_original_text='10', fx_status='not_needed', note='manual add', confirm=True)
    row = conn.execute("SELECT quantity, transaction_type FROM transactions").fetchone()
    assert row['quantity'] == '1.2345'
    assert row['transaction_type'] == 'initial_position_snapshot'


def test_cost_basis_missing_alert() -> None:
    conn = setup_conn(); cid, _ = add_catalog_entries(conn)
    result = add_manual_position_from_catalog(conn, catalog_entry_id=cid, account_id='a1', position_type='initial_snapshot', quantity_text='1', trade_date='2025-12-31', currency='CHF', cost_basis_original_text=None, fx_status='not_needed', note='manual add missing cost', confirm=True)
    assert 'cost_basis_uncertain' in result.warnings
    assert conn.execute("SELECT COUNT(*) AS c FROM alerts WHERE rule_id='cost_basis_uncertain' AND status='active'").fetchone()['c'] == 1


def test_fx_missing_alert() -> None:
    conn = setup_conn(); _, cid = add_catalog_entries(conn)
    result = add_manual_position_from_catalog(conn, catalog_entry_id=cid, account_id='a1', position_type='initial_snapshot', quantity_text='1', trade_date='2025-12-31', currency='EUR', cost_basis_original_text='10', fx_status='missing', note='manual add missing fx', confirm=True)
    assert 'missing_fx' in result.warnings
    assert conn.execute("SELECT COUNT(*) AS c FROM alerts WHERE rule_id='missing_fx' AND status='active'").fetchone()['c'] == 1


def test_dashboard_renders_without_live_api() -> None:
    conn = setup_conn(); add_catalog_entries(conn)
    page_13_equity_manage.render(FakeSt(), conn)


def test_provider_lookup_failure_does_not_crash() -> None:
    conn = setup_conn()
    results, warnings = search_instruments(conn, 'ABC', providers=[UnavailableCatalogLookupProvider()])
    assert results == []
    assert 'provider_lookup_unavailable' in warnings
    assert conn.execute("SELECT COUNT(*) AS c FROM alerts WHERE rule_id='provider_lookup_unavailable'").fetchone()['c'] == 1
