"""
Integration tests for LangChain Olostep package.
"""

import pytest
import asyncio
from unittest.mock import Mock, patch, AsyncMock
from langchain_olostep import scrape_website, scrape_batch, crawl_website, OlostepLoader, OlostepWebCrawler


class TestIntegration:
    """Integration tests for the complete package."""
    
    @patch('langchain_olostep.tools.OlostepAPI')
    def test_tool_imports(self, mock_api_class):
        """Test that all tools can be imported and used."""
        # Test tool imports
        assert scrape_website is not None
        assert scrape_batch is not None
        assert crawl_website is not None
        assert OlostepLoader is not None
        assert OlostepWebCrawler is not None
    
    @patch('langchain_olostep.tools.OlostepAPI')
    def test_tool_schemas(self, mock_api_class):
        """Test that tools have proper schemas."""
        # Mock API to avoid actual calls
        mock_api = Mock()
        mock_api.scrape_url = AsyncMock(return_value={"markdown": "# Test"})
        mock_api.scrape_batch = AsyncMock(return_value=[{"markdown": "# Test"}])
        mock_api.crawl_website = AsyncMock(return_value={"pages": [{"markdown": "# Test"}]})
        mock_api_class.return_value = mock_api
        
        # Test tool schemas
        assert hasattr(scrape_website, 'args_schema')
        assert hasattr(scrape_batch, 'args_schema')
        assert hasattr(crawl_website, 'args_schema')
        
        # Test that tools can be called
        result = asyncio.run(scrape_website.ainvoke({
            "url": "https://example.com",
            "format": "markdown"
        }))
        assert result == "# Test"
    
    @patch('langchain_olostep.document_loaders.OlostepAPI')
    def test_loader_imports(self, mock_api_class):
        """Test that document loaders can be imported and used."""
        # Mock API to avoid actual calls
        mock_api = Mock()
        mock_api.scrape_url = AsyncMock(return_value={"markdown": "# Test"})
        mock_api.crawl_website = AsyncMock(return_value={"pages": [{"markdown": "# Test"}]})
        mock_api_class.return_value = mock_api
        
        # Test loader creation
        loader = OlostepLoader(
            urls=["https://example.com"],
            api_key="test-key"
        )
        assert loader is not None
        
        crawler = OlostepWebCrawler(
            start_url="https://example.com",
            api_key="test-key"
        )
        assert crawler is not None
    
    def test_package_version(self):
        """Test that package version is accessible."""
        from langchain_olostep import __version__
        assert __version__ is not None
        assert isinstance(__version__, str)
    
    def test_package_exports(self):
        """Test that package exports are correct."""
        from langchain_olostep import __all__
        
        expected_exports = [
            "scrape_website",
            "scrape_batch", 
            "OlostepLoader",
            "OlostepWebCrawler"
        ]
        
        for export in expected_exports:
            assert export in __all__
    
    @patch('langchain_olostep.tools.OlostepAPI')
    def test_error_handling_consistency(self, mock_api_class):
        """Test that error handling is consistent across tools."""
        # Mock API to raise errors
        mock_api = Mock()
        mock_api.scrape_url = AsyncMock(side_effect=Exception("Test error"))
        mock_api.scrape_batch = AsyncMock(side_effect=Exception("Test error"))
        mock_api.crawl_website = AsyncMock(side_effect=Exception("Test error"))
        mock_api_class.return_value = mock_api
        
        # Test that all tools raise LangChainException
        from langchain_core.exceptions import LangChainException
        
        with pytest.raises(LangChainException):
            asyncio.run(scrape_website.ainvoke({
                "url": "https://example.com",
                "format": "markdown"
            }))
        
        with pytest.raises(LangChainException):
            asyncio.run(scrape_batch.ainvoke({
                "urls": ["https://example.com"],
                "format": "markdown"
            }))
        
        with pytest.raises(LangChainException):
            asyncio.run(crawl_website.ainvoke({
                "start_url": "https://example.com",
                "max_pages": 10,
                "format": "markdown"
            }))
    
    @patch('langchain_olostep.tools.OlostepAPI')
    def test_format_consistency(self, mock_api_class):
        """Test that format handling is consistent across tools."""
        # Mock API responses with different formats
        mock_api = Mock()
        mock_api.scrape_url = AsyncMock(return_value={
            "markdown": "# Markdown Content",
            "html": "<h1>HTML Content</h1>",
            "text": "Plain Text Content",
            "json": {"content": "JSON Content"}
        })
        mock_api_class.return_value = mock_api
        
        # Test different formats
        formats = ["markdown", "html", "text", "json"]
        for format_type in formats:
            result = asyncio.run(scrape_website.ainvoke({
                "url": "https://example.com",
                "format": format_type
            }))
            
            if format_type == "markdown":
                assert result == "# Markdown Content"
            elif format_type == "html":
                assert result == "<h1>HTML Content</h1>"
            elif format_type == "text":
                assert result == "Plain Text Content"
            elif format_type == "json":
                assert result == '{"content": "JSON Content"}'
    
    @patch('langchain_olostep.tools.OlostepAPI')
    def test_async_consistency(self, mock_api_class):
        """Test that async behavior is consistent."""
        # Mock API with async methods
        mock_api = Mock()
        mock_api.scrape_url = AsyncMock(return_value={"markdown": "# Test"})
        mock_api.scrape_batch = AsyncMock(return_value=[{"markdown": "# Test"}])
        mock_api.crawl_website = AsyncMock(return_value={"pages": [{"markdown": "# Test"}]})
        mock_api_class.return_value = mock_api
        
        # Test that all tools work with asyncio.run
        async def test_async_tools():
            # Test scrape_website
            result1 = await scrape_website.ainvoke({
                "url": "https://example.com",
                "format": "markdown"
            })
            assert result1 == "# Test"
            
            # Test scrape_batch
            result2 = await scrape_batch.ainvoke({
                "urls": ["https://example.com"],
                "format": "markdown"
            })
            assert "Test" in result2
            
            # Test crawl_website
            result3 = await crawl_website.ainvoke({
                "start_url": "https://example.com",
                "max_pages": 10,
                "format": "markdown"
            })
            assert "Test" in result3
        
        # Run async test
        asyncio.run(test_async_tools())
    
    def test_environment_variable_handling(self):
        """Test that environment variable handling works correctly."""
        import os
        
        # Test with no API key
        with patch.dict(os.environ, {}, clear=True):
            with pytest.raises(ValueError, match="OLOSTEP_API_KEY environment variable is required"):
                from langchain_olostep.tools import OlostepAPI
                OlostepAPI()
        
        # Test with API key in environment
        with patch.dict(os.environ, {"OLOSTEP_API_KEY": "test-key"}):
            from langchain_olostep.tools import OlostepAPI
            api = OlostepAPI()
            assert api.api_key == "test-key"
    
    @patch('langchain_olostep.document_loaders.OlostepAPI')
    def test_metadata_consistency(self, mock_api_class):
        """Test that metadata is consistent across document loaders."""
        # Mock API responses with metadata
        mock_api = Mock()
        mock_api.scrape_url = AsyncMock(return_value={
            "markdown": "# Test Content",
            "id": "test-id",
            "timestamp": "2023-01-01T00:00:00Z"
        })
        mock_api.crawl_website = AsyncMock(return_value={
            "pages": [{"url": "https://example.com", "markdown": "# Test"}],
            "id": "crawl-id",
            "timestamp": "2023-01-01T00:00:00Z"
        })
        mock_api_class.return_value = mock_api
        
        # Test OlostepLoader metadata
        loader = OlostepLoader(
            urls=["https://example.com"],
            api_key="test-key"
        )
        documents = loader.load()
        
        assert documents[0].metadata["source"] == "olostep"
        assert documents[0].metadata["url"] == "https://example.com"
        assert documents[0].metadata["format"] == "markdown"
        assert documents[0].metadata["scrape_id"] == "test-id"
        
        # Test OlostepWebCrawler metadata
        crawler = OlostepWebCrawler(
            start_url="https://example.com",
            api_key="test-key"
        )
        documents = crawler.load()
        
        assert documents[0].metadata["source"] == "olostep_crawl"
        assert documents[0].metadata["url"] == "https://example.com"
        assert documents[0].metadata["format"] == "markdown"
        assert documents[0].metadata["crawl_id"] == "crawl-id"

