"""
Tests for LangChain Olostep tools.
"""

import pytest
import asyncio
from unittest.mock import Mock, patch, AsyncMock
from langchain_olostep.tools import scrape_website, scrape_batch, crawl_website, OlostepAPI


class TestOlostepAPI:
    """Test OlostepAPI client."""
    
    def test_init_with_api_key(self):
        """Test initialization with API key."""
        api = OlostepAPI("test-api-key")
        assert api.api_key == "test-api-key"
        assert api.base_url == "https://api.olostep.com/v1"
        assert api.headers["Authorization"] == "Bearer test-api-key"
    
    def test_init_without_api_key(self):
        """Test initialization without API key raises error."""
        with pytest.raises(ValueError, match="OLOSTEP_API_KEY environment variable is required"):
            OlostepAPI()
    
    @patch('requests.post')
    def test_scrape_url_success(self, mock_post):
        """Test successful URL scraping."""
        # Mock response
        mock_response = Mock()
        mock_response.json.return_value = {
            "markdown": "# Test Content",
            "html": "<h1>Test Content</h1>",
            "id": "test-id"
        }
        mock_response.raise_for_status.return_value = None
        mock_post.return_value = mock_response
        
        # Test
        api = OlostepAPI("test-api-key")
        result = asyncio.run(api.scrape_url("https://example.com"))
        
        # Assertions
        assert result["markdown"] == "# Test Content"
        mock_post.assert_called_once()
        call_args = mock_post.call_args
        assert call_args[1]["json"]["url_to_scrape"] == "https://example.com"
        assert call_args[1]["json"]["formats"] == ["json", "markdown", "text", "html"]
    
    @patch('requests.post')
    def test_scrape_url_with_parser(self, mock_post):
        """Test URL scraping with parser ID."""
        mock_response = Mock()
        mock_response.json.return_value = {"markdown": "# Test Content"}
        mock_response.raise_for_status.return_value = None
        mock_post.return_value = mock_response
        
        api = OlostepAPI("test-api-key")
        result = asyncio.run(api.scrape_url("https://example.com", parser_id="test-parser"))
        
        call_args = mock_post.call_args
        assert call_args[1]["json"]["parser"]["id"] == "test-parser"
    
    @patch('requests.post')
    def test_scrape_url_error(self, mock_post):
        """Test URL scraping error handling."""
        mock_post.side_effect = Exception("Network error")
        
        api = OlostepAPI("test-api-key")
        with pytest.raises(Exception, match="Network error"):
            asyncio.run(api.scrape_url("https://example.com"))
    
    @patch('requests.post')
    def test_scrape_batch_success(self, mock_post):
        """Test successful batch scraping."""
        mock_response = Mock()
        mock_response.json.return_value = [
            {"markdown": "# Page 1"},
            {"markdown": "# Page 2"}
        ]
        mock_response.raise_for_status.return_value = None
        mock_post.return_value = mock_response
        
        api = OlostepAPI("test-api-key")
        urls = ["https://example1.com", "https://example2.com"]
        result = asyncio.run(api.scrape_batch(urls))
        
        assert len(result) == 2
        assert result[0]["markdown"] == "# Page 1"
        assert result[1]["markdown"] == "# Page 2"
    
    @patch('requests.post')
    def test_crawl_website_success(self, mock_post):
        """Test successful website crawling."""
        mock_response = Mock()
        mock_response.json.return_value = {
            "pages": [
                {"url": "https://example.com", "markdown": "# Homepage"},
                {"url": "https://example.com/about", "markdown": "# About"}
            ],
            "id": "crawl-id"
        }
        mock_response.raise_for_status.return_value = None
        mock_post.return_value = mock_response
        
        api = OlostepAPI("test-api-key")
        result = asyncio.run(api.crawl_website("https://example.com", max_pages=5))
        
        assert len(result["pages"]) == 2
        assert result["pages"][0]["url"] == "https://example.com"


class TestScrapeWebsiteTool:
    """Test scrape_website tool."""
    
    @patch('langchain_olostep.tools.OlostepAPI')
    def test_scrape_website_success(self, mock_api_class):
        """Test successful website scraping."""
        # Mock API client
        mock_api = Mock()
        mock_api.scrape_url = AsyncMock(return_value={
            "markdown": "# Test Content",
            "html": "<h1>Test Content</h1>"
        })
        mock_api_class.return_value = mock_api
        
        # Test
        result = asyncio.run(scrape_website.ainvoke({
            "url": "https://example.com",
            "format": "markdown"
        }))
        
        assert result == "# Test Content"
        mock_api.scrape_url.assert_called_once_with(
            "https://example.com", "markdown", 0, None
        )
    
    @patch('langchain_olostep.tools.OlostepAPI')
    def test_scrape_website_different_format(self, mock_api_class):
        """Test scraping with different format."""
        mock_api = Mock()
        mock_api.scrape_url = AsyncMock(return_value={
            "html": "<h1>Test Content</h1>",
            "markdown": "# Test Content"
        })
        mock_api_class.return_value = mock_api
        
        result = asyncio.run(scrape_website.ainvoke({
            "url": "https://example.com",
            "format": "html"
        }))
        
        assert result == "<h1>Test Content</h1>"
    
    @patch('langchain_olostep.tools.OlostepAPI')
    def test_scrape_website_fallback_format(self, mock_api_class):
        """Test scraping with fallback format."""
        mock_api = Mock()
        mock_api.scrape_url = AsyncMock(return_value={
            "text": "Test Content",
            "markdown": "# Test Content"
        })
        mock_api_class.return_value = mock_api
        
        result = asyncio.run(scrape_website.ainvoke({
            "url": "https://example.com",
            "format": "html"  # Requested format not available
        }))
        
        # Should fallback to markdown, then text
        assert result == "# Test Content"
    
    @patch('langchain_olostep.tools.OlostepAPI')
    def test_scrape_website_error(self, mock_api_class):
        """Test scraping error handling."""
        mock_api = Mock()
        mock_api.scrape_url = AsyncMock(side_effect=Exception("API Error"))
        mock_api_class.return_value = mock_api
        
        with pytest.raises(Exception, match="Failed to scrape https://example.com: API Error"):
            asyncio.run(scrape_website.ainvoke({
                "url": "https://example.com",
                "format": "markdown"
            }))


class TestScrapeBatchTool:
    """Test scrape_batch tool."""
    
    @patch('langchain_olostep.tools.OlostepAPI')
    def test_scrape_batch_success(self, mock_api_class):
        """Test successful batch scraping."""
        mock_api = Mock()
        mock_api.scrape_batch = AsyncMock(return_value=[
            {"markdown": "# Page 1"},
            {"markdown": "# Page 2"}
        ])
        mock_api_class.return_value = mock_api
        
        result = asyncio.run(scrape_batch.ainvoke({
            "urls": ["https://example1.com", "https://example2.com"],
            "format": "markdown"
        }))
        
        # Should return JSON string
        import json
        parsed_result = json.loads(result)
        assert len(parsed_result) == 2
        assert parsed_result[0]["content"] == "# Page 1"
        assert parsed_result[0]["url"] == "https://example1.com"
    
    @patch('langchain_olostep.tools.OlostepAPI')
    def test_scrape_batch_error(self, mock_api_class):
        """Test batch scraping error handling."""
        mock_api = Mock()
        mock_api.scrape_batch = AsyncMock(side_effect=Exception("Batch API Error"))
        mock_api_class.return_value = mock_api
        
        with pytest.raises(Exception, match="Failed to scrape batch: Batch API Error"):
            asyncio.run(scrape_batch.ainvoke({
                "urls": ["https://example.com"],
                "format": "markdown"
            }))


class TestCrawlWebsiteTool:
    """Test crawl_website tool."""
    
    @patch('langchain_olostep.tools.OlostepAPI')
    def test_crawl_website_success(self, mock_api_class):
        """Test successful website crawling."""
        mock_api = Mock()
        mock_api.crawl_website = AsyncMock(return_value={
            "pages": [
                {"url": "https://example.com", "markdown": "# Homepage"},
                {"url": "https://example.com/about", "markdown": "# About"}
            ],
            "id": "crawl-id"
        })
        mock_api_class.return_value = mock_api
        
        result = asyncio.run(crawl_website.ainvoke({
            "start_url": "https://example.com",
            "max_pages": 10,
            "format": "markdown"
        }))
        
        # Should return JSON string
        import json
        parsed_result = json.loads(result)
        assert parsed_result["start_url"] == "https://example.com"
        assert len(parsed_result["pages"]) == 2
        assert parsed_result["pages"][0]["url"] == "https://example.com"
    
    @patch('langchain_olostep.tools.OlostepAPI')
    def test_crawl_website_error(self, mock_api_class):
        """Test crawling error handling."""
        mock_api = Mock()
        mock_api.crawl_website = AsyncMock(side_effect=Exception("Crawl API Error"))
        mock_api_class.return_value = mock_api
        
        with pytest.raises(Exception, match="Failed to crawl https://example.com: Crawl API Error"):
            asyncio.run(crawl_website.ainvoke({
                "start_url": "https://example.com",
                "max_pages": 10,
                "format": "markdown"
            }))




