"""
Shared fixtures for Confluence unit tests.

This module provides specialized fixtures for testing Confluence-related functionality.
It builds upon the root conftest.py fixtures and integrates with the new test utilities
framework to provide efficient, reusable test fixtures with session-scoped caching.
"""

import os
import sys
from pathlib import Path
from unittest.mock import MagicMock, patch

import pytest

# Add the root tests directory to PYTHONPATH
sys.path.append(str(Path(__file__).parent.parent.parent))

from fixtures.confluence_mocks import (
    MOCK_COMMENTS_RESPONSE,
    MOCK_CQL_SEARCH_RESPONSE,
    MOCK_LABELS_RESPONSE,
    MOCK_PAGE_RESPONSE,
    MOCK_PAGES_FROM_SPACE_RESPONSE,
    MOCK_SPACES_RESPONSE,
)

from mcp_atlassian.confluence.client import ConfluenceClient
from mcp_atlassian.confluence.config import ConfluenceConfig
from mcp_atlassian.utils.oauth import OAuthConfig
from tests.utils.factories import AuthConfigFactory, ConfluencePageFactory
from tests.utils.mocks import MockAtlassianClient, MockPreprocessor

# ============================================================================
# Session-Scoped Confluence Data Fixtures
# ============================================================================


@pytest.fixture(scope="session")
def session_confluence_spaces():
    """
    Session-scoped fixture providing Confluence space definitions.

    This expensive-to-create data is cached for the entire test session
    to improve test performance.

    Returns:
        List[Dict[str, Any]]: Complete Confluence space definitions
    """
    return [
        {
            "id": 12345,
            "key": "TEST",
            "name": "Test Space",
            "type": "global",
            "status": "current",
            "description": {"plain": {"value": "Test space for unit tests"}},
            "_links": {
                "webui": "/spaces/TEST",
                "self": "https://test.atlassian.net/wiki/rest/api/space/TEST",
            },
        },
        {
            "id": 12346,
            "key": "DEMO",
            "name": "Demo Space",
            "type": "global",
            "status": "current",
            "description": {"plain": {"value": "Demo space for testing"}},
            "_links": {
                "webui": "/spaces/DEMO",
                "self": "https://test.atlassian.net/wiki/rest/api/space/DEMO",
            },
        },
        {
            "id": 12347,
            "key": "SAMPLE",
            "name": "Sample Space",
            "type": "personal",
            "status": "current",
            "description": {"plain": {"value": "Sample personal space"}},
            "_links": {
                "webui": "/spaces/SAMPLE",
                "self": "https://test.atlassian.net/wiki/rest/api/space/SAMPLE",
            },
        },
    ]


@pytest.fixture(scope="session")
def session_confluence_content_types():
    """
    Session-scoped fixture providing Confluence content type definitions.

    Returns:
        List[Dict[str, Any]]: Mock Confluence content type data
    """
    return [
        {"name": "page", "type": "content"},
        {"name": "blogpost", "type": "content"},
        {"name": "comment", "type": "content"},
        {"name": "attachment", "type": "content"},
        {"name": "space", "type": "space"},
        {"name": "user", "type": "user"},
    ]


@pytest.fixture(scope="session")
def session_confluence_macros():
    """
    Session-scoped fixture providing Confluence macro definitions.

    Returns:
        List[Dict[str, Any]]: Mock Confluence macro data
    """
    return [
        {"name": "info", "hasBody": True, "bodyType": "rich-text"},
        {"name": "warning", "hasBody": True, "bodyType": "rich-text"},
        {"name": "note", "hasBody": True, "bodyType": "rich-text"},
        {"name": "tip", "hasBody": True, "bodyType": "rich-text"},
        {"name": "code", "hasBody": True, "bodyType": "plain-text"},
        {"name": "toc", "hasBody": False},
        {"name": "children", "hasBody": False},
        {"name": "excerpt", "hasBody": True, "bodyType": "rich-text"},
        {"name": "include", "hasBody": False},
        {"name": "panel", "hasBody": True, "bodyType": "rich-text"},
    ]


# ============================================================================
# Configuration Fixtures
# ============================================================================


@pytest.fixture
def confluence_config_factory():
    """
    Factory for creating ConfluenceConfig instances with customizable options.

    Returns:
        Callable: Function that creates ConfluenceConfig instances

    Example:
        def test_config(confluence_config_factory):
            config = confluence_config_factory(url="https://custom.atlassian.net/wiki")
            assert "custom" in config.url
    """

    def _create_config(**overrides):
        defaults = {
            "url": "https://example.atlassian.net/wiki",
            "auth_type": "basic",
            "username": "test_user",
            "api_token": "test_token",
        }
        config_data = {**defaults, **overrides}
        return ConfluenceConfig(**config_data)

    return _create_config


@pytest.fixture
def mock_config(confluence_config_factory):
    """
    Create a standard mock ConfluenceConfig instance.

    This fixture provides a consistent ConfluenceConfig for tests that don't
    need custom configuration.

    Returns:
        ConfluenceConfig: Standard test configuration
    """
    return confluence_config_factory()


# ============================================================================
# Environment Fixtures
# ============================================================================


@pytest.fixture
def mock_env_vars():
    """
    Mock environment variables for testing.

    Note: This fixture is maintained for backward compatibility.
    Consider using the environment fixtures from root conftest.py.
    """
    with patch.dict(
        "os.environ",
        {
            "CONFLUENCE_URL": "https://example.atlassian.net/wiki",
            "CONFLUENCE_USERNAME": "test_user",
            "CONFLUENCE_API_TOKEN": "test_token",
        },
    ):
        yield


@pytest.fixture
def confluence_auth_environment():
    """
    Fixture providing Confluence-specific authentication environment.

    This sets up environment variables specifically for Confluence authentication
    and can be customized per test.
    """
    auth_config = AuthConfigFactory.create_basic_auth_config()
    confluence_env = {
        "CONFLUENCE_URL": f"{auth_config['url']}/wiki",
        "CONFLUENCE_USERNAME": auth_config["username"],
        "CONFLUENCE_API_TOKEN": auth_config["api_token"],
    }

    with patch.dict(os.environ, confluence_env, clear=False):
        yield confluence_env


# ============================================================================
# Mock Atlassian Client Fixtures
# ============================================================================


@pytest.fixture
def mock_atlassian_confluence(
    session_confluence_spaces, session_confluence_content_types
):
    """
    Enhanced mock of the Atlassian Confluence client.

    This fixture provides a comprehensive mock that uses session-scoped
    data for improved performance and consistency.

    Args:
        session_confluence_spaces: Session-scoped space definitions
        session_confluence_content_types: Session-scoped content type data

    Returns:
        MagicMock: Fully configured mock Confluence client
    """
    with patch("mcp_atlassian.confluence.client.Confluence") as mock:
        confluence_instance = mock.return_value

        # Use original mock data to maintain backward compatibility for existing tests
        confluence_instance.get_all_spaces.return_value = MOCK_SPACES_RESPONSE

        # Set up common return values using both legacy mocks and new factories
        confluence_instance.get_page_by_id.return_value = MOCK_PAGE_RESPONSE
        confluence_instance.get_page_by_title.return_value = MOCK_PAGE_RESPONSE
        confluence_instance.get_all_pages_from_space.return_value = (
            MOCK_PAGES_FROM_SPACE_RESPONSE
        )
        confluence_instance.get_page_comments.return_value = MOCK_COMMENTS_RESPONSE
        confluence_instance.get_page_labels.return_value = MOCK_LABELS_RESPONSE
        confluence_instance.cql.return_value = MOCK_CQL_SEARCH_RESPONSE

        # Enhanced responses using factories
        confluence_instance.create_page.return_value = ConfluencePageFactory.create(
            page_id="123456789", title="New Test Page"
        )

        # Mock update_page to return None (as the actual method does)
        confluence_instance.update_page.return_value = None

        # Mock delete_page to return None
        confluence_instance.delete_page.return_value = None

        # Mock page history
        confluence_instance.get_page_history.return_value = {
            "results": [
                {
                    "version": {"number": 1},
                    "when": "2023-01-01T12:00:00.000Z",
                    "by": {"displayName": "Test User"},
                    "message": "Initial version",
                }
            ]
        }

        # Mock page ancestors
        confluence_instance.get_page_ancestors.return_value = [
            ConfluencePageFactory.create(page_id="parent123", title="Parent Page")
        ]

        # Mock page children
        confluence_instance.get_page_child_by_type.return_value = {
            "results": [
                ConfluencePageFactory.create(page_id="child123", title="Child Page")
            ]
        }

        yield confluence_instance


@pytest.fixture
def enhanced_mock_confluence_client():
    """
    Enhanced mock Confluence client using the new factory system.

    This provides a more flexible mock that can be easily customized
    and integrates with the factory system.

    Returns:
        MagicMock: Enhanced mock Confluence client with factory integration
    """
    return MockAtlassianClient.create_confluence_client()


@pytest.fixture
def mock_atlassian_confluence_with_session_data(
    session_confluence_spaces, session_confluence_content_types
):
    """
    Alternative mock using session-scoped data for new tests.

    This fixture is recommended for new tests as it uses the efficient
    session-scoped data. Existing tests should continue using
    mock_atlassian_confluence for compatibility.

    Args:
        session_confluence_spaces: Session-scoped space definitions
        session_confluence_content_types: Session-scoped content type data

    Returns:
        MagicMock: Mock Confluence client with session-scoped data
    """
    with patch("mcp_atlassian.confluence.client.Confluence") as mock:
        confluence_instance = mock.return_value

        # Use session-scoped data for improved performance
        confluence_instance.get_all_spaces.return_value = {
            "results": session_confluence_spaces,
            "size": len(session_confluence_spaces),
        }

        # Enhanced responses using factories
        confluence_instance.get_page_by_id.return_value = ConfluencePageFactory.create()
        confluence_instance.get_page_by_title.return_value = (
            ConfluencePageFactory.create()
        )
        confluence_instance.create_page.return_value = ConfluencePageFactory.create(
            page_id="123456789", title="New Test Page"
        )

        # Use session data for content types
        confluence_instance.get_content_types.return_value = (
            session_confluence_content_types
        )

        yield confluence_instance


# ============================================================================
# Preprocessor Fixtures
# ============================================================================


@pytest.fixture
def mock_preprocessor():
    """
    Mock the TextPreprocessor with enhanced functionality.

    This fixture provides a preprocessor mock that can be customized
    for testing different content processing scenarios.

    Returns:
        MagicMock: Mock preprocessor with common methods
    """
    preprocessor_instance = MagicMock()

    # Default processing behavior
    preprocessor_instance.process_html_content.return_value = (
        "<p>Processed HTML</p>",
        "Processed Markdown",
    )

    # Additional processing methods
    preprocessor_instance.clean_html.return_value = "<p>Clean HTML</p>"
    preprocessor_instance.html_to_markdown.return_value = "# Markdown Content"
    preprocessor_instance.markdown_to_html.return_value = "<h1>HTML Content</h1>"

    yield preprocessor_instance


@pytest.fixture
def preprocessor_factory():
    """
    Factory for creating preprocessor mocks with different behaviors.

    Returns:
        Dict[str, Callable]: Factory functions for different preprocessor types

    Example:
        def test_preprocessing(preprocessor_factory):
            html_processor = preprocessor_factory["html_to_markdown"]()
            markdown_processor = preprocessor_factory["markdown_to_html"]()
    """
    return {
        "html_to_markdown": MockPreprocessor.create_html_to_markdown,
        "markdown_to_html": MockPreprocessor.create_markdown_to_html,
    }


# ============================================================================
# Client Instance Fixtures
# ============================================================================


@pytest.fixture
def oauth_confluence_client(mock_preprocessor):
    """
    Create a ConfluenceClient instance configured for OAuth authentication.

    This fixture provides a Confluence client configured with OAuth settings
    for testing OAuth-specific functionality.

    Args:
        mock_preprocessor: Mock text preprocessor

    Returns:
        ConfluenceClient: OAuth-configured client instance
    """
    # Create OAuth configuration
    oauth_config = OAuthConfig(
        client_id="test-client-id",
        client_secret="test-client-secret",
        redirect_uri="http://localhost:8080/callback",
        scope="read:confluence-content write:confluence-content",
        cloud_id="test-cloud-id",
    )

    # Convert to ConfluenceConfig format (use .atlassian.net URL to make is_cloud return True)
    config = ConfluenceConfig(
        url="https://test.atlassian.net/wiki",
        auth_type="oauth",
        oauth_config=oauth_config,
    )

    # Mock the OAuth session setup and Confluence client
    with patch(
        "mcp_atlassian.confluence.client.configure_oauth_session"
    ) as mock_oauth_session:
        with patch(
            "mcp_atlassian.confluence.client.Confluence"
        ) as mock_confluence_class:
            with patch(
                "mcp_atlassian.preprocessing.TextPreprocessor"
            ) as mock_text_preprocessor:
                # Mock OAuth session configuration to succeed
                mock_oauth_session.return_value = True

                mock_text_preprocessor.return_value = mock_preprocessor

                # Create the mock Confluence instance
                mock_confluence_instance = MagicMock()
                mock_confluence_class.return_value = mock_confluence_instance

                # Set up OAuth-specific mock responses
                mock_confluence_instance.get_all_spaces.return_value = (
                    MOCK_SPACES_RESPONSE
                )
                mock_confluence_instance.get_page_by_id.return_value = (
                    MOCK_PAGE_RESPONSE
                )
                mock_confluence_instance.create_page.return_value = (
                    ConfluencePageFactory.create(
                        page_id="v2_123456789", title="OAuth Test Page"
                    )
                )

                # Mock the session to have OAuth characteristics
                mock_session = MagicMock()
                mock_confluence_instance._session = mock_session

                # Create the client with OAuth config
                client = ConfluenceClient(config=config)
                client.confluence = mock_confluence_instance
                client.preprocessor = mock_preprocessor

                yield client


@pytest.fixture
def confluence_client(mock_config, mock_atlassian_confluence, mock_preprocessor):
    """
    Create a ConfluenceClient instance with mocked dependencies.

    This fixture provides a fully functional ConfluenceClient with mocked
    Atlassian API calls and content preprocessing for testing.

    Args:
        mock_config: Mock configuration
        mock_atlassian_confluence: Mock Atlassian client
        mock_preprocessor: Mock text preprocessor

    Returns:
        ConfluenceClient: Configured client instance
    """
    # Create a client with a mocked configuration
    with patch(
        "mcp_atlassian.preprocessing.TextPreprocessor"
    ) as mock_text_preprocessor:
        mock_text_preprocessor.return_value = mock_preprocessor

        client = ConfluenceClient(config=mock_config)
        # Replace the actual Confluence instance with our mock
        client.confluence = mock_atlassian_confluence
        # Replace the actual preprocessor with our mock
        client.preprocessor = mock_preprocessor
        yield client


# ============================================================================
# Specialized Test Data Fixtures
# ============================================================================


@pytest.fixture
def make_confluence_page_with_content():
    """
    Factory fixture for creating Confluence pages with rich content.

    Returns:
        Callable: Function that creates page data with content

    Example:
        def test_page_content(make_confluence_page_with_content):
            page = make_confluence_page_with_content(
                title="Rich Page",
                content="<h1>Header</h1><p>Content</p>",
                labels=["test", "content"]
            )
    """

    def _create_page_with_content(
        title: str = "Test Page",
        content: str = "<p>Test content</p>",
        labels: list[str] = None,
        **overrides,
    ):
        labels = labels or ["test"]
        page = ConfluencePageFactory.create(title=title, **overrides)

        # Add rich content
        page["body"]["storage"]["value"] = content

        # Add labels
        page["metadata"] = {
            "labels": {"results": [{"name": label} for label in labels]}
        }

        # Add version info
        page["version"]["message"] = f"Updated {title}"

        return page

    return _create_page_with_content


@pytest.fixture
def make_confluence_search_results():
    """
    Factory fixture for creating Confluence search results.

    Returns:
        Callable: Function that creates CQL search results

    Example:
        def test_search(make_confluence_search_results):
            results = make_confluence_search_results(
                pages=["Page 1", "Page 2"],
                total=2
            )
    """

    def _create_search_results(pages: list[str] = None, total: int = None, **overrides):
        if pages is None:
            pages = ["Test Page 1", "Test Page 2", "Test Page 3"]
        if total is None:
            total = len(pages)

        page_objects = [
            ConfluencePageFactory.create(page_id=str(i), title=title)
            for i, title in enumerate(pages, 1)
        ]

        defaults = {
            "results": page_objects,
            "totalSize": total,
            "start": 0,
            "limit": 25,
        }

        return {**defaults, **overrides}

    return _create_search_results


@pytest.fixture
def make_confluence_space():
    """
    Factory fixture for creating Confluence spaces.

    Returns:
        Callable: Function that creates space data

    Example:
        def test_space(make_confluence_space):
            space = make_confluence_space(
                key="CUSTOM",
                name="Custom Space",
                type="personal"
            )
    """

    def _create_space(
        key: str = "TEST",
        name: str = "Test Space",
        space_type: str = "global",
        **overrides,
    ):
        defaults = {
            "id": 12345,
            "key": key,
            "name": name,
            "type": space_type,
            "status": "current",
            "description": {"plain": {"value": f"{name} for testing"}},
            "_links": {
                "webui": f"/spaces/{key}",
                "self": f"https://test.atlassian.net/wiki/rest/api/space/{key}",
            },
        }

        return {**defaults, **overrides}

    return _create_space


# ============================================================================
# Integration Test Fixtures
# ============================================================================


@pytest.fixture
def confluence_integration_client(session_auth_configs):
    """
    Create a ConfluenceClient for integration testing.

    This fixture creates a client that can be used for integration tests
    when real API credentials are available.

    Args:
        session_auth_configs: Session-scoped auth configurations

    Returns:
        Optional[ConfluenceClient]: Real client if credentials available, None otherwise
    """
    # Check if integration test environment variables are set
    required_vars = ["CONFLUENCE_URL", "CONFLUENCE_USERNAME", "CONFLUENCE_API_TOKEN"]
    if not all(os.environ.get(var) for var in required_vars):
        pytest.skip("Integration test environment variables not set")

    config = ConfluenceConfig(
        url=os.environ["CONFLUENCE_URL"],
        auth_type="basic",
        username=os.environ["CONFLUENCE_USERNAME"],
        api_token=os.environ["CONFLUENCE_API_TOKEN"],
    )

    return ConfluenceClient(config=config)


# ============================================================================
# Parameterized Fixtures
# ============================================================================


@pytest.fixture
def parametrized_confluence_content_type(request):
    """
    Parametrized fixture for testing with different Confluence content types.

    Use with pytest.mark.parametrize to test functionality across
    different content types.

    Example:
        @pytest.mark.parametrize("parametrized_confluence_content_type",
                               ["page", "blogpost"], indirect=True)
        def test_content_types(parametrized_confluence_content_type):
            # Test runs once for each content type
            pass
    """
    content_type = request.param
    return ConfluencePageFactory.create(type=content_type)


@pytest.fixture
def parametrized_confluence_space_type(request):
    """
    Parametrized fixture for testing with different Confluence space types.

    Use with pytest.mark.parametrize to test functionality across
    different space types.
    """
    space_type = request.param
    return {
        "key": "TEST",
        "name": "Test Space",
        "type": space_type,
        "status": "current",
    }
