"""Tests for framework integrations — exercise the tool logic against MockWallet."""

from __future__ import annotations

from unittest.mock import patch

import pytest

from remitmd.models.tab import TabStatus
from remitmd.testing.mock import MockRemit


@pytest.fixture
def mock():
    return MockRemit()


@pytest.fixture
def payer(mock):
    return mock.create_wallet(balance=500.0)


@pytest.fixture
def payee(mock):
    return mock.create_wallet(balance=0.0)


# ─── LangChain ────────────────────────────────────────────────────────────────


class TestLangChainIntegration:
    """Test LangChain tools using MockWallet without importing langchain."""

    def test_langchain_tools_import(self):
        """Import should fail gracefully if langchain not installed."""
        import sys

        # If langchain is not installed, importing should raise ImportError
        with patch.dict(sys.modules, {"langchain_core": None, "langchain_core.tools": None}):
            # Re-import would raise; this just verifies the guard exists
            pass

    @pytest.mark.asyncio
    async def test_pay_direct_tool_async(self, payer, payee):
        """The _arun path transfers funds via MockWallet."""
        result = await payer.pay_direct(payee.address, 10.0, "test")
        assert result.tx_hash is not None
        assert await payee.balance() == 10.0

    @pytest.mark.asyncio
    async def test_create_escrow_tool_async(self, payer, payee):
        from remitmd.models.invoice import Invoice

        invoice = Invoice(to=payee.address, amount=50.0, memo="task")
        tx = await payer.pay(invoice)
        assert tx.invoice_id is not None

    @pytest.mark.asyncio
    async def test_release_escrow_tool_async(self, payer, payee):
        from remitmd.models.invoice import Invoice

        invoice = Invoice(to=payee.address, amount=50.0)
        tx = await payer.pay(invoice)
        await payer.release_escrow(tx.invoice_id)
        assert await payee.balance() == 50.0

    @pytest.mark.asyncio
    async def test_open_tab_tool_async(self, payer, payee):
        tab = await payer.open_tab(payee.address, limit=20.0, per_unit=0.05)
        assert tab.id is not None
        assert tab.status == TabStatus.open

    @pytest.mark.asyncio
    async def test_check_balance_async(self, payer):
        bal = await payer.balance()
        assert bal == 500.0

    @pytest.mark.asyncio
    async def test_get_reputation_async(self, payer):
        rep = await payer.get_reputation(payer.address)
        assert rep.score >= 0.0


# ─── Escrow ───────────────────────────────────────────────────────────────────


class TestEscrowIntegration:
    @pytest.mark.asyncio
    async def test_full_escrow_lifecycle_via_mock(self, payer, payee):
        from remitmd.models.invoice import Invoice

        invoice = Invoice(to=payee.address, amount=100.0, memo="build feature X")
        tx = await payer.pay(invoice)
        assert tx.invoice_id is not None

        await payee.claim_start(tx.invoice_id)
        await payee.submit_evidence(tx.invoice_id, "ipfs://Qmabc123")
        await payer.release_escrow(tx.invoice_id)

        assert await payee.balance() == 100.0
        assert await payer.balance() == 400.0


# ─── Tab metered billing ──────────────────────────────────────────────────────


class TestTabIntegration:
    @pytest.mark.asyncio
    async def test_metered_billing_scenario(self, payer, payee):
        """Simulate an LLM API billing $0.003 per call via a tab."""
        tab = await payer.open_tab(payee.address, limit=1.0, per_unit=0.003)

        # Agent makes 100 API calls
        for _ in range(100):
            await payer.charge_tab(tab.id, units=1)

        await payer.close_tab(tab.id)

        # payee gets 100 * $0.003 = $0.30
        assert await payee.balance() == pytest.approx(0.30, abs=0.001)
        # payer gets back $0.70
        assert await payer.balance() == pytest.approx(499.70, abs=0.001)


# ─── Bounty workflow ──────────────────────────────────────────────────────────


class TestBountyIntegration:
    @pytest.mark.asyncio
    async def test_open_bounty_multiple_submitters(self, mock):
        poster = mock.create_wallet(balance=200.0)
        worker1 = mock.create_wallet(balance=0.0)
        worker2 = mock.create_wallet(balance=0.0)

        bounty = await poster.post_bounty(
            amount=50.0, task="solve puzzle", deadline=mock.now() + 3600
        )
        await worker1.submit_bounty(bounty.id, "ipfs://Qm1")
        await worker2.submit_bounty(bounty.id, "ipfs://Qm2")

        assert len(mock._state.bounties[bounty.id].submissions or []) == 2

        await poster.award_bounty(bounty.id, winner=worker2.address)
        assert await worker2.balance() == 50.0
        assert await worker1.balance() == 0.0
