from __future__ import annotations

from decimal import Decimal

import pytest
from fastapi import HTTPException

from jarvis_finance.api.schemas.positions import (
    AccountValueConfirmRequest,
    AccountValuePreviewRequest,
    CashSnapshotConfirmRequest,
    CashSnapshotPreviewRequest,
)
from jarvis_finance.services.cash_service import (
    CANONICAL_CASH_ACCOUNTS,
    confirm_cash_snapshot,
    ensure_canonical_cash_accounts,
    get_cash_summary,
    preview_cash_snapshot,
)
from jarvis_finance.services.manual_entry_service import (
    confirm_account_value,
    preview_account_value,
    preview_position,
)
from jarvis_finance.services.portfolio_service import get_overview
from jarvis_finance.storage.database import connect_memory
from jarvis_finance.storage.migrations import apply_migrations


def db():
    conn = connect_memory()
    apply_migrations(conn)
    return conn


def test_canonical_cash_accounts_are_present_with_expected_modes_and_audit():
    conn = db()

    result = ensure_canonical_cash_accounts(conn, created_by="test")

    assert result["created"] == 6
    assert result["duplicates"] == []
    rows = conn.execute(
        """
        SELECT a.account_name, a.account_type, a.currency, a.balance_mode, p.name AS platform
        FROM accounts a JOIN platforms p ON p.platform_id=a.platform_id
        WHERE a.account_type='cash'
        ORDER BY a.account_name
        """
    ).fetchall()
    by_name = {row["account_name"]: row for row in rows}
    assert set(by_name) == {item["account_name"] for item in CANONICAL_CASH_ACCOUNTS}
    assert by_name["Raiffeisen"]["balance_mode"] == "csv_calculated"
    assert by_name["AKB Haushaltskonto"]["balance_mode"] == "csv_calculated"
    assert by_name["PostFinance Cash-Konto"]["balance_mode"] == "manual"
    assert by_name["PostFinance Cash-Konto"]["platform"] == "PostFinance Cash"
    assert all(row["currency"] == "CHF" for row in rows)
    audits = conn.execute("SELECT action FROM audit_log WHERE action='cash_account_seed_confirm'").fetchall()
    assert len(audits) == 6


def test_duplicate_cash_accounts_return_consolidation_preview_without_silent_merge():
    conn = db()
    ensure_canonical_cash_accounts(conn)
    conn.execute("INSERT INTO platforms(platform_id, name, platform_type, default_currency, created_at) VALUES ('plat_pf_duplicate', 'PostFinance Duplicate', 'bank', 'CHF', '2026-01-01')")
    conn.execute(
        "INSERT INTO accounts(account_id, platform_id, account_name, account_type, currency, balance_mode, created_at) VALUES ('acct_dup_pf_cash', 'plat_pf_duplicate', 'PostFinance Cash-Konto', 'cash', 'CHF', 'manual', '2026-01-01')"
    )
    conn.commit()

    result = ensure_canonical_cash_accounts(conn)

    assert result["created"] == 0
    assert result["duplicates"] == ["PostFinance Cash-Konto"]
    count = conn.execute("SELECT COUNT(*) FROM accounts WHERE account_name='PostFinance Cash-Konto'").fetchone()[0]
    assert count == 2


def test_manual_cash_snapshot_preview_confirm_audit_and_dashboard_value_for_manual_account():
    conn = db()
    ensure_canonical_cash_accounts(conn)
    account_id = conn.execute("SELECT account_id FROM accounts WHERE account_name='PostFinance Cash-Konto'").fetchone()[0]

    preview = preview_cash_snapshot(conn, CashSnapshotPreviewRequest(account_id=account_id, snapshot_type="manual_balance", balance_date="2026-05-20", amount_chf="1234.56", note="Abgleich"))
    assert preview.amount_chf == "1234.56"
    assert preview.fx_status == "not_needed"

    confirmed = confirm_cash_snapshot(conn, CashSnapshotConfirmRequest(account_id=account_id, snapshot_type="manual_balance", balance_date="2026-05-20", amount_chf="1234.56", note="Abgleich", preview_id=preview.preview_id, confirm=True))

    assert confirmed.status == "confirmed"
    assert confirmed.audit_id.startswith("audit_")
    summary = get_cash_summary(conn)
    row = next(item for item in summary.positions if item.account_label == "PostFinance Cash-Konto")
    assert row.balance_mode == "manual"
    assert row.manual_balance_chf == "1234.56"
    assert row.used_value_chf == "1234.56"
    assert row.calculated_balance_chf == "0.00"
    assert row.difference_chf == "1234.56"
    assert row.last_manual_reconciliation == "2026-05-20"
    replay = confirm_cash_snapshot(conn, CashSnapshotConfirmRequest(account_id=account_id, snapshot_type="manual_balance", balance_date="2026-05-20", amount_chf="1234.56", note="Abgleich", preview_id=preview.preview_id, confirm=True))
    assert replay.entity_id == confirmed.entity_id
    assert conn.execute("SELECT COUNT(*) FROM cash_account_snapshots WHERE account_id=?", (account_id,)).fetchone()[0] == 1


def test_csv_calculated_account_keeps_calculation_as_control_but_newer_manual_snapshot_wins():
    conn = db()
    ensure_canonical_cash_accounts(conn)
    account_id = conn.execute("SELECT account_id FROM accounts WHERE account_name='Raiffeisen'").fetchone()[0]
    confirm_cash_snapshot(conn, CashSnapshotConfirmRequest(account_id=account_id, snapshot_type="csv_anchor_balance", balance_date="2026-05-01", amount_chf="1000.00", note="Start", preview_id="p", confirm=True))
    conn.execute(
        "INSERT INTO transactions(transaction_id, transaction_type, account_id, trade_date, gross_amount_original, net_amount_original, currency_original, fx_rate_to_chf, fx_status, gross_amount_chf, net_amount_chf, source_type, is_confirmed, quality_status, created_at) VALUES ('tx_csv_1', 'csv_import', ?, '2026-05-05', '250.00', '250.00', 'CHF', '1', 'ok', '250.00', '250.00', 'csv_import', 1, 'ok', '2026-05-05')",
        (account_id,),
    )
    confirm_cash_snapshot(conn, CashSnapshotConfirmRequest(account_id=account_id, snapshot_type="manual_balance", balance_date="2026-05-20", amount_chf="1300.00", note="Kontrolle", preview_id="p", confirm=True))

    row = next(item for item in get_cash_summary(conn).positions if item.account_label == "Raiffeisen")

    assert row.balance_mode == "csv_calculated"
    assert row.csv_anchor_balance_chf == "1000.00"
    assert row.calculated_balance_chf == "1250.00"
    assert row.manual_balance_chf == "1300.00"
    assert row.used_value_chf == "1300.00"
    assert row.difference_chf == "50.00"
    assert row.status == "Abgleich offen"


def test_truewealth_manual_value_history_and_position_block():
    conn = db()
    platform_id = "plat_truewealth"
    account_id = "acct_truewealth"
    conn.execute("INSERT INTO platforms(platform_id, name, platform_type, default_currency, created_at) VALUES (?, 'TrueWealth', 'roboadvisor', 'CHF', '2026-01-01')", (platform_id,))
    conn.execute("INSERT INTO accounts(account_id, platform_id, account_name, account_type, currency, created_at) VALUES (?, ?, 'TrueWealth Gesamtwert', 'managed_portfolio', 'CHF', '2026-01-01')", (account_id, platform_id))
    conn.execute("INSERT INTO instruments(instrument_id, asset_class, name, currency, created_at) VALUES ('inst_vti', 'etf', 'Do not allow', 'USD', '2026-01-01')")
    conn.commit()

    preview = preview_account_value(conn, account_id, AccountValuePreviewRequest(valuation_date="2026-05-20", total_value_chf="55555.55", note="Monat"))
    assert preview.amount_chf == "55555.55"
    confirmed = confirm_account_value(conn, account_id, AccountValueConfirmRequest(valuation_date="2026-05-20", total_value_chf="55555.55", note="Monat", preview_id=preview.preview_id, confirm=True))
    assert confirmed.audit_id.startswith("audit_")
    overview = get_overview(conn)
    assert overview.truewealth_value_chf == "55555.55"
    assert overview.total_value_chf == "55555.55"

    with pytest.raises(HTTPException) as exc:
        preview_position(conn, type("Req", (), {"account_id": account_id, "quantity": "1", "currency": "USD", "trade_date": "2026-05-20", "asset_class": "etf", "instrument_id": "inst_vti", "candidate_id": None, "name": None, "ticker": None, "isin": None, "cost_basis_original": "1", "transaction_type": "buy"})())
    assert "Einzelpositionen" in exc.value.detail


def test_portfolio_uses_cash_balance_modes_without_double_counting_truewealth():
    conn = db()
    ensure_canonical_cash_accounts(conn)
    manual_id = conn.execute("SELECT account_id FROM accounts WHERE account_name='AKB gemeinsames Konto'").fetchone()[0]
    csv_id = conn.execute("SELECT account_id FROM accounts WHERE account_name='AKB Haushaltskonto'").fetchone()[0]
    confirm_cash_snapshot(conn, CashSnapshotConfirmRequest(account_id=manual_id, snapshot_type="manual_balance", balance_date="2026-05-20", amount_chf="2000.00", note="", preview_id="p", confirm=True))
    confirm_cash_snapshot(conn, CashSnapshotConfirmRequest(account_id=csv_id, snapshot_type="csv_anchor_balance", balance_date="2026-05-01", amount_chf="3000.00", note="", preview_id="p", confirm=True))

    overview = get_overview(conn)

    assert overview.cash_value_chf == "5000.00"
    assert overview.truewealth_value_chf == "0.00"
    assert Decimal(overview.total_value_chf) >= Decimal("5000.00")
