"""
Tests for dynamic workflow tools functionality.
"""

import pytest
import json
from pathlib import Path
from unittest.mock import Mock, patch, AsyncMock
import tempfile

from app.tools.workflow import (
    WorkflowConfig,
    WorkflowsConfig,
    AuthenticationConfig,
    load_workflow_configs,
    get_workflow_by_name,
    generate_claude_tool_definitions,
    get_workflow_tool_names,
    validate_workflows_json,
    clear_workflow_cache
)


@pytest.fixture
def temp_working_dir():
    """Create a temporary working directory for tests."""
    with tempfile.TemporaryDirectory() as tmpdir:
        yield tmpdir


@pytest.fixture
def sample_workflows_config():
    """Sample workflows configuration."""
    return {
        "workflows": [
            {
                "name": "analyze_keywords",
                "description": "Analyzes keyword strategy",
                "endpoint": "https://api.example.com/workflows/analyze/stream",
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "keywords": {"type": "array"},
                        "market": {"type": "string"}
                    },
                    "required": ["keywords"]
                },
                "authentication": {
                    "type": "bearer",
                    "token_env": "WORKFLOW_API_TOKEN"
                },
                "timeout": 300
            },
            {
                "name": "generate_report",
                "description": "Generates a comprehensive report",
                "endpoint": "https://api.example.com/workflows/report/stream",
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "data": {"type": "object"}
                    },
                    "required": ["data"]
                },
                "timeout": 600
            }
        ]
    }


class TestWorkflowConfig:
    """Tests for WorkflowConfig model."""
    
    def test_valid_workflow_config(self):
        """Test creating a valid workflow configuration."""
        config = WorkflowConfig(
            name="test_workflow",
            description="Test workflow",
            endpoint="https://api.example.com/test",
            input_schema={
                "type": "object",
                "properties": {
                    "input": {"type": "string"}
                }
            }
        )
        
        assert config.name == "test_workflow"
        assert config.timeout == 300  # Default value
    
    def test_invalid_workflow_name(self):
        """Test that invalid workflow names are rejected."""
        with pytest.raises(ValueError, match="must be a valid Python identifier"):
            WorkflowConfig(
                name="invalid-name",  # Hyphens not allowed
                description="Test",
                endpoint="https://api.example.com/test",
                input_schema={"type": "object"}
            )
    
    def test_invalid_endpoint(self):
        """Test that invalid endpoints are rejected."""
        with pytest.raises(ValueError, match="must start with http"):
            WorkflowConfig(
                name="test_workflow",
                description="Test",
                endpoint="ftp://invalid.com/test",  # Wrong protocol
                input_schema={"type": "object"}
            )
    
    def test_to_claude_tool_definition(self):
        """Test converting workflow config to Claude tool definition."""
        config = WorkflowConfig(
            name="test_workflow",
            description="Test workflow",
            endpoint="https://api.example.com/test",
            input_schema={
                "type": "object",
                "properties": {
                    "input": {"type": "string"}
                }
            }
        )
        
        tool_def = config.to_claude_tool_definition()
        
        assert tool_def["name"] == "test_workflow"
        assert tool_def["description"] == "Test workflow"
        assert "input_schema" in tool_def
    
    def test_get_auth_headers_bearer(self):
        """Test getting bearer authentication headers."""
        config = WorkflowConfig(
            name="test_workflow",
            description="Test",
            endpoint="https://api.example.com/test",
            input_schema={"type": "object"},
            authentication=AuthenticationConfig(
                type="bearer",
                token_env="TEST_TOKEN"
            )
        )
        
        with patch.dict("os.environ", {"TEST_TOKEN": "test_token_123"}):
            headers = config.get_auth_headers()
            assert headers["Authorization"] == "Bearer test_token_123"
    
    def test_get_auth_headers_api_key(self):
        """Test getting API key authentication headers."""
        config = WorkflowConfig(
            name="test_workflow",
            description="Test",
            endpoint="https://api.example.com/test",
            input_schema={"type": "object"},
            authentication=AuthenticationConfig(
                type="api_key",
                token_env="TEST_API_KEY"
            )
        )
        
        with patch.dict("os.environ", {"TEST_API_KEY": "api_key_456"}):
            headers = config.get_auth_headers()
            assert headers["X-API-Key"] == "api_key_456"


class TestWorkflowsConfig:
    """Tests for WorkflowsConfig model."""
    
    def test_unique_workflow_names(self, sample_workflows_config):
        """Test that workflow names must be unique."""
        config = WorkflowsConfig(**sample_workflows_config)
        assert len(config.workflows) == 2
    
    def test_duplicate_workflow_names(self):
        """Test that duplicate workflow names are rejected."""
        config_data = {
            "workflows": [
                {
                    "name": "duplicate",
                    "description": "First",
                    "endpoint": "https://api.example.com/1",
                    "input_schema": {"type": "object"}
                },
                {
                    "name": "duplicate",
                    "description": "Second",
                    "endpoint": "https://api.example.com/2",
                    "input_schema": {"type": "object"}
                }
            ]
        }
        
        with pytest.raises(ValueError, match="Duplicate workflow names"):
            WorkflowsConfig(**config_data)


class TestDynamicToolLoader:
    """Tests for dynamic tool loader functions."""
    
    def test_load_workflow_configs_missing_file(self, temp_working_dir):
        """Test loading workflows when file doesn't exist."""
        configs = load_workflow_configs(temp_working_dir)
        assert configs == []
    
    def test_load_workflow_configs_valid_file(self, temp_working_dir, sample_workflows_config):
        """Test loading valid workflow.json file."""
        workflows_file = Path(temp_working_dir) / "workflow.json"
        with open(workflows_file, 'w') as f:
            json.dump(sample_workflows_config, f)
        
        configs = load_workflow_configs(temp_working_dir)
        assert len(configs) == 2
        assert configs[0].name == "analyze_keywords"
        assert configs[1].name == "generate_report"
    
    def test_load_workflow_configs_invalid_json(self, temp_working_dir):
        """Test loading invalid JSON file."""
        workflows_file = Path(temp_working_dir) / "workflow.json"
        with open(workflows_file, 'w') as f:
            f.write("invalid json {{{")
        
        configs = load_workflow_configs(temp_working_dir)
        assert configs == []
    
    def test_workflow_cache(self, temp_working_dir, sample_workflows_config):
        """Test that workflow configs are cached."""
        workflows_file = Path(temp_working_dir) / "workflow.json"
        with open(workflows_file, 'w') as f:
            json.dump(sample_workflows_config, f)
        
        # Load once
        configs1 = load_workflow_configs(temp_working_dir)
        
        # Modify file
        workflows_file.unlink()
        
        # Load again - should return cached version
        configs2 = load_workflow_configs(temp_working_dir)
        assert len(configs2) == 2
        
        # Clear cache and load again - should return empty
        clear_workflow_cache(temp_working_dir)
        configs3 = load_workflow_configs(temp_working_dir)
        assert configs3 == []
    
    def test_get_workflow_by_name(self, temp_working_dir, sample_workflows_config):
        """Test getting workflow by name."""
        workflows_file = Path(temp_working_dir) / "workflow.json"
        with open(workflows_file, 'w') as f:
            json.dump(sample_workflows_config, f)
        
        workflow = get_workflow_by_name(temp_working_dir, "analyze_keywords")
        assert workflow is not None
        assert workflow.name == "analyze_keywords"
        
        workflow = get_workflow_by_name(temp_working_dir, "nonexistent")
        assert workflow is None
    
    def test_generate_claude_tool_definitions(self, temp_working_dir, sample_workflows_config):
        """Test generating Claude tool definitions."""
        workflows_file = Path(temp_working_dir) / "workflow.json"
        with open(workflows_file, 'w') as f:
            json.dump(sample_workflows_config, f)
        
        configs = load_workflow_configs(temp_working_dir)
        tool_defs = generate_claude_tool_definitions(configs)
        
        assert len(tool_defs) == 2
        assert tool_defs[0]["name"] == "analyze_keywords"
        assert "description" in tool_defs[0]
        assert "input_schema" in tool_defs[0]
    
    def test_get_workflow_tool_names(self, temp_working_dir, sample_workflows_config):
        """Test extracting workflow tool names."""
        workflows_file = Path(temp_working_dir) / "workflow.json"
        with open(workflows_file, 'w') as f:
            json.dump(sample_workflows_config, f)
        
        configs = load_workflow_configs(temp_working_dir)
        names = get_workflow_tool_names(configs)
        
        assert names == ["analyze_keywords", "generate_report"]
    
    def test_validate_workflows_json_valid(self, temp_working_dir, sample_workflows_config):
        """Test validating valid workflow.json file."""
        workflows_file = Path(temp_working_dir) / "workflow.json"
        with open(workflows_file, 'w') as f:
            json.dump(sample_workflows_config, f)
        
        is_valid, errors = validate_workflows_json(str(workflows_file))
        assert is_valid
        assert errors == []
    
    def test_validate_workflows_json_missing_file(self, temp_working_dir):
        """Test validating missing file."""
        workflows_file = Path(temp_working_dir) / "workflow.json"
        
        is_valid, errors = validate_workflows_json(str(workflows_file))
        assert not is_valid
        assert len(errors) > 0


@pytest.mark.asyncio
class TestWorkflowToolHandler:
    """Tests for WorkflowToolHandler."""
    
    async def test_can_handle_workflow_tool(self):
        """Test that handler can identify workflow tools."""
        from app.tools.workflow import WorkflowToolHandler, WorkflowConfig
        
        workflow_config = WorkflowConfig(
            name="test_workflow",
            description="Test",
            endpoint="https://api.example.com/test",
            input_schema={"type": "object"}
        )
        
        handler = WorkflowToolHandler(
            builder=Mock(),
            sse_handler=Mock(),
            processor=None,
            workflow_configs={"test_workflow": workflow_config}
        )
        
        assert handler.can_handle("test_workflow")
        assert not handler.can_handle("other_tool")


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

