"""Pytest configuration for mala tests."""

from __future__ import annotations

import os
from collections.abc import Callable
from typing import TYPE_CHECKING, Any

import pytest

# Type alias for lock releaser function - defined at runtime to support get_type_hints()
LockReleaserFunc = Callable[[list[str]], int]

if TYPE_CHECKING:
    from pathlib import Path

    from src.core.protocols import (
        CodeReviewer,
        GateChecker,
        IssueProvider,
        LogProvider,
        MalaEventSink,
    )
    from src.infra.io.config import MalaConfig
    from src.infra.telemetry import TelemetryProvider
    from src.orchestration.orchestrator import MalaOrchestrator

# Ignore fixture templates under tests/fixtures
collect_ignore_glob = ["fixtures/e2e-fixture/**"]


def pytest_configure(config: pytest.Config) -> None:
    """Configure test environment before collection.

    Sets up environment variables to:
    - Redirect run metadata to /tmp to avoid polluting ~/.config/mala/runs/
    - Redirect Claude SDK logs to /tmp to avoid polluting ~/.claude/projects/
    """
    # Disable debug logging in tests for better test isolation
    # (avoids disk I/O and global logging state changes in each test)
    os.environ["MALA_DISABLE_DEBUG_LOG"] = "1"

    # Redirect run metadata to /tmp to avoid polluting user config
    os.environ["MALA_RUNS_DIR"] = "/tmp/mala-test-runs"

    # Redirect lock directory to /tmp to avoid cross-test interference
    os.environ["MALA_LOCK_DIR"] = "/tmp/mala-test-locks"

    # Redirect Claude SDK logs to /tmp to avoid polluting user Claude config
    os.environ["CLAUDE_CONFIG_DIR"] = "/tmp/mala-test-claude"


def pytest_sessionfinish(session: pytest.Session, exitstatus: int) -> None:
    """Clean up test artifacts after test session completes.

    This runs even when tests are interrupted (Ctrl+C), ensuring lock files
    don't accumulate between test runs.
    """
    import shutil
    from pathlib import Path

    lock_dir = Path("/tmp/mala-test-locks")
    if lock_dir.exists():
        shutil.rmtree(lock_dir, ignore_errors=True)


def pytest_collection_modifyitems(
    config: pytest.Config, items: list[pytest.Item]
) -> None:
    """Apply markers based on test file path."""
    for item in items:
        # Check if test already has an explicit marker (not just package name in keywords)
        if any(item.get_closest_marker(m) for m in ("unit", "integration", "e2e")):
            continue
        # Path-based auto-marking (use item.path for pytest 7+ compatibility)
        # Normalize path separators for cross-platform compatibility (Windows uses backslashes)
        path = str(item.path).replace("\\", "/")
        if "/e2e/" in path:
            item.add_marker(pytest.mark.e2e)
        elif "/integration/" in path:
            item.add_marker(pytest.mark.integration)
        elif "/unit/" in path:
            item.add_marker(pytest.mark.unit)
        else:
            pytest.fail(
                f"Test {item.nodeid} is not in a recognized test category "
                f"(unit/, integration/, e2e/). Move it to the appropriate directory.",
                pytrace=False,
            )


@pytest.fixture
def make_orchestrator() -> Callable[..., MalaOrchestrator]:
    """Factory fixture for creating MalaOrchestrator instances.

    Returns a callable that creates orchestrators using the factory pattern.
    This replaces direct MalaOrchestrator() constructor calls in tests.

    Usage:
        def test_something(make_orchestrator, tmp_path):
            orchestrator = make_orchestrator(
                repo_path=tmp_path,
                max_agents=2,
            )
            ...
    """
    from src.orchestration.factory import OrchestratorConfig, create_orchestrator

    def _make_orchestrator(
        repo_path: Path,
        max_agents: int | None = None,
        timeout_minutes: int | None = None,
        max_issues: int | None = None,
        epic_id: str | None = None,
        only_ids: list[str] | None = None,
        max_gate_retries: int = 3,
        max_review_retries: int = 3,
        disable_validations: set[str] | None = None,
        include_wip: bool = False,
        strict_resume: bool = False,
        focus: bool = True,
        orphans_only: bool = False,
        cli_args: dict[str, Any] | None = None,
        epic_override_ids: set[str] | None = None,
        context_restart_threshold: float = 0.90,
        context_limit: int = 200_000,
        issue_provider: IssueProvider | None = None,
        code_reviewer: CodeReviewer | None = None,
        gate_checker: GateChecker | None = None,
        log_provider: LogProvider | None = None,
        telemetry_provider: TelemetryProvider | None = None,
        event_sink: MalaEventSink | None = None,
        config: MalaConfig | None = None,
        runs_dir: Path | None = None,
        lock_releaser: LockReleaserFunc | None = None,
    ) -> MalaOrchestrator:
        """Create an orchestrator using the factory pattern."""
        from src.orchestration.factory import OrchestratorDependencies

        orch_config = OrchestratorConfig(
            repo_path=repo_path,
            max_agents=max_agents,
            timeout_minutes=timeout_minutes,
            max_issues=max_issues,
            epic_id=epic_id,
            only_ids=only_ids,
            max_gate_retries=max_gate_retries,
            max_review_retries=max_review_retries,
            disable_validations=disable_validations,
            include_wip=include_wip,
            strict_resume=strict_resume,
            focus=focus,
            orphans_only=orphans_only,
            cli_args=cli_args,
            epic_override_ids=epic_override_ids or set(),
            context_restart_threshold=context_restart_threshold,
            context_limit=context_limit,
        )

        deps = OrchestratorDependencies(
            issue_provider=issue_provider,
            code_reviewer=code_reviewer,
            gate_checker=gate_checker,
            log_provider=log_provider,
            telemetry_provider=telemetry_provider,
            event_sink=event_sink,
            runs_dir=runs_dir,
            lock_releaser=lock_releaser,
        )

        return create_orchestrator(orch_config, mala_config=config, deps=deps)

    return _make_orchestrator


@pytest.fixture
def log_provider() -> LogProvider:
    """Provide a FileSystemLogProvider for tests that need log parsing.

    Returns:
        A LogProvider instance for reading session logs from filesystem.
    """
    from src.infra.io.session_log_parser import FileSystemLogProvider

    return FileSystemLogProvider()
