"""
API Integration Tests for Dynamic Execution Endpoints

Tests the HTTP API endpoints for dynamic agent/team execution.
"""

import pytest
import json
from fastapi.testclient import TestClient
from unittest.mock import patch, Mock, AsyncMock, MagicMock
import uuid
import os

# Set required environment variables before importing app
os.environ["SUPABASE_URL"] = "http://test-supabase.local"
os.environ["SUPABASE_SERVICE_KEY"] = "test-service-key"
os.environ["TEMPORAL_HOST"] = "test-temporal.local:7233"
os.environ["TEMPORAL_NAMESPACE"] = "test-namespace"

from control_plane_api.app.main import app
from control_plane_api.app.database import get_db


# Mock database session
def get_mock_db():
    """Mock database session for testing"""
    mock_db = MagicMock()
    mock_db.query = MagicMock()
    mock_db.add = MagicMock()
    mock_db.commit = MagicMock()
    mock_db.refresh = MagicMock()
    mock_db.close = MagicMock()
    try:
        yield mock_db
    finally:
        mock_db.close()


# Override the database dependency
app.dependency_overrides[get_db] = get_mock_db


class TestDynamicExecutionAPI:
    """Test the dynamic execution API endpoints"""

    def setup_method(self):
        """Setup test client"""
        self.client = TestClient(app)
        self.test_api_key = "eyJhbGciOiJIUzI1NiIsImtpZCI6Imt1Yml5YS5haSIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InNoYWtlZEBrdWJpeWEuYWkiLCJleHAiOjE3OTU2MjY5OTAsImlhdCI6MTc2NDE3NzM5MCwibmJmIjoxNzY0MTc3MzkwLCJvcmdhbml6YXRpb24iOiJrdWJpeWEtYWkiLCJ0b2tlbl9pZCI6ImIwZjI1ZmFiLTE5MWItNDA3Zi04MjUwLTIzNjNmNWJmN2MyYyIsInRva2VuX25hbWUiOnsidHlwZSI6IiIsIm5hbWUiOiJzaGFrZWQtbWJwLXRlc3QyIiwiZGVzY3JpcHRpb24iOiJtY3AiLCJlbWFpbCI6InNoYWtlZEBrdWJpeWEuYWkiLCJ0b2tlbl9pZCI6ImIwZjI1ZmFiLTE5MWItNDA3Zi04MjUwLTIzNjNmNWJmN2MyYyIsInR0bCI6IjM2NGQifX0.LpyEz2jcqpK5c0Vd7CrOdlLMS9sqOdW2G9SvM_9y_VM"
        self.test_org_id = "kubiya-ai"
        self.test_user_id = "shaked@kubiya.ai"
        self.auth_headers = {
            "Authorization": f"Bearer {self.test_api_key}",
            "X-Organization-ID": self.test_org_id,
        }

    @patch('control_plane_api.app.routers.dynamic_executions.queue_resolution_service.resolve_queue')
    def test_resolve_queue_endpoint(self, mock_resolve):
        """Test POST /api/v1/dynamic-executions/resolve-queue"""
        # Mock service response
        mock_resolve.return_value = {
            "worker_queue_id": "queue_123",
            "worker_queue_name": "k8s-workers",
            "reasoning": "Kubernetes capability match",
            "confidence": "high",
            "alternatives": [],
        }

        # Make request
        response = self.client.post(
            "/api/v1/dynamic-executions/resolve-queue",
            json={
                "prompt": "Deploy kubernetes cluster",
                "organization_id": self.test_org_id,
                "preferred_runtime": "claude_code",
            },
            headers=self.auth_headers,
        )

        # Verify response
        assert response.status_code == 200
        data = response.json()
        assert data["worker_queue_id"] == "queue_123"
        assert data["confidence"] == "high"
        assert "Kubernetes" in data["reasoning"]

    @patch('control_plane_api.app.routers.dynamic_executions.dynamic_execution_service.create_and_execute')
    def test_execute_dynamic_agent(self, mock_execute):
        """Test POST /api/v1/dynamic-executions/execute with agent"""
        # Mock service response
        execution_id = str(uuid.uuid4())
        workflow_id = f"dynamic-execution-{execution_id}"
        mock_execute.return_value = {
            "execution_id": execution_id,
            "workflow_id": workflow_id,
            "status": "PENDING",
            "entity_id": str(uuid.uuid4()),
            "worker_queue_id": "queue_456",
            "message": "Dynamic execution started successfully",
        }

        # Make request
        response = self.client.post(
            "/api/v1/dynamic-executions/execute",
            json={
                "prompt": "Analyze security vulnerabilities",
                "entity": {
                    "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": {},
                },
                "meta_agent_session_id": "session_789",
                "is_shared": False,
                "privacy_level": "private",
            },
            headers=self.auth_headers,
        )

        # Verify response
        assert response.status_code == 200
        data = response.json()
        assert data["execution_id"] == execution_id
        assert data["status"] == "PENDING"
        assert "entity_id" in data

        # Verify service was called with correct params
        mock_execute.assert_called_once()
        call_kwargs = mock_execute.call_args.kwargs
        assert call_kwargs["prompt"] == "Analyze security vulnerabilities"
        assert call_kwargs["entity_definition"]["name"] == "Security Scanner"
        assert call_kwargs["meta_agent_session_id"] == "session_789"

    @patch('control_plane_api.app.routers.dynamic_executions.dynamic_execution_service.create_and_execute')
    def test_execute_dynamic_team_with_subagents(self, mock_execute):
        """Test POST /api/v1/dynamic-executions/execute with team"""
        execution_id = str(uuid.uuid4())
        mock_execute.return_value = {
            "execution_id": execution_id,
            "workflow_id": f"dynamic-execution-{execution_id}",
            "status": "PENDING",
            "entity_id": str(uuid.uuid4()),
            "worker_queue_id": "queue_789",
            "message": "Dynamic execution started successfully",
        }

        # Make request with team + subagents
        response = self.client.post(
            "/api/v1/dynamic-executions/execute",
            json={
                "prompt": "Review this pull request",
                "entity": {
                    "entity_type": "team",
                    "name": "Code Review Team",
                    "system_prompt": "Coordinate code review",
                    "runtime": "claude_code",
                    "subagents": [
                        {
                            "name": "Security Reviewer",
                            "system_prompt": "Check for security issues",
                            "skill_ids": ["security_scan"],
                        },
                        {
                            "name": "Style Checker",
                            "system_prompt": "Check code style",
                            "skill_ids": ["linter"],
                        },
                    ],
                },
            },
            headers=self.auth_headers,
        )

        # Verify
        assert response.status_code == 200
        data = response.json()
        assert data["execution_id"] == execution_id

        # Verify team config was passed
        call_kwargs = mock_execute.call_args.kwargs
        assert call_kwargs["entity_definition"]["entity_type"] == "team"
        assert len(call_kwargs["entity_definition"]["subagents"]) == 2

    @patch('control_plane_api.app.routers.dynamic_executions.access_control_service.request_access')
    def test_request_access_endpoint(self, mock_request):
        """Test POST /api/v1/dynamic-executions/executions/{id}/request-access"""
        execution_id = str(uuid.uuid4())
        mock_request.return_value = {"status": "request_sent"}

        response = self.client.post(
            f"/api/v1/dynamic-executions/executions/{execution_id}/request-access",
            headers=self.auth_headers,
        )

        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "request_sent"

    @patch('control_plane_api.app.routers.dynamic_executions.access_control_service.grant_access')
    def test_grant_access_endpoint(self, mock_grant):
        """Test POST /api/v1/dynamic-executions/executions/{id}/grant-access/{user_id}"""
        execution_id = str(uuid.uuid4())
        target_user_id = "user_789"
        mock_grant.return_value = {"status": "access_granted"}

        response = self.client.post(
            f"/api/v1/dynamic-executions/executions/{execution_id}/grant-access/{target_user_id}",
            headers=self.auth_headers,
        )

        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "access_granted"

    @patch('control_plane_api.app.routers.dynamic_executions.access_control_service.deny_access')
    def test_deny_access_endpoint(self, mock_deny):
        """Test POST /api/v1/dynamic-executions/executions/{id}/deny-access/{user_id}"""
        execution_id = str(uuid.uuid4())
        target_user_id = "user_789"
        mock_deny.return_value = {"status": "access_denied"}

        response = self.client.post(
            f"/api/v1/dynamic-executions/executions/{execution_id}/deny-access/{target_user_id}",
            json={"reason": "Not authorized"},
            headers=self.auth_headers,
        )

        assert response.status_code == 200
        data = response.json()
        assert data["status"] == "access_denied"

    def test_execute_without_worker_queue_auto_resolves(self):
        """Test that omitting worker_queue_id triggers auto-resolution"""
        with patch('control_plane_api.app.routers.dynamic_executions.dynamic_execution_service.create_and_execute') as mock_execute:
            execution_id = str(uuid.uuid4())
            mock_execute.return_value = {
                "execution_id": execution_id,
                "workflow_id": f"dynamic-execution-{execution_id}",
                "status": "PENDING",
                "entity_id": str(uuid.uuid4()),
                "worker_queue_id": "auto_selected_queue",
                "message": "Dynamic execution started successfully",
            }

            response = self.client.post(
                "/api/v1/dynamic-executions/execute",
                json={
                    "prompt": "Deploy application",
                    "entity": {
                        "system_prompt": "You are a deployment specialist",
                    },
                    # worker_queue_id omitted - should auto-resolve
                },
                headers=self.auth_headers,
            )

            assert response.status_code == 200
            # Verify auto-resolution happened (worker_queue_id was null in request)
            call_kwargs = mock_execute.call_args.kwargs
            assert call_kwargs["worker_queue_id"] is None  # Service will resolve

    def test_invalid_entity_type_returns_422(self):
        """Test that invalid entity_type returns validation error"""
        response = self.client.post(
            "/api/v1/dynamic-executions/execute",
            json={
                "prompt": "Test",
                "entity": {
                    "entity_type": "invalid_type",  # Not 'agent' or 'team'
                    "system_prompt": "Test",
                },
            },
            headers=self.auth_headers,
        )

        assert response.status_code == 422  # Validation error

    def test_missing_system_prompt_returns_422(self):
        """Test that missing system_prompt returns validation error"""
        response = self.client.post(
            "/api/v1/dynamic-executions/execute",
            json={
                "prompt": "Test",
                "entity": {
                    "entity_type": "agent",
                    # system_prompt missing - required field
                },
            },
            headers=self.auth_headers,
        )

        assert response.status_code == 422


class TestErrorHandling:
    """Test error handling in dynamic execution"""

    def setup_method(self):
        self.client = TestClient(app)
        self.test_api_key = "eyJhbGciOiJIUzI1NiIsImtpZCI6Imt1Yml5YS5haSIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InNoYWtlZEBrdWJpeWEuYWkiLCJleHAiOjE3OTU2MjY5OTAsImlhdCI6MTc2NDE3NzM5MCwibmJmIjoxNzY0MTc3MzkwLCJvcmdhbml6YXRpb24iOiJrdWJpeWEtYWkiLCJ0b2tlbl9pZCI6ImIwZjI1ZmFiLTE5MWItNDA3Zi04MjUwLTIzNjNmNWJmN2MyYyIsInRva2VuX25hbWUiOnsidHlwZSI6IiIsIm5hbWUiOiJzaGFrZWQtbWJwLXRlc3QyIiwiZGVzY3JpcHRpb24iOiJtY3AiLCJlbWFpbCI6InNoYWtlZEBrdWJpeWEuYWkiLCJ0b2tlbl9pZCI6ImIwZjI1ZmFiLTE5MWItNDA3Zi04MjUwLTIzNjNmNWJmN2MyYyIsInR0bCI6IjM2NGQifX0.LpyEz2jcqpK5c0Vd7CrOdlLMS9sqOdW2G9SvM_9y_VM"
        self.auth_headers = {
            "Authorization": f"Bearer {self.test_api_key}",
            "X-Organization-ID": "kubiya-ai",
        }

    @patch('control_plane_api.app.routers.dynamic_executions.dynamic_execution_service.create_and_execute')
    def test_worker_queue_not_found_returns_400(self, mock_execute):
        """Test that invalid worker_queue_id returns 400"""
        mock_execute.side_effect = ValueError("Worker queue not found")

        response = self.client.post(
            "/api/v1/dynamic-executions/execute",
            json={
                "prompt": "Test",
                "entity": {
                    "system_prompt": "Test agent",
                },
                "worker_queue_id": "nonexistent_queue",
            },
            headers=self.auth_headers,
        )

        assert response.status_code == 400
        assert "not found" in response.json()["detail"].lower()

    @patch('control_plane_api.app.routers.dynamic_executions.access_control_service.grant_access')
    def test_grant_access_permission_denied_returns_403(self, mock_grant):
        """Test that non-owner cannot grant access"""
        mock_grant.side_effect = PermissionError("Only owner can grant access")

        response = self.client.post(
            f"/api/v1/dynamic-executions/executions/{uuid.uuid4()}/grant-access/user_123",
            headers=self.auth_headers,
        )

        assert response.status_code == 403
        assert "owner" in response.json()["detail"].lower()


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