"""
Tests for the website crawler functionality
"""
import pytest
import tempfile
import os
from unittest.mock import Mock, patch
from crawler import WebsiteCrawler

class TestWebsiteCrawler:
    
    def setup_method(self):
        """Set up test fixtures"""
        self.crawler = WebsiteCrawler()
    
    def test_init(self):
        """Test crawler initialization"""
        assert self.crawler.max_depth == 3
        assert self.crawler.max_pages == 100
        assert self.crawler.crawl_delay == 1.0
        assert len(self.crawler.file_extensions) > 0
        assert len(self.crawler.suspicious_patterns) > 0
    
    def test_is_downloadable_file(self):
        """Test file type detection"""
        # Test executable files
        assert self.crawler._is_downloadable_file("http://example.com/file.exe", "Download")
        assert self.crawler._is_downloadable_file("http://example.com/script.bat", "Get file")
        
        # Test archive files
        assert self.crawler._is_downloadable_file("http://example.com/archive.zip", "Download")
        assert self.crawler._is_downloadable_file("http://example.com/data.rar", "Save")
        
        # Test document files
        assert self.crawler._is_downloadable_file("http://example.com/doc.pdf", "Document")
        
        # Test non-downloadable files
        assert not self.crawler._is_downloadable_file("http://example.com/page.html", "Link")
        assert not self.crawler._is_downloadable_file("http://example.com/image.jpg", "Image")
    
    def test_is_suspicious_filename(self):
        """Test suspicious filename detection"""
        # Test suspicious patterns
        assert self.crawler._is_suspicious_filename("http://example.com/keygen.exe", "Keygen")
        assert self.crawler._is_suspicious_filename("http://example.com/crack.zip", "Crack")
        assert self.crawler._is_suspicious_filename("http://example.com/loader.bat", "Loader")
        
        # Test normal files
        assert not self.crawler._is_suspicious_filename("http://example.com/document.pdf", "Document")
        assert not self.crawler._is_suspicious_filename("http://example.com/software.exe", "Software")
    
    def test_extract_filename(self):
        """Test filename extraction"""
        # Test URL with filename
        filename = self.crawler._extract_filename("http://example.com/path/to/file.exe", "Download")
        assert filename == "file.exe"
        
        # Test URL without filename
        filename = self.crawler._extract_filename("http://example.com/download", "Get File")
        assert filename == "Get_File"
        
        # Test URL with query parameters
        filename = self.crawler._extract_filename("http://example.com/download?file=test.zip", "Download")
        assert filename == "test.zip"
    
    def test_is_internal_link(self):
        """Test internal link detection"""
        base_url = "https://example.com"
        
        # Test internal links
        assert self.crawler._is_internal_link("https://example.com/page", base_url)
        assert self.crawler._is_internal_link("https://example.com/subdir/file", base_url)
        
        # Test external links
        assert not self.crawler._is_internal_link("https://other.com/page", base_url)
        assert not self.crawler._is_internal_link("http://example.com/page", base_url)  # Different protocol
    
    def test_calculate_file_hash(self):
        """Test file hash calculation"""
        with tempfile.NamedTemporaryFile(delete=False) as tmp_file:
            tmp_file.write(b"test content")
            tmp_file.flush()
            
            hash_value = self.crawler._calculate_file_hash(tmp_file.name)
            assert len(hash_value) == 64  # SHA256 hash length
            assert hash_value == "a8f5f167f44f4964e6c998dee827110c"  # Expected hash for "test content"
            
            os.unlink(tmp_file.name)
    
    @patch('crawler.requests.Session.get')
    def test_download_file_success(self, mock_get):
        """Test successful file download"""
        # Mock response
        mock_response = Mock()
        mock_response.status_code = 200
        mock_response.headers = {'content-type': 'application/octet-stream'}
        mock_response.iter_content.return_value = [b"file content"]
        mock_get.return_value = mock_response
        
        file_info = {
            'url': 'http://example.com/test.exe',
            'filename': 'test.exe'
        }
        
        with tempfile.TemporaryDirectory() as tmp_dir:
            result = self.crawler.download_file(file_info, tmp_dir)
            
            assert result is not None
            assert result['local_path'] is not None
            assert result['file_size'] == 12  # Length of "file content"
            assert result['file_hash'] is not None
            assert os.path.exists(result['local_path'])
    
    @patch('crawler.requests.Session.get')
    def test_download_file_failure(self, mock_get):
        """Test file download failure"""
        # Mock failed response
        mock_response = Mock()
        mock_response.status_code = 404
        mock_get.return_value = mock_response
        
        file_info = {
            'url': 'http://example.com/nonexistent.exe',
            'filename': 'nonexistent.exe'
        }
        
        result = self.crawler.download_file(file_info)
        assert result is None
    
    def test_find_embedded_files(self):
        """Test embedded file discovery"""
        from bs4 import BeautifulSoup
        
        html_content = """
        <html>
        <head>
            <script>
                var fileUrl = "http://example.com/embedded.exe";
                var config = { download: "http://example.com/config.zip" };
            </script>
        </head>
        <body>
            <script>
                var maliciousFile = "http://example.com/malware.bat";
            </script>
        </body>
        </html>
        """
        
        soup = BeautifulSoup(html_content, 'html.parser')
        files = self.crawler._find_embedded_files(soup, "http://example.com")
        
        assert len(files) >= 2  # Should find at least embedded.exe and malware.bat
        assert any(f['filename'] == 'embedded.exe' for f in files)
        assert any(f['filename'] == 'malware.bat' for f in files)
