"""Integration tests against a live DropMail instance.

Skipped automatically unless DROPMAIL_TOKEN is set.
Defaults to http://localhost:8080/api/graphql — set DROPMAIL_BASE_URL to override.

To run:
    DROPMAIL_TOKEN=mytoken pytest tests/test_integration.py
"""
from __future__ import annotations

import asyncio
import os
import smtplib
from email.mime.text import MIMEText
from typing import Any

import pytest

from dropmail_mcp.client import DropmailClient
from dropmail_mcp.server import (
    _Q_CREATE_SESSION,
    _Q_GET_INBOX,
    _Q_LIST_DOMAINS,
    _Q_WAIT_FOR_EMAIL,
)

TOKEN = os.environ.get("DROPMAIL_TOKEN")
BASE_URL = os.environ.get("DROPMAIL_BASE_URL", "http://localhost:8080/api/graphql")
SMTP_HOST = os.environ.get("DROPMAIL_SMTP_HOST", "localhost")
SMTP_PORT = int(os.environ.get("DROPMAIL_SMTP_PORT", "2525"))

pytestmark = pytest.mark.skipif(
    not TOKEN, reason="DROPMAIL_TOKEN not set — skipping integration tests"
)


# ---------------------------------------------------------------------------
# Fixtures
# ---------------------------------------------------------------------------


@pytest.fixture
async def client() -> DropmailClient:  # type: ignore[return]
    c = DropmailClient(token=TOKEN, base_url=BASE_URL)  # type: ignore[arg-type]
    yield c
    await c.aclose()


@pytest.fixture
async def session(client: DropmailClient) -> dict[str, Any]:
    """Create a fresh session and return session_id + first address."""
    data, errors = await client.execute(_Q_CREATE_SESSION, {"input": {"withAddress": True}})
    obj = client.unwrap(data, errors, "introduceSession")
    return {
        "session_id": obj["id"],
        "address": obj["addresses"][0]["address"],
    }


# ---------------------------------------------------------------------------
# Tests
# ---------------------------------------------------------------------------


async def test_list_domains(client: DropmailClient) -> None:
    data, errors = await client.execute(_Q_LIST_DOMAINS)
    domains = client.unwrap(data, errors, "domains")
    assert isinstance(domains, list) and len(domains) > 0
    assert all("name" in d and "id" in d for d in domains)


async def test_create_session(session: dict[str, Any]) -> None:
    assert session["session_id"]
    assert "@" in session["address"]


async def test_get_inbox_empty(client: DropmailClient, session: dict[str, Any]) -> None:
    data, errors = await client.execute(
        _Q_GET_INBOX, {"id": session["session_id"], "mailId": None}
    )
    sess = client.unwrap(data, errors, "session")
    assert sess["mailsAfterId"] == []


async def test_wait_for_email(client: DropmailClient, session: dict[str, Any]) -> None:
    """Long-poll for new mail while concurrently sending via SMTP."""

    async def _send_smtp() -> None:
        await asyncio.sleep(1.5)  # give the long-poll request time to reach the server
        msg = MIMEText("Hello from the MCP integration test.")
        msg["From"] = "mcp@dropmail.me"
        msg["To"] = session["address"]
        msg["Subject"] = "MCP integration test"

        def _smtp() -> None:
            with smtplib.SMTP(SMTP_HOST, SMTP_PORT) as smtp:
                smtp.sendmail("mcp@dropmail.me", [session["address"]], msg.as_string())

        await asyncio.to_thread(_smtp)

    send_task = asyncio.create_task(_send_smtp())
    try:
        data, errors = await client.execute(
            _Q_WAIT_FOR_EMAIL,
            {"id": session["session_id"], "mailsAfterId": None},
            timeout=30.0,
        )
        mail = (data or {}).get("sessionMailReceived")
        assert mail is not None, "No email arrived within timeout"
        assert "MCP integration test" in (mail.get("headerSubject") or "")
    finally:
        send_task.cancel()
        try:
            await send_task
        except (asyncio.CancelledError, Exception):
            pass
