"""
End-to-End Tests for Dynamic Agent/Team Execution

Tests the complete flow from API request to worker execution.
"""

import pytest
import asyncio
import uuid
import os
from datetime import datetime, timezone
from unittest.mock import Mock, patch, AsyncMock
from dotenv import load_dotenv

# Load production environment variables
load_dotenv('.env.local')

# Set DATABASE_URL from Supabase URL (fix postgres:// -> postgresql://)
# Use non-pooling URL to avoid connection issues in tests
db_url = os.getenv('SUPABASE_POSTGRES_URL_NON_POOLING', os.getenv('SUPABASE_POSTGRES_URL', ''))
if db_url.startswith('postgres://'):
    db_url = db_url.replace('postgres://', 'postgresql://', 1)
os.environ['DATABASE_URL'] = db_url

from control_plane_api.app.schemas.execution_schemas import (
    DynamicEntityDefinition,
    DynamicExecutionRequest,
    QueueResolutionRequest,
)
from control_plane_api.app.services.dynamic_execution_service import dynamic_execution_service
from control_plane_api.app.services.queue_resolution_service import queue_resolution_service
from control_plane_api.app.workflows.dynamic_agent_execution import (
    DynamicAgentExecutionWorkflow,
    DynamicAgentExecutionInput,
)


class TestQueueResolution:
    """Test smart queue resolution using pattern matching and LLM"""

    @pytest.mark.asyncio
    async def test_resolve_queue_with_k8s_task(self, db_session):
        """Test that kubernetes tasks resolve to queues with k8s capabilities"""
        from control_plane_api.app.models.worker import WorkerQueue

        # Get existing worker queues from database
        queues = db_session.query(WorkerQueue).filter(
            WorkerQueue.organization_id == "kubiya-ai",
            WorkerQueue.status == "active"
        ).all()

        if not queues:
            pytest.skip("No active worker queues found for testing")

        # Test pattern-based resolution (no LLM call needed)
        result = await queue_resolution_service.resolve_queue(
            db=db_session,
            prompt="Deploy kubernetes cluster with 3 nodes using kubectl",
            organization_id="kubiya-ai",
            preferred_runtime="claude_code",
        )

        # Verify result structure
        assert "worker_queue_id" in result
        assert "worker_queue_name" in result
        assert "reasoning" in result
        assert "confidence" in result
        assert result["confidence"] in ["high", "medium", "low"]

    @pytest.mark.asyncio
    async def test_resolve_queue_with_generic_task(self, db_session):
        """Test that generic tasks resolve to appropriate queue"""
        from control_plane_api.app.models.worker import WorkerQueue

        # Get existing worker queues
        queues = db_session.query(WorkerQueue).filter(
            WorkerQueue.organization_id == "kubiya-ai",
            WorkerQueue.status == "active"
        ).all()

        if not queues:
            pytest.skip("No active worker queues found for testing")

        # Test with generic task
        result = await queue_resolution_service.resolve_queue(
            db=db_session,
            prompt="Analyze security vulnerabilities in the codebase",
            organization_id="kubiya-ai",
            preferred_runtime="claude_code",
        )

        # Verify result structure
        assert "worker_queue_id" in result
        assert "worker_queue_name" in result
        assert result["confidence"] in ["high", "medium", "low"]


class TestDynamicAgentExecution:
    """Test dynamic agent execution without creating entities"""

    @pytest.mark.asyncio
    async def test_create_dynamic_agent_execution(self, db_session):
        """Test creating a dynamic agent execution"""
        # Get an existing worker queue from the production database
        from control_plane_api.app.models.worker import WorkerQueue

        worker_queue = db_session.query(WorkerQueue).filter(
            WorkerQueue.organization_id == "kubiya-ai",
            WorkerQueue.status == "active"
        ).first()

        if not worker_queue:
            pytest.skip("No active worker queue found in database for testing")

        # Mock Temporal client
        with patch('control_plane_api.app.services.dynamic_execution_service.get_temporal_client') as mock_temporal:
            mock_workflow = AsyncMock()
            mock_workflow.id = f"dynamic-execution-{uuid.uuid4()}"
            mock_temporal.return_value.start_workflow = AsyncMock(return_value=mock_workflow)

            # Test
            entity_definition = {
                "entity_type": "agent",
                "name": "Security Scanner",
                "system_prompt": "You are a security expert",
                "model_id": "kubiya/claude-sonnet-4",
                "runtime": "claude_code",
                "skill_ids": ["file_system", "security_scan"],
                "mcp_servers": {},
                "subagents": None,
            }

            user_metadata = {
                "user_id": "user_123",
                "user_email": "test@example.com",
                "user_name": "Test User",
            }

            result = await dynamic_execution_service.create_and_execute(
                db=db_session,
                prompt="Scan for security vulnerabilities",
                entity_definition=entity_definition,
                organization_id="kubiya-ai",
                user_metadata=user_metadata,
                control_plane_url="http://localhost:8000",
                api_key="test_key",
                worker_queue_id=worker_queue.id,
                meta_agent_session_id="session_456",
            )

            # Verify
            assert result["execution_id"]
            assert result["workflow_id"]
            assert result["status"] == "PENDING"
            assert result["entity_id"]  # Ephemeral UUID

            # Verify execution record
            from control_plane_api.app.models.execution import Execution
            execution = db_session.query(Execution).filter(
                Execution.id == result["execution_id"]
            ).first()

            assert execution is not None
            assert execution.execution_metadata["is_dynamic_execution"] is True
            assert execution.execution_metadata["dynamic_entity_definition"]["name"] == "Security Scanner"
            assert execution.execution_metadata["meta_agent_session_id"] == "session_456"

            # Verify entity NOT in agents table
            from control_plane_api.app.models.agent import Agent
            agent = db_session.query(Agent).filter(
                Agent.id == result["entity_id"]
            ).first()
            assert agent is None  # ✅ No entity created!

    @pytest.mark.asyncio
    async def test_create_dynamic_team_execution(self, db_session):
        """Test creating a dynamic team execution with subagents"""
        # Get an existing worker queue from the production database
        from control_plane_api.app.models.worker import WorkerQueue

        worker_queue = db_session.query(WorkerQueue).filter(
            WorkerQueue.organization_id == "kubiya-ai",
            WorkerQueue.status == "active"
        ).first()

        if not worker_queue:
            pytest.skip("No active worker queue found in database for testing")

        with patch('control_plane_api.app.services.dynamic_execution_service.get_temporal_client') as mock_temporal:
            mock_workflow = AsyncMock()
            mock_workflow.id = f"dynamic-execution-{uuid.uuid4()}"
            mock_temporal.return_value.start_workflow = AsyncMock(return_value=mock_workflow)

            # Test with team + subagents
            entity_definition = {
                "entity_type": "team",
                "name": "Code Review Team",
                "system_prompt": "Coordinate code review process",
                "runtime": "claude_code",
                "subagents": [
                    {
                        "entity_type": "agent",
                        "name": "Security Reviewer",
                        "system_prompt": "Review for security issues",
                        "skill_ids": ["security_scan"],
                    },
                    {
                        "entity_type": "agent",
                        "name": "Style Checker",
                        "system_prompt": "Check code style",
                        "skill_ids": ["linter"],
                    },
                ],
            }

            result = await dynamic_execution_service.create_and_execute(
                db=db_session,
                prompt="Review this pull request",
                entity_definition=entity_definition,
                organization_id="kubiya-ai",
                user_metadata={"user_id": "user_123"},
                control_plane_url="http://localhost:8000",
                api_key="test_key",
                worker_queue_id=worker_queue.id,
            )

            # Verify
            assert result["execution_id"]

            # Verify execution metadata has subagents
            from control_plane_api.app.models.execution import Execution
            execution = db_session.query(Execution).filter(
                Execution.id == result["execution_id"]
            ).first()

            assert execution.execution_type == "TEAM"
            entity_def = execution.execution_metadata["dynamic_entity_definition"]
            assert len(entity_def["subagents"]) == 2
            assert entity_def["subagents"][0]["name"] == "Security Reviewer"

            # Verify team NOT in teams table
            from control_plane_api.app.models.team import Team
            team = db_session.query(Team).filter(
                Team.id == result["entity_id"]
            ).first()
            assert team is None  # ✅ No team entity created!


class TestWorkflowExecution:
    """Test the dedicated dynamic agent execution workflow"""

    @pytest.mark.asyncio
    async def test_workflow_uses_inline_config(self):
        """Verify workflow uses inline config, not database queries"""

        workflow_input = DynamicAgentExecutionInput(
            execution_id=str(uuid.uuid4()),
            entity_id=str(uuid.uuid4()),  # Ephemeral UUID
            entity_type="agent",
            organization_id="org_123",
            prompt="Test task",
            system_prompt="You are a test agent",
            model_id="kubiya/claude-sonnet-4",
            model_config={"temperature": 0.7},
            agent_config={},
            mcp_servers={"filesystem": {"command": "npx", "args": ["-y", "@modelcontextprotocol/server-filesystem"]}},
            skill_ids=["file_system"],
            user_metadata={"user_id": "user_123"},
            runtime_type="claude_code",
            control_plane_url="http://localhost:8000",
            api_key="test_key",
        )

        # Mock the execute_agent_llm activity
        with patch('control_plane_api.app.workflows.dynamic_agent_execution.execute_agent_llm') as mock_execute:
            # Mock successful execution
            mock_execute.return_value = {
                "success": True,
                "response": "Task completed successfully",
                "usage": {"total_tokens": 100},
            }

            # Mock update_execution_status activity
            with patch('control_plane_api.app.workflows.dynamic_agent_execution.update_execution_status'):
                # Create workflow instance
                workflow = DynamicAgentExecutionWorkflow()

                # Note: This is a simplified test - in real Temporal environment,
                # you'd use the Temporal test framework

                # Verify the workflow would pass correct config to activity
                assert workflow_input.system_prompt == "You are a test agent"
                assert workflow_input.model_id == "kubiya/claude-sonnet-4"
                assert "filesystem" in workflow_input.mcp_servers
                assert workflow_input.skill_ids == ["file_system"]


class TestSkillsHandling:
    """Test that skills are handled correctly for dynamic executions"""

    @pytest.mark.asyncio
    async def test_skills_from_inline_config_not_database(self):
        """Verify skills come from request, not database lookup"""

        # When execute_agent_llm is called with an ephemeral entity_id,
        # the skills API call will fail (404), but execution should continue
        # using the skill_ids from the request

        from control_plane_api.app.activities.agent_activities import (
            execute_agent_llm,
            ActivityExecuteAgentInput,
        )

        activity_input = ActivityExecuteAgentInput(
            execution_id=str(uuid.uuid4()),
            agent_id=str(uuid.uuid4()),  # Ephemeral - not in DB
            organization_id="org_123",
            prompt="Test task",
            system_prompt="You are a test agent",
            model_id="kubiya/claude-sonnet-4",
            mcp_servers={},
            control_plane_url="http://localhost:8000",
            api_key="test_key",
        )

        # Mock the skills API call to return 404 (entity doesn't exist)
        with patch('httpx.AsyncClient') as mock_client:
            mock_response = Mock()
            mock_response.status_code = 404
            mock_client.return_value.__aenter__.return_value.get = AsyncMock(return_value=mock_response)

            # Mock agno_service to verify it's called with correct params
            with patch('control_plane_api.app.activities.agent_activities.agno_service.execute_agent_async') as mock_agno:
                mock_agno.return_value = {
                    "success": True,
                    "response": "Test response",
                    "usage": {"total_tokens": 50},
                }

                # Execute
                result = await execute_agent_llm(activity_input)

                # Verify execution succeeded despite 404 from skills API
                assert result["success"] is True

                # Verify agno was called with inline config
                mock_agno.assert_called_once()
                call_kwargs = mock_agno.call_args.kwargs
                assert call_kwargs["system_prompt"] == "You are a test agent"
                assert call_kwargs["model"] == "kubiya/claude-sonnet-4"


class TestAccessControl:
    """Test access control for dynamic executions"""

    @pytest.mark.asyncio
    async def test_request_access_to_private_execution(self, db_session):
        """Test requesting access to a private dynamic execution"""
        from control_plane_api.app.services.access_control_service import access_control_service
        from control_plane_api.app.models.execution import Execution
        from control_plane_api.app.models.associations import ExecutionParticipant

        # Create execution with owner
        execution_id = str(uuid.uuid4())
        execution = Execution(
            id=execution_id,
            organization_id="kubiya-ai",
            execution_type="AGENT",
            entity_id=str(uuid.uuid4()),
            runner_name="dynamic-execution",
            task_queue_name="test-queue",
            prompt="Test",
            status="COMPLETED",
            execution_metadata={
                "is_dynamic_execution": True,
                "privacy_level": "private",
                "share_requests": [],
            },
        )
        db_session.add(execution)

        owner = ExecutionParticipant(
            execution_id=execution_id,
            organization_id="kubiya-ai",
            user_id="owner_user",
            user_email="owner@example.com",
            role="owner",
        )
        db_session.add(owner)
        db_session.commit()

        # Mock Knock service
        with patch('control_plane_api.app.services.access_control_service.knock_service.send_access_request_notification') as mock_knock:
            mock_knock.return_value = True

            # Request access
            result = await access_control_service.request_access(
                db=db_session,
                execution_id=execution_id,
                organization_id="kubiya-ai",
                user_id="requester_user",
                user_name="Requester",
                user_email="requester@example.com",
            )

            # Verify
            assert result["status"] == "request_sent"

            # Verify share_requests updated
            db_session.refresh(execution)
            assert len(execution.execution_metadata["share_requests"]) == 1
            assert execution.execution_metadata["share_requests"][0]["user_id"] == "requester_user"

            # Verify Knock notification sent
            mock_knock.assert_called_once()


# Pytest fixtures
@pytest.fixture
def db_session():
    """Create a real database session for E2E testing"""
    from sqlalchemy import create_engine
    from sqlalchemy.orm import sessionmaker
    from control_plane_api.app.database import Base

    # Create engine with production database
    database_url = os.getenv('DATABASE_URL')
    if not database_url:
        pytest.skip("DATABASE_URL not configured for E2E tests")

    engine = create_engine(database_url)
    SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

    # Create session
    session = SessionLocal()

    yield session

    # Cleanup
    session.close()


if __name__ == "__main__":
    pytest.main([__file__, "-v", "-s"])
