"""Tests for Slack MCP server client."""

import pytest
from unittest.mock import AsyncMock, patch, MagicMock
from slack_mcp_server.client import SlackClient, SlackAuthError, SlackNotFoundError, SlackRateLimitError


@pytest.fixture
def client():
    return SlackClient(bot_token="xoxb-test-token", user_token="xoxp-test-user")


class TestClientInit:
    def test_requires_bot_token(self):
        with pytest.raises(SlackAuthError):
            SlackClient(bot_token="")

    def test_creates_with_valid_token(self):
        c = SlackClient(bot_token="xoxb-valid")
        assert c.bot_token == "xoxb-valid"

    def test_user_token_falls_back_to_bot(self):
        c = SlackClient(bot_token="xoxb-valid")
        assert c.user_token == "xoxb-valid"


class TestPostMessage:
    @pytest.mark.asyncio
    async def test_post_message_success(self, client, mock_slack_response):
        resp = mock_slack_response(data={"channel": "C123", "ts": "1234.5678"})
        with patch.object(client._bot_client, "post", return_value=resp):
            result = await client.post_message(channel="C123", text="Hello")
            assert result["channel"] == "C123"
            assert result["ts"] == "1234.5678"


class TestGetMessages:
    @pytest.mark.asyncio
    async def test_get_messages_success(self, client, mock_slack_response):
        resp = mock_slack_response(data={
            "messages": [
                {"user": "U1", "text": "Hi", "ts": "1234"},
                {"user": "U2", "text": "Hello", "ts": "1235"},
            ]
        })
        with patch.object(client._bot_client, "get", return_value=resp):
            result = await client.get_messages(channel="C123", limit=10)
            assert len(result) == 2
            assert result[0]["user"] == "U1"


class TestListChannels:
    @pytest.mark.asyncio
    async def test_list_channels_success(self, client, mock_slack_response):
        resp = mock_slack_response(data={
            "channels": [
                {"id": "C1", "name": "general", "topic": {"value": ""}, "purpose": {"value": ""}, "num_members": 10, "is_private": False},
            ]
        })
        with patch.object(client._bot_client, "get", return_value=resp):
            result = await client.list_channels()
            assert len(result) == 1
            assert result[0]["name"] == "general"


class TestErrorHandling:
    @pytest.mark.asyncio
    async def test_auth_error(self, client, mock_slack_response):
        resp = mock_slack_response(error="invalid_auth")
        with patch.object(client._bot_client, "post", return_value=resp):
            with pytest.raises(SlackAuthError):
                await client.post_message(channel="C123", text="test")

    @pytest.mark.asyncio
    async def test_not_found_error(self, client, mock_slack_response):
        resp = mock_slack_response(error="channel_not_found")
        with patch.object(client._bot_client, "get", return_value=resp):
            with pytest.raises(SlackNotFoundError):
                await client.get_messages(channel="INVALID")

    @pytest.mark.asyncio
    async def test_rate_limit_single_attempt(self, client):
        resp = AsyncMock()
        resp.status_code = 429
        resp.headers = {"Retry-After": "1"}
        resp.json = lambda: {"ok": False, "error": "ratelimited"}
        resp_ok = AsyncMock()
        resp_ok.status_code = 200
        resp_ok.json = lambda: {"ok": True, "channel": "C1", "ts": "1"}
        resp_ok.headers = {}
        with patch.object(client._bot_client, "post", side_effect=[resp, resp_ok]):
            result = await client.post_message(channel="C1", text="test")
            assert result["channel"] == "C1"


class TestConfigValidation:
    def test_valid_config(self, mock_config):
        errors = mock_config.validate()
        assert len(errors) == 0

    def test_missing_bot_token(self):
        from slack_mcp_server.config import SlackConfig
        import os
        with patch.dict(os.environ, {}, clear=True):
            config = SlackConfig()
            errors = config.validate()
            assert any("SLACK_BOT_TOKEN" in e for e in errors)

    def test_invalid_bot_token_prefix(self):
        from slack_mcp_server.config import SlackConfig
        import os
        with patch.dict(os.environ, {"SLACK_BOT_TOKEN": "bad-token"}, clear=True):
            config = SlackConfig()
            errors = config.validate()
            assert any("xoxb-" in e for e in errors)
