"""Tests for Stripe Connect endpoints."""

import pytest
from httpx import Response

from growflow_billing import GrowFlowBillingClient
from growflow_billing.exceptions import NotFoundError, ValidationError
from growflow_billing.models.connect import (
    ConnectAccount,
    ConnectAccountLink,
    ConnectAccountsPage,
    ConnectBalance,
    ConnectLoginLink,
    ConnectTransfer,
    ConnectTransfersPage,
)


# =============================================================================
# Connect Accounts
# =============================================================================


@pytest.mark.asyncio
async def test_list_connect_accounts(client: GrowFlowBillingClient, mock_api):
    """Test listing Connect accounts."""
    mock_api.get("/admin/accounts").mock(
        return_value=Response(
            200,
            json={
                "data": [
                    {
                        "id": "acct_001",
                        "email": "vendor1@example.com",
                        "country": "IT",
                        "business_type": "individual",
                        "charges_enabled": True,
                        "payouts_enabled": True,
                        "details_submitted": True,
                        "created_at": "2024-01-10T00:00:00Z",
                        "metadata": {},
                    },
                    {
                        "id": "acct_002",
                        "email": "vendor2@example.com",
                        "country": "DE",
                        "business_type": "company",
                        "charges_enabled": False,
                        "payouts_enabled": False,
                        "details_submitted": False,
                        "created_at": "2024-01-15T00:00:00Z",
                        "metadata": {"tier": "premium"},
                    },
                ],
                "has_more": True,
            },
        )
    )

    page = await client.list_connect_accounts(limit=2)
    assert isinstance(page, ConnectAccountsPage)
    assert len(page.data) == 2
    assert page.has_more is True
    assert page.data[0].id == "acct_001"
    assert page.data[0].email == "vendor1@example.com"
    assert page.data[1].country == "DE"


@pytest.mark.asyncio
async def test_list_connect_accounts_as_list(client: GrowFlowBillingClient, mock_api):
    """Test listing Connect accounts when API returns a plain list."""
    mock_api.get("/admin/accounts").mock(
        return_value=Response(
            200,
            json=[
                {
                    "id": "acct_001",
                    "email": "vendor1@example.com",
                    "country": "IT",
                    "business_type": "individual",
                    "charges_enabled": True,
                    "payouts_enabled": True,
                    "details_submitted": True,
                    "metadata": {},
                },
            ],
        )
    )

    page = await client.list_connect_accounts()
    assert len(page.data) == 1
    assert page.has_more is False


@pytest.mark.asyncio
async def test_list_connect_accounts_with_pagination(client: GrowFlowBillingClient, mock_api):
    """Test listing Connect accounts with starting_after cursor."""
    mock_api.get("/admin/accounts").mock(
        return_value=Response(
            200,
            json={"data": [], "has_more": False},
        )
    )

    page = await client.list_connect_accounts(limit=50, starting_after="acct_001")
    assert len(page.data) == 0
    assert page.has_more is False


@pytest.mark.asyncio
async def test_get_connect_account(client: GrowFlowBillingClient, mock_api):
    """Test getting a single Connect account."""
    mock_api.get("/admin/accounts/acct_001").mock(
        return_value=Response(
            200,
            json={
                "id": "acct_001",
                "email": "vendor@example.com",
                "country": "IT",
                "business_type": "individual",
                "charges_enabled": True,
                "payouts_enabled": True,
                "details_submitted": True,
                "created_at": "2024-01-10T00:00:00Z",
                "metadata": {"store_id": "store_123"},
            },
        )
    )

    account = await client.get_connect_account("acct_001")
    assert isinstance(account, ConnectAccount)
    assert account.id == "acct_001"
    assert account.email == "vendor@example.com"
    assert account.metadata == {"store_id": "store_123"}


@pytest.mark.asyncio
async def test_get_connect_account_not_found(client: GrowFlowBillingClient, mock_api):
    """Test getting non-existent Connect account raises NotFoundError."""
    mock_api.get("/admin/accounts/acct_nonexistent").mock(
        return_value=Response(404, json={"detail": "Account not found"})
    )

    with pytest.raises(NotFoundError):
        await client.get_connect_account("acct_nonexistent")


@pytest.mark.asyncio
async def test_create_connect_account(client: GrowFlowBillingClient, mock_api):
    """Test creating a Connect account."""
    mock_api.post("/admin/accounts").mock(
        return_value=Response(
            200,
            json={
                "id": "acct_new",
                "email": "new_vendor@example.com",
                "country": "IT",
                "business_type": "individual",
                "charges_enabled": False,
                "payouts_enabled": False,
                "details_submitted": False,
                "created_at": "2024-02-01T00:00:00Z",
                "metadata": {},
            },
        )
    )

    account = await client.create_connect_account(
        email="new_vendor@example.com",
        country="IT",
    )
    assert isinstance(account, ConnectAccount)
    assert account.id == "acct_new"
    assert account.email == "new_vendor@example.com"
    assert account.charges_enabled is False


@pytest.mark.asyncio
async def test_create_connect_account_with_metadata(client: GrowFlowBillingClient, mock_api):
    """Test creating a Connect account with metadata."""
    mock_api.post("/admin/accounts").mock(
        return_value=Response(
            200,
            json={
                "id": "acct_meta",
                "email": "meta@example.com",
                "country": "DE",
                "business_type": "company",
                "charges_enabled": False,
                "payouts_enabled": False,
                "details_submitted": False,
                "metadata": {"ref": "partner_abc"},
            },
        )
    )

    account = await client.create_connect_account(
        email="meta@example.com",
        country="DE",
        business_type="company",
        metadata={"ref": "partner_abc"},
    )
    assert account.country == "DE"
    assert account.business_type == "company"
    assert account.metadata == {"ref": "partner_abc"}


# =============================================================================
# Onboarding & Login Links
# =============================================================================


@pytest.mark.asyncio
async def test_create_onboarding_link(client: GrowFlowBillingClient, mock_api):
    """Test creating an onboarding link."""
    mock_api.post("/admin/accounts/acct_001/onboarding-link").mock(
        return_value=Response(
            200,
            json={
                "url": "https://connect.stripe.com/setup/e/acct_001/xxx",
                "expires_at": "2024-02-01T01:00:00Z",
            },
        )
    )

    link = await client.create_onboarding_link(
        account_id="acct_001",
        refresh_url="https://app.com/onboarding/refresh",
        return_url="https://app.com/onboarding/complete",
    )
    assert isinstance(link, ConnectAccountLink)
    assert link.url.startswith("https://connect.stripe.com")
    assert link.expires_at is not None


@pytest.mark.asyncio
async def test_create_login_link(client: GrowFlowBillingClient, mock_api):
    """Test creating a login link for Express dashboard."""
    mock_api.post("/admin/accounts/acct_001/login-link").mock(
        return_value=Response(
            200,
            json={
                "url": "https://connect.stripe.com/express/acct_001/login",
                "created": "2024-02-01T00:00:00Z",
            },
        )
    )

    link = await client.create_login_link("acct_001")
    assert isinstance(link, ConnectLoginLink)
    assert link.url.startswith("https://connect.stripe.com")


# =============================================================================
# Balance
# =============================================================================


@pytest.mark.asyncio
async def test_get_connect_account_balance(client: GrowFlowBillingClient, mock_api):
    """Test getting Connect account balance."""
    mock_api.get("/admin/accounts/acct_001/balance").mock(
        return_value=Response(
            200,
            json={
                "available": [{"amount": 50000, "currency": "eur"}],
                "pending": [{"amount": 12000, "currency": "eur"}],
            },
        )
    )

    balance = await client.get_connect_account_balance("acct_001")
    assert isinstance(balance, ConnectBalance)
    assert len(balance.available) == 1
    assert balance.available[0]["amount"] == 50000
    assert balance.available[0]["currency"] == "eur"
    assert len(balance.pending) == 1
    assert balance.pending[0]["amount"] == 12000


# =============================================================================
# Transfers
# =============================================================================


@pytest.mark.asyncio
async def test_create_transfer(client: GrowFlowBillingClient, mock_api):
    """Test creating a transfer."""
    mock_api.post("/admin/accounts/acct_001/transfers").mock(
        return_value=Response(
            200,
            json={
                "id": "tr_001",
                "amount": 5000,
                "currency": "eur",
                "destination": "acct_001",
                "description": "Monthly payout",
                "created_at": "2024-02-01T00:00:00Z",
            },
        )
    )

    transfer = await client.create_transfer(
        account_id="acct_001",
        amount_cents=5000,
        currency="eur",
        description="Monthly payout",
    )
    assert isinstance(transfer, ConnectTransfer)
    assert transfer.id == "tr_001"
    assert transfer.amount == 5000
    assert transfer.destination == "acct_001"
    assert transfer.description == "Monthly payout"


@pytest.mark.asyncio
async def test_create_transfer_with_metadata(client: GrowFlowBillingClient, mock_api):
    """Test creating a transfer with metadata."""
    mock_api.post("/admin/accounts/acct_001/transfers").mock(
        return_value=Response(
            200,
            json={
                "id": "tr_meta",
                "amount": 2500,
                "currency": "eur",
                "destination": "acct_001",
                "created_at": "2024-02-01T00:00:00Z",
            },
        )
    )

    transfer = await client.create_transfer(
        account_id="acct_001",
        amount_cents=2500,
        metadata={"order_id": "ord_123"},
    )
    assert transfer.id == "tr_meta"
    assert transfer.amount == 2500


@pytest.mark.asyncio
async def test_create_transfer_validation_error(client: GrowFlowBillingClient, mock_api):
    """Test transfer with invalid amount returns validation error from API."""
    mock_api.post("/admin/accounts/acct_001/transfers").mock(
        return_value=Response(422, json={"detail": "Amount must be greater than 0"})
    )

    with pytest.raises(ValidationError):
        await client.create_transfer(
            account_id="acct_001",
            amount_cents=0,
        )


@pytest.mark.asyncio
async def test_list_transfers(client: GrowFlowBillingClient, mock_api):
    """Test listing transfers for an account."""
    mock_api.get("/admin/accounts/acct_001/transfers").mock(
        return_value=Response(
            200,
            json={
                "data": [
                    {
                        "id": "tr_001",
                        "amount": 5000,
                        "currency": "eur",
                        "destination": "acct_001",
                        "description": "January payout",
                        "created_at": "2024-01-31T00:00:00Z",
                    },
                    {
                        "id": "tr_002",
                        "amount": 7500,
                        "currency": "eur",
                        "destination": "acct_001",
                        "description": "February payout",
                        "created_at": "2024-02-28T00:00:00Z",
                    },
                ],
                "has_more": False,
            },
        )
    )

    page = await client.list_transfers("acct_001")
    assert isinstance(page, ConnectTransfersPage)
    assert len(page.data) == 2
    assert page.has_more is False
    assert page.data[0].id == "tr_001"
    assert page.data[1].amount == 7500


@pytest.mark.asyncio
async def test_list_transfers_as_list(client: GrowFlowBillingClient, mock_api):
    """Test listing transfers when API returns a plain list."""
    mock_api.get("/admin/accounts/acct_001/transfers").mock(
        return_value=Response(
            200,
            json=[
                {
                    "id": "tr_001",
                    "amount": 5000,
                    "currency": "eur",
                    "destination": "acct_001",
                },
            ],
        )
    )

    page = await client.list_transfers("acct_001")
    assert len(page.data) == 1
    assert page.has_more is False


# =============================================================================
# Model properties
# =============================================================================


def test_connect_account_is_fully_onboarded():
    """Test ConnectAccount.is_fully_onboarded property."""
    onboarded = ConnectAccount(
        id="acct_001",
        email="test@example.com",
        charges_enabled=True,
        payouts_enabled=True,
        details_submitted=True,
    )
    assert onboarded.is_fully_onboarded is True

    partial = ConnectAccount(
        id="acct_002",
        email="test@example.com",
        charges_enabled=True,
        payouts_enabled=False,
        details_submitted=True,
    )
    assert partial.is_fully_onboarded is False

    new_account = ConnectAccount(
        id="acct_003",
        email="test@example.com",
    )
    assert new_account.is_fully_onboarded is False


def test_connect_transfer_amount_decimal():
    """Test ConnectTransfer.amount_decimal property."""
    transfer = ConnectTransfer(
        id="tr_001",
        amount=5000,
        currency="eur",
        destination="acct_001",
    )
    assert transfer.amount_decimal == 50.00

    small = ConnectTransfer(
        id="tr_002",
        amount=99,
        currency="eur",
        destination="acct_001",
    )
    assert small.amount_decimal == 0.99


def test_connect_accounts_page_defaults():
    """Test ConnectAccountsPage default values."""
    empty_page = ConnectAccountsPage()
    assert empty_page.data == []
    assert empty_page.has_more is False


def test_connect_transfers_page_defaults():
    """Test ConnectTransfersPage default values."""
    empty_page = ConnectTransfersPage()
    assert empty_page.data == []
    assert empty_page.has_more is False


def test_connect_balance_defaults():
    """Test ConnectBalance default values."""
    empty_balance = ConnectBalance()
    assert empty_balance.available == []
    assert empty_balance.pending == []
