"""
Copyright (C) 2025 Luciano Guerche

This file is part of rabbitmq-mcp-server.

rabbitmq-mcp-server is free software: you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

rabbitmq-mcp-server is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License
along with rabbitmq-mcp-server. If not, see <https://www.gnu.org/licenses/>.
"""

import sys
from collections.abc import Iterator
from pathlib import Path

import pytest

PROJECT_ROOT = Path(__file__).resolve().parent.parent
SRC_ROOT = PROJECT_ROOT / "src"

# Ensure both the project root and src/ directory are importable without installation.
if str(PROJECT_ROOT) not in sys.path:
    sys.path.insert(0, str(PROJECT_ROOT))
if str(SRC_ROOT) not in sys.path:
    sys.path.insert(0, str(SRC_ROOT))


@pytest.fixture(autouse=True)
def _set_env_defaults(monkeypatch: pytest.MonkeyPatch) -> Iterator[None]:
    """Garante valores padrão de conexão para os testes."""
    env_defaults = {
        "AMQP_HOST": "localhost",
        "AMQP_PORT": "5672",
        "AMQP_USER": "guest",
        "AMQP_PASSWORD": "guest",
        "AMQP_VHOST": "/",
    }
    for key, value in env_defaults.items():
        monkeypatch.setenv(key, value)
    yield


@pytest.fixture
def project_root() -> Path:
    return PROJECT_ROOT


# =============================================================================
# Testcontainers Integration Test Fixtures (Epic 3 Action Item)
# =============================================================================


@pytest.fixture(scope="session")
def docker_available() -> bool:
    """Check if Docker is available for testcontainers."""
    try:
        import docker

        client = docker.from_env()
        client.ping()
        return True
    except Exception:
        return False


@pytest.fixture(scope="session")
def rabbitmq_container_session(docker_available):
    """
    Session-scoped RabbitMQ container for integration tests.

    Provides a real RabbitMQ 3.13 instance with Management Plugin for the entire
    test session. Container is reused across all tests for better performance.

    Usage:
        @pytest.mark.integration
        def test_something(rabbitmq_container_session):
            host = rabbitmq_container_session.get_container_host_ip()
            port = rabbitmq_container_session.get_exposed_port(15672)
            # ... test code ...

    Requirements:
        - Docker must be running
        - testcontainers>=3.7 installed
        - Tests marked with @pytest.mark.integration

    Epic: Epic 3 Action Item - "Setup testcontainers for integration tests"
    Reference: docs-bmad/sprint-artifacts/epic-3-retro-2026-02-12.md
    """
    if not docker_available:
        pytest.skip("Docker not available - required for integration tests")

    try:
        from testcontainers.rabbitmq import RabbitMqContainer
    except ImportError:
        pytest.skip("testcontainers not installed - run: uv sync --all-extras")

    container = RabbitMqContainer("rabbitmq:3.13-management")
    container.with_exposed_ports(5672, 15672)
    container.start()

    # Wait for container to be fully ready
    import time

    time.sleep(5)

    yield container

    container.stop()


@pytest.fixture
def rabbitmq_container(rabbitmq_container_session):
    """
    Function-scoped alias for session container with test isolation.

    Each test gets a clean namespace by using unique queue/exchange names
    or by cleaning up resources in test teardown.

    Usage:
        @pytest.mark.integration
        def test_create_queue(rabbitmq_container):
            # Use unique names: f"test-queue-{uuid.uuid4()}"
            # Or cleanup in teardown
            pass
    """
    yield rabbitmq_container_session


@pytest.fixture
def rabbitmq_settings(rabbitmq_container):
    """
    Create Settings object configured for testcontainer.

    Returns pre-configured Settings instance pointing to the RabbitMQ
    testcontainer with correct host, port, and credentials.

    Usage:
        @pytest.mark.integration
        def test_with_settings(rabbitmq_settings):
            operation = ListQueuesOperation(http_client_manager, rabbitmq_settings)
            # ... test code ...
    """
    from config.settings import Settings

    host = rabbitmq_container.get_container_host_ip()
    management_port = int(rabbitmq_container.get_exposed_port(15672))
    amqp_port = int(rabbitmq_container.get_exposed_port(5672))

    return Settings(
        host=host,
        port=management_port,
        user="guest",
        password="guest",
        vhost="/",
        use_tls=False,
        amqp_port=amqp_port,
        crud_timeout=10,
        list_timeout=30,
    )


@pytest.fixture
async def http_client_manager(rabbitmq_container):
    """
    Create HTTPClientManager connected to testcontainer.

    Provides an HTTP client manager for testing topology operations
    against real RabbitMQ Management API.

    Usage:
        @pytest.mark.asyncio
        @pytest.mark.integration
        async def test_http_operations(http_client_manager):
            response = await http_client_manager.get("/api/queues")
            # ... test code ...
    """
    from rabbitmq_mcp_connection.http_client import HTTPClientManager

    host = rabbitmq_container.get_container_host_ip()
    port = int(rabbitmq_container.get_exposed_port(15672))

    manager = HTTPClientManager(
        host=host,
        port=port,
        username="guest",
        password="guest",
        use_tls=False,
        timeout=30,
        pool_size=5,
    )

    yield manager

    await manager.close()


@pytest.fixture
async def amqp_connection(rabbitmq_container):
    """
    Create AMQP connection to testcontainer.

    Provides a connection manager for testing messaging operations
    against real RabbitMQ AMQP protocol.

    Usage:
        @pytest.mark.asyncio
        @pytest.mark.integration
        async def test_amqp_operations(amqp_connection):
            channel = await amqp_connection.get_channel()
            # ... test code ...
    """
    from rabbitmq_mcp_connection.connection.manager import ConnectionManager
    from rabbitmq_mcp_connection.schemas.connection import ConnectionConfig

    host = rabbitmq_container.get_container_host_ip()
    port = int(rabbitmq_container.get_exposed_port(5672))

    config = ConnectionConfig(
        host=host,
        port=port,
        user="guest",
        password="guest",
        vhost="/",
        timeout=30,
        heartbeat=60,
    )

    manager = ConnectionManager(config)
    await manager.connect()

    yield manager

    await manager.disconnect()
