"""
Global test configuration and fixtures for MarkiTect project.

Provides shared fixtures, utilities, and configuration for all test types.
"""

import pytest
import tempfile
import shutil
import asyncio
from pathlib import Path
from unittest.mock import Mock, AsyncMock
from typing import Generator, Dict, Any
import sqlite3
import os

# Import async test utilities
from tests.utils.assertions import cleanup_async_mocks, create_async_mock_that_returns


# Note: event_loop fixture is now handled by pytest-asyncio with asyncio_mode=auto
# This replaces the manual event loop management for better async test support


@pytest.fixture(scope="session")
def test_workspace() -> Generator[Path, None, None]:
    """Create isolated test workspace for file operations."""
    temp_dir = tempfile.mkdtemp(prefix="markitect_test_")
    workspace_path = Path(temp_dir)

    # Create subdirectories
    (workspace_path / "documents").mkdir()
    (workspace_path / "cache").mkdir()
    (workspace_path / "workspaces").mkdir()

    yield workspace_path

    # Cleanup
    shutil.rmtree(temp_dir, ignore_errors=True)


@pytest.fixture
def test_database_path(test_workspace) -> Path:
    """Provide path for test database."""
    return test_workspace / "test.db"


@pytest.fixture
def mock_database():
    """Provide mocked database for testing."""
    mock_db = Mock()
    mock_cursor = Mock()
    mock_db.cursor.return_value = mock_cursor
    mock_db.execute.return_value = mock_cursor
    mock_cursor.fetchone.return_value = None
    mock_cursor.fetchall.return_value = []
    mock_cursor.lastrowid = 1
    return mock_db


@pytest.fixture
def mock_http_client():
    """Provide mocked HTTP client for API tests."""
    mock_client = Mock()
    mock_response = Mock()
    mock_response.status_code = 200
    mock_response.json.return_value = {"status": "success"}
    mock_response.text = '{"status": "success"}'
    mock_client.get.return_value = mock_response
    mock_client.post.return_value = mock_response
    mock_client.put.return_value = mock_response
    mock_client.delete.return_value = mock_response
    return mock_client


@pytest.fixture
def mock_async_http_client():
    """Provide mocked async HTTP client for API tests."""
    mock_client = AsyncMock()
    mock_response = AsyncMock()
    mock_response.status = 200
    mock_response.json = AsyncMock(return_value={"status": "success"})
    mock_response.text = AsyncMock(return_value='{"status": "success"}')
    mock_client.get.return_value = mock_response
    mock_client.post.return_value = mock_response
    mock_client.put.return_value = mock_response
    mock_client.delete.return_value = mock_response
    return mock_client


@pytest.fixture
def test_config(test_workspace) -> Dict[str, Any]:
    """Provide test configuration dictionary."""
    return {
        "workspace_dir": str(test_workspace / "workspaces"),
        "database_path": str(test_workspace / "test.db"),
        "cache_dir": str(test_workspace / "cache"),
        "gitea_url": "http://test-gitea.com",
        "gitea_token": "test-token",
        "repo_owner": "test",
        "repo_name": "repo",
        "log_level": "DEBUG"
    }


@pytest.fixture
def clean_environment():
    """Provide clean environment variables for testing."""
    original_env = dict(os.environ)

    # Clear relevant environment variables
    test_env_vars = [
        "MARKITECT_WORKSPACE_DIR",
        "MARKITECT_GITEA_URL",
        "MARKITECT_GITEA_TOKEN",
        "MARKITECT_REPO_OWNER",
        "MARKITECT_REPO_NAME"
    ]

    for var in test_env_vars:
        os.environ.pop(var, None)

    yield

    # Restore original environment
    os.environ.clear()
    os.environ.update(original_env)


@pytest.fixture
def isolated_environment(test_workspace, clean_environment):
    """Set up isolated environment for CLI testing."""
    env = {
        "MARKITECT_WORKSPACE_DIR": str(test_workspace / "workspaces"),
        "MARKITECT_GITEA_URL": "http://test-gitea.com",
        "MARKITECT_GITEA_TOKEN": "test-token",
        "MARKITECT_REPO_OWNER": "test",
        "MARKITECT_REPO_NAME": "repo",
        "PYTHONPATH": "."
    }

    # Update current process environment
    for key, value in env.items():
        os.environ[key] = value

    yield env


@pytest.fixture
def sample_markdown_content():
    """Provide sample markdown content for testing."""
    return """---
title: Test Document
author: Test Author
tags: [test, sample]
---

# Test Document

This is a test document with **bold** and *italic* text.

## Section 1

- Item 1
- Item 2
- Item 3

## Section 2

Here's a code block:

```python
def hello_world():
    print("Hello, World!")
```

And a link: [Test Link](https://example.com)
"""


@pytest.fixture
def sample_issue_data():
    """Provide sample issue data for testing."""
    return {
        "number": 123,
        "title": "Test Issue",
        "body": "This is a test issue description",
        "state": "open",
        "labels": [
            {"name": "bug"},
            {"name": "priority:high"},
            {"name": "status:in-progress"}
        ],
        "milestone": {
            "id": 1,
            "title": "Version 1.0",
            "description": "First release"
        },
        "created_at": "2025-01-01T00:00:00Z",
        "updated_at": "2025-01-01T12:00:00Z"
    }


@pytest.fixture
def sample_project_data():
    """Provide sample project data for testing."""
    return {
        "name": "Test Project",
        "description": "A test project for testing",
        "state": "active",
        "milestones": [
            {
                "id": 1,
                "title": "Version 1.0",
                "description": "First release",
                "due_date": "2025-12-31T23:59:59Z",
                "state": "open",
                "open_issues": 5,
                "closed_issues": 3
            }
        ],
        "kanban_columns": ["Todo", "In Progress", "Review", "Done"],
        "created_at": "2024-01-01T00:00:00Z",
        "updated_at": "2025-01-01T00:00:00Z"
    }


# Performance testing fixtures
@pytest.fixture
def performance_timer():
    """Timer fixture for performance testing."""
    import time

    class Timer:
        def __init__(self):
            self.start_time = None
            self.end_time = None

        def start(self):
            self.start_time = time.time()

        def stop(self):
            self.end_time = time.time()

        @property
        def elapsed(self) -> float:
            if self.start_time is None:
                raise ValueError("Timer not started")
            if self.end_time is None:
                return time.time() - self.start_time
            return self.end_time - self.start_time

    return Timer()


# Async test helpers
@pytest.fixture
def async_test_timeout():
    """Default timeout for async tests."""
    return 30.0  # 30 seconds


@pytest.fixture
def async_cleanup():
    """Fixture to help with async test cleanup and prevent coroutine warnings."""
    mocks_to_cleanup = []

    def register_mock(mock):
        """Register a mock for cleanup."""
        mocks_to_cleanup.append(mock)
        return mock

    yield register_mock

    # Cleanup all registered mocks
    cleanup_async_mocks(*mocks_to_cleanup)


@pytest.fixture
def async_mock_client(async_cleanup):
    """Provide a properly configured async HTTP client mock."""
    mock_client = AsyncMock()
    mock_response = AsyncMock()
    mock_response.status = 200
    mock_response.json = create_async_mock_that_returns({"status": "success"})
    mock_response.text = create_async_mock_that_returns('{"status": "success"}')

    # Configure the mock client methods
    mock_client.get = create_async_mock_that_returns(mock_response)
    mock_client.post = create_async_mock_that_returns(mock_response)
    mock_client.put = create_async_mock_that_returns(mock_response)
    mock_client.delete = create_async_mock_that_returns(mock_response)

    # Register for cleanup
    async_cleanup(mock_client)
    async_cleanup(mock_response)

    return mock_client


# Test markers configuration
def pytest_configure(config):
    """Configure pytest markers."""
    config.addinivalue_line(
        "markers", "slow: marks tests as slow (deselect with '-m \"not slow\"')"
    )
    config.addinivalue_line(
        "markers", "integration: marks tests as integration tests"
    )
    config.addinivalue_line(
        "markers", "e2e: marks tests as end-to-end tests"
    )
    config.addinivalue_line(
        "markers", "performance: marks tests as performance tests"
    )
    config.addinivalue_line(
        "markers", "unit: marks tests as unit tests"
    )


# Collection hooks
def pytest_collection_modifyitems(config, items):
    """Modify test collection to add markers based on test location."""
    for item in items:
        # Add markers based on test file location
        if "unit" in str(item.fspath):
            item.add_marker(pytest.mark.unit)
        elif "integration" in str(item.fspath):
            item.add_marker(pytest.mark.integration)
        elif "e2e" in str(item.fspath):
            item.add_marker(pytest.mark.e2e)
        elif "performance" in str(item.fspath):
            item.add_marker(pytest.mark.performance)