"""Integration tests — pentesting agent end-to-end with mock adapter + mock LLM."""

from __future__ import annotations

from typing import Any
from unittest.mock import AsyncMock, MagicMock

import pytest

from tests.conftest import MockAgentAdapter
from ziran.domain.entities.pentest import (
    PentestStatus,
)


def _mock_llm_client(
    responses: list[str] | None = None,
) -> MagicMock:
    """Create a mock LLM client that cycles through configured responses."""
    client = MagicMock()
    default_responses = responses or [
        '{"tool": "run_scan", "args": {}}',
        "The scan is complete. Session complete — sufficient findings discovered.",
        "# Final Report\n\nNo critical vulnerabilities found.",
    ]

    def _make_response(text: str) -> MagicMock:
        resp = MagicMock()
        resp.content = text
        resp.prompt_tokens = 10
        resp.completion_tokens = 5
        resp.total_tokens = 15
        return resp

    # Infinite cycling via a wrapper
    import itertools

    cycle = itertools.cycle(default_responses)

    async def _complete(*args: Any, **kwargs: Any) -> MagicMock:
        return _make_response(next(cycle))

    client.complete = _complete
    return client


@pytest.mark.integration
class TestPentestCampaign:
    """End-to-end pentesting agent tests with mock adapter and mock LLM."""

    async def test_autonomous_session_completes(self) -> None:
        """Full autonomous session should complete and produce results."""
        from ziran.application.pentesting.orchestrator import PentestOrchestrator

        adapter = MockAgentAdapter(
            responses=["I cannot help with that request."],
            capabilities=[],
        )
        llm = _mock_llm_client()

        orchestrator = PentestOrchestrator(
            adapter=adapter,
            llm_client=llm,
            max_iterations=2,
        )
        # Mock deduplication to avoid embedding calls
        orchestrator.deduplicator.deduplicate = AsyncMock(return_value=[])

        session = await orchestrator.run_autonomous(
            goal="Find prompt injection vulnerabilities",
        )

        assert session.status == PentestStatus.COMPLETE
        assert session.plan is not None
        assert session.plan.goal == "Find prompt injection vulnerabilities"
        assert session.iteration_count >= 1
        assert len(session.steps) >= 1

    async def test_interactive_session_with_directive(self) -> None:
        """Interactive session should accept directives and respond."""
        from ziran.application.pentesting.orchestrator import PentestOrchestrator

        adapter = MockAgentAdapter(
            responses=["I cannot help with that request."],
            capabilities=[],
        )

        # Provide enough responses for: start_interactive (plan+exec+reason+report)
        # then send_directive (plan+exec+reason+report)
        responses = [
            '{"tool": "run_scan", "args": {}}',
            "Session complete.",
            "# Report: Initial scan done",
        ] * 4
        llm = _mock_llm_client(responses=responses)

        orchestrator = PentestOrchestrator(
            adapter=adapter,
            llm_client=llm,
            max_iterations=2,
        )

        session = await orchestrator.start_interactive(
            goal="Red team the agent",
        )
        assert session is not None
        assert orchestrator.get_session(session.session_id) is not None

        # Send a directive
        updated = await orchestrator.send_directive(
            session.session_id,
            "Focus on data exfiltration",
        )
        assert updated is not None
        assert len(updated.conversation_history) >= 1

    async def test_session_export_produces_files(self) -> None:
        """Report export should create JSON and Markdown files."""
        import tempfile

        from ziran.application.pentesting.orchestrator import PentestOrchestrator

        adapter = MockAgentAdapter(
            responses=["I cannot help with that request."],
            capabilities=[],
        )
        llm = _mock_llm_client()

        orchestrator = PentestOrchestrator(
            adapter=adapter,
            llm_client=llm,
            max_iterations=1,
        )
        orchestrator.deduplicator.deduplicate = AsyncMock(return_value=[])

        session = await orchestrator.run_autonomous(goal="Test export")

        with tempfile.TemporaryDirectory() as tmpdir:
            paths = await orchestrator.export_report(session, output_dir=tmpdir)
            assert len(paths) == 3  # json + md + html
            for p in paths:
                assert p.exists()
                assert p.stat().st_size > 0
