"""Tests for the receive_sms skill."""

import os
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

from fliiq.runtime.package_data import bundled_skills_dir
from fliiq.runtime.skills.base import SkillBase

SKILLS_DIR = str(bundled_skills_dir())


def _load_skill() -> SkillBase:
    return SkillBase(os.path.join(SKILLS_DIR, "receive_sms"))


def test_receive_sms_schema():
    skill = _load_skill()
    schema = skill.schema()
    assert schema["name"] == "receive_sms"
    assert "since" in schema["parameters"]["properties"]


async def test_receive_sms_missing_account_sid(monkeypatch):
    monkeypatch.delenv("TWILIO_ACCOUNT_SID", raising=False)
    monkeypatch.delenv("TWILIO_AUTH_TOKEN", raising=False)
    monkeypatch.delenv("TWILIO_PHONE_NUMBER", raising=False)
    skill = _load_skill()
    with pytest.raises(ValueError, match="TWILIO_ACCOUNT_SID"):
        await skill.execute({})


async def test_receive_sms_missing_auth_token(monkeypatch):
    monkeypatch.setenv("TWILIO_ACCOUNT_SID", "ACfake")
    monkeypatch.delenv("TWILIO_AUTH_TOKEN", raising=False)
    monkeypatch.delenv("TWILIO_PHONE_NUMBER", raising=False)
    skill = _load_skill()
    with pytest.raises(ValueError, match="TWILIO_AUTH_TOKEN"):
        await skill.execute({})


async def test_receive_sms_missing_phone_number(monkeypatch):
    monkeypatch.setenv("TWILIO_ACCOUNT_SID", "ACfake")
    monkeypatch.setenv("TWILIO_AUTH_TOKEN", "fake-token")
    monkeypatch.delenv("TWILIO_PHONE_NUMBER", raising=False)
    skill = _load_skill()
    with pytest.raises(ValueError, match="TWILIO_PHONE_NUMBER"):
        await skill.execute({})


async def test_receive_sms_invalid_since(monkeypatch):
    monkeypatch.setenv("TWILIO_ACCOUNT_SID", "ACfake")
    monkeypatch.setenv("TWILIO_AUTH_TOKEN", "fake-token")
    monkeypatch.setenv("TWILIO_PHONE_NUMBER", "+15005550006")
    skill = _load_skill()
    with pytest.raises(ValueError, match="Invalid 'since' datetime"):
        await skill.execute({"since": "not-a-date"})


async def test_receive_sms_success(monkeypatch):
    monkeypatch.setenv("TWILIO_ACCOUNT_SID", "ACfake")
    monkeypatch.setenv("TWILIO_AUTH_TOKEN", "fake-token")
    monkeypatch.setenv("TWILIO_PHONE_NUMBER", "+15005550006")
    skill = _load_skill()

    mock_response = MagicMock()
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "messages": [
            {
                "sid": "SMabc123",
                "from": "+14155551234",
                "body": "Hello Fliiq",
                "date_sent": "2025-02-10T12:00:00Z",
                "direction": "inbound",
            },
            {
                "sid": "SMdef456",
                "from": "+14155555678",
                "body": "Another message",
                "date_sent": "2025-02-10T12:05:00Z",
                "direction": "inbound",
            },
        ]
    }

    with patch("httpx.AsyncClient") as mock_client_cls:
        mock_client = AsyncMock()
        mock_client.get.return_value = mock_response
        mock_client_cls.return_value.__aenter__ = AsyncMock(return_value=mock_client)
        mock_client_cls.return_value.__aexit__ = AsyncMock(return_value=False)
        result = await skill.execute({"since": "2025-02-10T00:00:00Z"})

    assert len(result["messages"]) == 2
    assert result["messages"][0]["sid"] == "SMabc123"
    assert result["messages"][0]["from"] == "+14155551234"
    assert "Hello Fliiq" in result["messages"][0]["body"]
    assert "<external_message" in result["messages"][0]["body"]
    assert result["has_more"] is False


async def test_receive_sms_filters_outbound(monkeypatch):
    monkeypatch.setenv("TWILIO_ACCOUNT_SID", "ACfake")
    monkeypatch.setenv("TWILIO_AUTH_TOKEN", "fake-token")
    monkeypatch.setenv("TWILIO_PHONE_NUMBER", "+15005550006")
    skill = _load_skill()

    mock_response = MagicMock()
    mock_response.status_code = 200
    mock_response.json.return_value = {
        "messages": [
            {
                "sid": "SMin1",
                "from": "+14155551234",
                "body": "Inbound msg",
                "date_sent": "2025-02-10T12:00:00Z",
                "direction": "inbound",
            },
            {
                "sid": "SMout1",
                "from": "+15005550006",
                "body": "Outbound msg",
                "date_sent": "2025-02-10T12:01:00Z",
                "direction": "outbound-api",
            },
        ]
    }

    with patch("httpx.AsyncClient") as mock_client_cls:
        mock_client = AsyncMock()
        mock_client.get.return_value = mock_response
        mock_client_cls.return_value.__aenter__ = AsyncMock(return_value=mock_client)
        mock_client_cls.return_value.__aexit__ = AsyncMock(return_value=False)
        result = await skill.execute({})

    assert len(result["messages"]) == 1
    assert result["messages"][0]["sid"] == "SMin1"


async def test_receive_sms_default_since(monkeypatch):
    monkeypatch.setenv("TWILIO_ACCOUNT_SID", "ACfake")
    monkeypatch.setenv("TWILIO_AUTH_TOKEN", "fake-token")
    monkeypatch.setenv("TWILIO_PHONE_NUMBER", "+15005550006")
    skill = _load_skill()

    mock_response = MagicMock()
    mock_response.status_code = 200
    mock_response.json.return_value = {"messages": []}

    with patch("httpx.AsyncClient") as mock_client_cls:
        mock_client = AsyncMock()
        mock_client.get.return_value = mock_response
        mock_client_cls.return_value.__aenter__ = AsyncMock(return_value=mock_client)
        mock_client_cls.return_value.__aexit__ = AsyncMock(return_value=False)
        result = await skill.execute({})

    assert result["messages"] == []
    assert result["has_more"] is False
    # Verify the GET was called (default since applied internally)
    mock_client.get.assert_called_once()


async def test_receive_sms_api_error(monkeypatch):
    monkeypatch.setenv("TWILIO_ACCOUNT_SID", "ACfake")
    monkeypatch.setenv("TWILIO_AUTH_TOKEN", "fake-token")
    monkeypatch.setenv("TWILIO_PHONE_NUMBER", "+15005550006")
    skill = _load_skill()

    mock_response = MagicMock()
    mock_response.status_code = 401
    mock_response.json.return_value = {"message": "Authentication required"}
    mock_response.text = "Authentication required"

    with patch("httpx.AsyncClient") as mock_client_cls:
        mock_client = AsyncMock()
        mock_client.get.return_value = mock_response
        mock_client_cls.return_value.__aenter__ = AsyncMock(return_value=mock_client)
        mock_client_cls.return_value.__aexit__ = AsyncMock(return_value=False)
        with pytest.raises(ValueError, match="Twilio API error"):
            await skill.execute({})
