"""Smoke tests for nerve-email SDK."""
import asyncio

import pytest
from nerve_email import (
    NerveClient,
    NerveAdmin,
    NerveError,
    NerveSessionError,
    NerveAuthError,
    NerveRateLimitError,
    NerveQuotaError,
    NerveSubscriptionError,
)
from nerve_email.tools import get_tool_definitions, NERVE_TOOLS
from nerve_email.types import Thread, Message, EmailAddress, DraftResult, SendResult


class TestImports:
    """Verify all public exports are importable."""

    def test_nerve_client_importable(self):
        assert NerveClient is not None

    def test_nerve_admin_importable(self):
        assert NerveAdmin is not None

    def test_all_exceptions_importable(self):
        for exc_cls in [
            NerveError,
            NerveSessionError,
            NerveAuthError,
            NerveRateLimitError,
            NerveQuotaError,
            NerveSubscriptionError,
        ]:
            assert issubclass(exc_cls, Exception)

    def test_version_exists(self):
        from nerve_email import __version__
        assert __version__ == "0.1.1"


class TestExceptions:
    """Verify exception hierarchy and attributes."""

    def test_base_error_has_code(self):
        err = NerveError("test", code=42)
        assert err.code == 42
        assert str(err) == "test"

    def test_rate_limit_has_retry_after(self):
        err = NerveRateLimitError("slow down", retry_after=5.0)
        assert err.retry_after == 5.0
        assert err.code == -32042

    def test_quota_error_code(self):
        err = NerveQuotaError()
        assert err.code == -32040

    def test_subscription_error_code(self):
        err = NerveSubscriptionError()
        assert err.code == -32041

    def test_all_exceptions_inherit_base(self):
        for cls in [NerveSessionError, NerveAuthError, NerveRateLimitError,
                     NerveQuotaError, NerveSubscriptionError]:
            assert issubclass(cls, NerveError)


class TestToolDefinitions:
    """Verify tool definitions and format adapters."""

    def test_nerve_tools_not_empty(self):
        assert len(NERVE_TOOLS) > 0

    def test_all_tools_have_required_fields(self):
        for name, tool in NERVE_TOOLS.items():
            assert tool.name == name
            assert tool.description
            assert tool.parameters

    def test_claude_format(self):
        tools = get_tool_definitions(format="claude")
        assert len(tools) == len(NERVE_TOOLS)
        for t in tools:
            assert "name" in t
            assert "description" in t
            assert "input_schema" in t

    def test_openai_format(self):
        tools = get_tool_definitions(format="openai")
        for t in tools:
            assert t["type"] == "function"
            assert "function" in t
            assert "name" in t["function"]

    def test_prefix_applied(self):
        tools = get_tool_definitions(format="claude", prefix="email_")
        for t in tools:
            assert t["name"].startswith("email_")

    def test_invalid_format_raises(self):
        with pytest.raises(ValueError, match="Unknown format"):
            get_tool_definitions(format="invalid")


class TestTypes:
    """Verify Pydantic models."""

    def test_thread_model(self):
        t = Thread(id="t1", subject="Test", status="open")
        assert t.id == "t1"

    def test_message_model(self):
        m = Message(id="m1", thread_id="t1")
        assert m.thread_id == "t1"

    def test_email_address_model(self):
        addr = EmailAddress(address="test@example.com", name="Test")
        assert addr.address == "test@example.com"


class TestClientInit:
    """Verify client construction (no network calls)."""

    def test_client_accepts_api_key(self):
        client = NerveClient(base_url="https://example.com", api_key="sk_test")
        assert client.base_url == "https://example.com"

    def test_client_accepts_bearer_token(self):
        client = NerveClient(base_url="https://example.com", bearer_token="tok_test")
        assert client._bearer_token == "tok_test"

    def test_client_strips_trailing_slash(self):
        client = NerveClient(base_url="https://example.com/", api_key="sk")
        assert client.base_url == "https://example.com"

    def test_admin_accepts_api_key(self):
        admin = NerveAdmin(base_url="https://example.com", api_key="admin_key")
        assert admin.base_url == "https://example.com"


class TestTypedToolMethods:
    """Verify typed wrappers match the published tool contract."""

    def test_send_reply_forwards_html_and_idempotency_key(self):
        client = NerveClient(base_url="https://example.com", api_key="sk_test")
        captured = {}

        async def fake_call_tool(name, arguments):
            captured["name"] = name
            captured["arguments"] = arguments
            return {"message_id": "msg_123", "status": "queued"}

        client._call_tool = fake_call_tool

        result = asyncio.run(
            client.send_reply(
                thread_id="thread_123",
                body_or_draft_id="Plain fallback",
                html="<p>Plain fallback</p>",
                needs_human_approval=True,
                idempotency_key="reply_123",
            )
        )

        assert result == {"message_id": "msg_123", "status": "queued"}
        assert captured["name"] == "send_reply"
        assert captured["arguments"] == {
            "thread_id": "thread_123",
            "body_or_draft_id": "Plain fallback",
            "html": "<p>Plain fallback</p>",
            "needs_human_approval": True,
            "idempotency_key": "reply_123",
        }

    def test_compose_email_accepts_html_without_plain_text(self):
        client = NerveClient(base_url="https://example.com", api_key="sk_test")
        captured = {}

        async def fake_call_tool(name, arguments):
            captured["name"] = name
            captured["arguments"] = arguments
            return {"thread_id": "thread_123", "message_id": "msg_123", "status": "sent"}

        client._call_tool = fake_call_tool

        result = asyncio.run(
            client.compose_email(
                inbox_id="inbox_123",
                to="customer@example.com",
                subject="Hello",
                html="<p>Hello</p>",
                idempotency_key="compose_123",
            )
        )

        assert result == {"thread_id": "thread_123", "message_id": "msg_123", "status": "sent"}
        assert captured["name"] == "compose_email"
        assert captured["arguments"] == {
            "inbox_id": "inbox_123",
            "to": "customer@example.com",
            "subject": "Hello",
            "html": "<p>Hello</p>",
            "idempotency_key": "compose_123",
        }

    def test_compose_email_requires_body_or_html(self):
        client = NerveClient(base_url="https://example.com", api_key="sk_test")

        with pytest.raises(ValueError, match="compose_email requires body or html"):
            asyncio.run(
                client.compose_email(
                    inbox_id="inbox_123",
                    to="customer@example.com",
                    subject="Hello",
                )
            )
