"""Tests for Mailbox and MailAdmin."""

import pytest
import respx
import httpx

from primitif import Primitif, Mailbox, AuthError
from primitif.mail import (
    MailError,
    MailAuthError,
    MailNetworkError,
    MailboxInfo,
    SendResult,
    InboxMessage,
)
from primitif._models import PaginatedList


MAIL = "https://api.mail.primitif.ai"


# ── Mailbox (token auth) ──


@respx.mock
def test_mailbox_info():
    respx.get(f"{MAIL}/v1/me").mock(
        return_value=httpx.Response(200, json={
            "id": "mb_abc", "address": "test@mail.primitif.ai",
            "name": "test", "created_at": "2025-01-01T00:00:00Z",
        })
    )
    mb = Mailbox(token="mb_tok_test123456789012345678901234")
    info = mb.info()
    assert isinstance(info, MailboxInfo)
    assert info.address == "test@mail.primitif.ai"
    mb.close()


@respx.mock
def test_mailbox_address_property():
    respx.get(f"{MAIL}/v1/me").mock(
        return_value=httpx.Response(200, json={
            "id": "mb_abc", "address": "test@mail.primitif.ai",
            "name": "test", "created_at": "2025-01-01T00:00:00Z",
        })
    )
    mb = Mailbox(token="mb_tok_test123456789012345678901234")
    assert mb.address == "test@mail.primitif.ai"
    mb.close()


@respx.mock
def test_mailbox_send():
    respx.post(f"{MAIL}/v1/me/send").mock(
        return_value=httpx.Response(200, json={
            "thread_id": "th_abc", "message_id": "msg_abc", "status": "sent",
        })
    )
    mb = Mailbox(token="mb_tok_test123456789012345678901234")
    result = mb.send(to="user@test.com", subject="Hi", body_text="Hello")
    assert isinstance(result, SendResult)
    assert result.status == "sent"
    mb.close()


@respx.mock
def test_mailbox_send_with_approval():
    respx.post(f"{MAIL}/v1/me/send").mock(
        return_value=httpx.Response(200, json={
            "thread_id": "th_abc", "status": "held",
            "held_email_id": "he_123", "approval_url": "https://example.com/approve",
        })
    )
    mb = Mailbox(token="mb_tok_test123456789012345678901234")
    result = mb.send(to="ceo@client.com", subject="Contract", body_text="...", require_approval=True)
    assert result.status == "held"
    assert result.held_email_id == "he_123"
    mb.close()


@respx.mock
def test_mailbox_inbox():
    respx.get(f"{MAIL}/v1/me/inbox").mock(
        return_value=httpx.Response(200, json={
            "items": [
                {"id": "msg_1", "thread_id": "th_1", "from_address": "a@b.com",
                 "subject": "Hi", "snippet": "Hello", "is_read": False,
                 "created_at": "2025-01-01T00:00:00Z"},
            ],
            "has_more": False,
        })
    )
    mb = Mailbox(token="mb_tok_test123456789012345678901234")
    inbox = mb.inbox()
    assert isinstance(inbox, PaginatedList)
    assert len(inbox) == 1
    assert inbox.items[0].from_address == "a@b.com"
    mb.close()


@respx.mock
def test_mailbox_401():
    respx.get(f"{MAIL}/v1/me").mock(
        return_value=httpx.Response(401, json={"error": "unauthorized", "message": "Bad token"})
    )
    mb = Mailbox(token="mb_tok_test123456789012345678901234")
    with pytest.raises(MailAuthError):
        mb.info()
    mb.close()


@respx.mock
def test_mailbox_context_manager():
    respx.get(f"{MAIL}/v1/me").mock(
        return_value=httpx.Response(200, json={
            "id": "mb_abc", "address": "test@mail.primitif.ai",
            "name": "test", "created_at": "2025-01-01T00:00:00Z",
        })
    )
    with Mailbox(token="mb_tok_test123456789012345678901234") as mb:
        info = mb.info()
        assert info.id == "mb_abc"


def test_mailbox_no_token_raises():
    with pytest.raises(MailError, match="No token"):
        Mailbox()


def test_mailbox_empty_token_raises():
    with pytest.raises(MailError, match="No token"):
        Mailbox(token="")


def test_mailbox_whitespace_token_raises():
    with pytest.raises(MailError, match="No token"):
        Mailbox(token="   ")


def test_mailbox_token_from_constructor():
    mb = Mailbox(token="mb_tok_test123456789012345678901234")
    assert mb.token == "mb_tok_test123456789012345678901234"
    mb.close()


@respx.mock
def test_mailbox_token_from_create():
    respx.post(f"{MAIL}/v1/mailboxes").mock(
        return_value=httpx.Response(200, json={
            "id": "mb_new", "address": "agent@mail.primitif.ai",
            "token": "mb_tok_plaintext123", "name": "agent",
        })
    )
    p = Primitif(api_key="pk_live_test12345678901234567890123456")
    mb = p.mail.create_mailbox(name="agent")
    assert mb.token == "mb_tok_plaintext123"
    mb.close()
    p.close()


# ── MailAdmin (via Primitif) ──


@respx.mock
def test_create_mailbox_returns_mailbox():
    respx.post(f"{MAIL}/v1/mailboxes").mock(
        return_value=httpx.Response(200, json={
            "id": "mb_new", "address": "agent@mail.primitif.ai",
            "token": "mb_tok_plaintext123", "name": "agent",
        })
    )
    p = Primitif(api_key="pk_live_test12345678901234567890123456")
    mb = p.mail.create_mailbox(name="agent", ttl=3600)
    assert isinstance(mb, Mailbox)
    assert mb.address == "agent@mail.primitif.ai"
    mb.close()
    p.close()


@respx.mock
def test_list_mailboxes():
    respx.get(f"{MAIL}/v1/mailboxes").mock(
        return_value=httpx.Response(200, json={
            "items": [
                {"id": "mb_1", "address": "a@mail.primitif.ai", "name": "a",
                 "created_at": "2025-01-01T00:00:00Z", "received_count": 5, "sent_count": 3},
            ],
            "has_more": False,
        })
    )
    p = Primitif(api_key="pk_live_test12345678901234567890123456")
    mailboxes = p.mail.list_mailboxes()
    assert len(mailboxes) == 1
    assert mailboxes.items[0].address == "a@mail.primitif.ai"
    p.close()


def test_diamond_inheritance():
    """MailAuthError is catchable as both MailError and AuthError."""
    err = MailAuthError(message="test", status_code=401)
    assert isinstance(err, MailError)
    assert isinstance(err, AuthError)


def test_primitif_no_key_raises():
    from primitif._exceptions import PrimitifError
    with pytest.raises(PrimitifError, match="No API key"):
        Primitif()


@respx.mock
def test_mailbox_delete():
    """delete() calls DELETE /v1/me and marks mailbox as destroyed."""
    respx.delete(f"{MAIL}/v1/me").mock(
        return_value=httpx.Response(204)
    )
    mb = Mailbox(token="mb_tok_test123456789012345678901234")
    mb.delete()
    with pytest.raises(MailError, match="destroyed"):
        mb.send(to="a@b.com", subject="x", body_text="y")


@respx.mock
def test_mailbox_delete_failure_does_not_brick():
    """If DELETE fails, the mailbox should still be usable."""
    respx.delete(f"{MAIL}/v1/me").mock(
        return_value=httpx.Response(500, json={"error": "internal", "message": "Oops"})
    )
    respx.get(f"{MAIL}/v1/me").mock(
        return_value=httpx.Response(200, json={
            "id": "mb_abc", "address": "test@mail.primitif.ai",
            "name": "test", "created_at": "2025-01-01T00:00:00Z",
        })
    )
    from primitif.mail import MailServerError
    mb = Mailbox(token="mb_tok_test123456789012345678901234")
    with pytest.raises(MailServerError):
        mb.delete()
    # mailbox should still work
    info = mb.info()
    assert info.address == "test@mail.primitif.ai"
    mb.close()


@respx.mock
def test_download_attachment_wraps_transport_error():
    """download_attachment wraps httpx transport errors in MailNetworkError."""
    respx.get(f"{MAIL}/v1/me/messages/msg_1/attachments/att_1").mock(
        side_effect=httpx.ReadTimeout("timed out")
    )
    mb = Mailbox(token="mb_tok_test123456789012345678901234", max_retries=0)
    with pytest.raises(MailNetworkError, match="ReadTimeout"):
        mb.download_attachment("msg_1", "att_1")
    mb.close()


@respx.mock
def test_download_attachment_retries_connect_error():
    """Transient connect errors are retried in download_attachment."""
    route = respx.get(f"{MAIL}/v1/me/messages/msg_1/attachments/att_1")
    route.side_effect = [
        httpx.ConnectError("connection refused"),
        httpx.Response(200, content=b"file-data"),
    ]
    mb = Mailbox(token="mb_tok_test123456789012345678901234", max_retries=1)
    data = mb.download_attachment("msg_1", "att_1")
    assert data == b"file-data"
    assert route.call_count == 2
    mb.close()


@respx.mock
def test_auto_paging_iter():
    """auto_paging_iter fetches all pages."""
    page1 = respx.get(f"{MAIL}/v1/me/inbox").mock(
        return_value=httpx.Response(200, json={
            "items": [
                {"id": "msg_1", "thread_id": "th_1", "from_address": "a@b.com",
                 "subject": "Page 1", "snippet": "Hello", "is_read": False,
                 "created_at": "2025-01-01T00:00:00Z"},
            ],
            "has_more": True,
            "cursor": "cur_abc",
        })
    )
    mb = Mailbox(token="mb_tok_test123456789012345678901234")
    inbox = mb.inbox()
    assert inbox.has_more is True

    # Now mock the second page (with cursor param)
    page1.side_effect = [
        httpx.Response(200, json={
            "items": [
                {"id": "msg_2", "thread_id": "th_2", "from_address": "c@d.com",
                 "subject": "Page 2", "snippet": "World", "is_read": True,
                 "created_at": "2025-01-02T00:00:00Z"},
            ],
            "has_more": False,
        }),
    ]

    all_msgs = list(inbox.auto_paging_iter())
    assert len(all_msgs) == 2
    assert all_msgs[0].id == "msg_1"
    assert all_msgs[1].id == "msg_2"
    mb.close()


@respx.mock
def test_list_requests_empty_items():
    """list_requests with empty items list should return empty PaginatedList."""
    APPROVAL = "https://api.approval.primitif.ai"
    respx.get(f"{APPROVAL}/v1/requests").mock(
        return_value=httpx.Response(200, json={
            "items": [],
            "has_more": False,
        })
    )
    p = Primitif(api_key="pk_live_test12345678901234567890123456")
    requests = p.approval.list_requests()
    assert len(requests) == 0
    p.close()
