"""Тесты для Users API."""

import pytest
from datetime import date, timedelta
from unittest.mock import AsyncMock, patch
from amnezio import AmnezioClient
from amnezio.models import User, KeyPair, UserConfig
from amnezio.exceptions import NotFoundError, APIError


@pytest.fixture
def client():
    """Создает клиент для тестов."""
    client = AmnezioClient(
        base_url="https://test.com",
        username="admin",
        password="secret"
    )
    client._token = "test_token"
    return client


@pytest.mark.asyncio
async def test_list_users(client, mock_response):
    """Тест получения списка пользователей."""
    with patch.object(client, 'request') as mock_request:
        mock_request.return_value = mock_response(200, {
            "items": [
                {
                    "id": 1,
                    "telegram_id": None,
                    "sub_id": "user1",
                    "status": "active",
                    "public_key": "key1",
                    "preshared_key": None,
                    "assigned_ip": "10.0.0.2",
                    "paid_for": "2026-12-31",
                    "tag": None,
                    "description": None,
                    "profile_id": 1,
                    "last_connected_at": None,
                    "last_connected_node_id": None,
                    "last_connected_node_name": None,
                    "rx_bytes": 0,
                    "tx_bytes": 0,
                }
            ],
            "total": 1,
            "page": 1,
            "per_page": 50,
            "total_pages": 1,
        })
        
        result = await client.users.list()
        
        assert result.total == 1
        assert len(result.items) == 1
        assert isinstance(result.items[0], User)
        assert result.items[0].sub_id == "user1"


@pytest.mark.asyncio
async def test_create_user(client, mock_response):
    """Тест создания пользователя."""
    with patch.object(client, 'request') as mock_request:
        user_data = {
            "id": 1,
            "telegram_id": None,
            "sub_id": "user1",
            "status": "active",
            "public_key": "key1",
            "preshared_key": "psk1",
            "assigned_ip": "10.0.0.2",
            "paid_for": "2026-12-31",
            "tag": "test",
            "description": "Test user",
            "profile_id": 1,
            "last_connected_at": None,
            "last_connected_node_id": None,
            "last_connected_node_name": None,
            "rx_bytes": 0,
            "tx_bytes": 0,
        }
        mock_request.return_value = mock_response(200, user_data)
        
        user = await client.users.create(
            sub_id="user1",
            private_key="private_key",
            profile_id=1,
            paid_for=date(2026, 12, 31),
            tag="test",
            description="Test user"
        )
        
        assert isinstance(user, User)
        assert user.sub_id == "user1"
        assert user.assigned_ip == "10.0.0.2"


@pytest.mark.asyncio
async def test_create_user_sends_zero_telegram_id(client, mock_response):
    """telegram_id=0 не должен отбрасываться из payload."""
    with patch.object(client, 'request') as mock_request:
        user_data = {
            "id": 1,
            "telegram_id": 0,
            "sub_id": "user1",
            "status": "active",
            "public_key": "key1",
            "preshared_key": "psk1",
            "assigned_ip": "10.0.0.2",
            "paid_for": "2026-12-31",
            "tag": "test",
            "description": "Test user",
            "profile_id": 1,
            "last_connected_at": None,
            "last_connected_node_id": None,
            "last_connected_node_name": None,
            "rx_bytes": 0,
            "tx_bytes": 0,
        }
        mock_request.return_value = mock_response(200, user_data)

        user = await client.users.create(
            sub_id="user1",
            private_key="private_key",
            telegram_id=0,
        )

        assert user.telegram_id == 0
        _, kwargs = mock_request.call_args
        assert kwargs["json"]["telegram_id"] == 0


@pytest.mark.asyncio
async def test_get_user(client, mock_response):
    """Тест получения пользователя."""
    with patch.object(client, 'request') as mock_request:
        user_data = {
            "id": 1,
            "telegram_id": None,
            "sub_id": "user1",
            "status": "active",
            "public_key": "key1",
            "preshared_key": None,
            "assigned_ip": "10.0.0.2",
            "paid_for": "2026-12-31",
            "tag": None,
            "description": None,
            "profile_id": 1,
            "last_connected_at": None,
            "last_connected_node_id": None,
            "last_connected_node_name": None,
            "rx_bytes": 0,
            "tx_bytes": 0,
        }
        mock_request.return_value = mock_response(200, user_data)
        
        user = await client.users.get(1)
        
        assert isinstance(user, User)
        assert user.id == 1
        assert user.sub_id == "user1"


@pytest.mark.asyncio
async def test_get_user_not_found(client, mock_response):
    """Тест получения несуществующего пользователя."""
    with patch.object(client, 'request') as mock_request:
        mock_request.return_value = mock_response(404)
        
        with pytest.raises(NotFoundError):
            await client.users.get(999)


@pytest.mark.asyncio
async def test_update_user(client, mock_response):
    """Тест обновления пользователя."""
    with patch.object(client, 'request') as mock_request:
        user_data = {
            "id": 1,
            "telegram_id": None,
            "sub_id": "user1",
            "status": "disabled",
            "public_key": "key1",
            "preshared_key": None,
            "assigned_ip": "10.0.0.2",
            "paid_for": "2027-12-31",
            "tag": None,
            "description": None,
            "profile_id": 1,
            "last_connected_at": None,
            "last_connected_node_id": None,
            "last_connected_node_name": None,
            "rx_bytes": 0,
            "tx_bytes": 0,
        }
        mock_request.return_value = mock_response(200, user_data)
        
        user = await client.users.update(
            user_id=1,
            status="disabled",
            paid_for=date(2027, 12, 31)
        )
        
        assert user.status == "disabled"
        assert str(user.paid_for) == "2027-12-31"


@pytest.mark.asyncio
async def test_delete_user(client, mock_response):
    """Тест удаления пользователя."""
    with patch.object(client, 'request') as mock_request:
        mock_request.return_value = mock_response(200, {"status": "ok"})
        
        result = await client.users.delete(1)
        
        assert result["status"] == "ok"


@pytest.mark.asyncio
async def test_get_config(client, mock_response):
    """Тест получения конфига."""
    with patch.object(client, 'request') as mock_request:
        config_text = "[Interface]\nPrivateKey = test\n[Peer]\nPublicKey = test"
        mock_request.return_value = mock_response(200, {"config": config_text})
        
        config = await client.users.get_config(1)
        
        assert isinstance(config, UserConfig)
        assert "[Interface]" in config.config


@pytest.mark.asyncio
async def test_enable_user(client, mock_response):
    """Тест включения пользователя."""
    with patch.object(client, 'request') as mock_request:
        user_data = {
            "id": 1,
            "telegram_id": None,
            "sub_id": "user1",
            "status": "active",
            "public_key": "key1",
            "preshared_key": None,
            "assigned_ip": "10.0.0.2",
            "paid_for": "2026-12-31",
            "tag": None,
            "description": None,
            "profile_id": 1,
            "last_connected_at": None,
            "last_connected_node_id": None,
            "last_connected_node_name": None,
            "rx_bytes": 0,
            "tx_bytes": 0,
        }
        mock_request.return_value = mock_response(200, user_data)
        
        user = await client.users.enable(1)
        
        assert user.status == "active"


@pytest.mark.asyncio
async def test_disable_user(client, mock_response):
    """Тест отключения пользователя."""
    with patch.object(client, 'request') as mock_request:
        user_data = {
            "id": 1,
            "telegram_id": None,
            "sub_id": "user1",
            "status": "disabled",
            "public_key": "key1",
            "preshared_key": None,
            "assigned_ip": "10.0.0.2",
            "paid_for": "2026-12-31",
            "tag": None,
            "description": None,
            "profile_id": 1,
            "last_connected_at": None,
            "last_connected_node_id": None,
            "last_connected_node_name": None,
            "rx_bytes": 0,
            "tx_bytes": 0,
        }
        mock_request.return_value = mock_response(200, user_data)
        
        user = await client.users.disable(1)
        
        assert user.status == "disabled"


@pytest.mark.asyncio
async def test_renew_subscription(client, mock_response):
    """Тест продления подписки."""
    with patch.object(client, 'request') as mock_request:
        new_date = date.today() + timedelta(days=60)
        user_data = {
            "id": 1,
            "telegram_id": None,
            "sub_id": "user1",
            "status": "active",
            "public_key": "key1",
            "preshared_key": None,
            "assigned_ip": "10.0.0.2",
            "paid_for": new_date.isoformat(),
            "tag": None,
            "description": None,
            "profile_id": 1,
            "last_connected_at": None,
            "last_connected_node_id": None,
            "last_connected_node_name": None,
            "rx_bytes": 0,
            "tx_bytes": 0,
        }
        mock_request.return_value = mock_response(200, user_data)
        
        user = await client.users.renew_subscription(1, new_date)
        
        assert user.status == "active"
        assert user.paid_for == new_date


@pytest.mark.asyncio
async def test_generate_keypair(client, mock_response):
    """Тест генерации пары ключей."""
    with patch.object(client, 'request') as mock_request:
        mock_request.return_value = mock_response(200, {
            "private_key": "private_test",
            "public_key": "public_test",
            "preshared_key": "preshared_test"
        })
        
        keypair = await client.users.generate_keypair()
        
        assert isinstance(keypair, KeyPair)
        assert keypair.private_key == "private_test"
        assert keypair.public_key == "public_test"
        assert keypair.preshared_key == "preshared_test"
