"""
Tests for CHRONOS Dark Web Monitor Module
"""

import pytest
import asyncio
from datetime import datetime
from unittest.mock import MagicMock, patch, AsyncMock

from chronos.core.detect.darkweb_monitor import (
    DarkWebMonitor,
    TorController,
    AlertDispatcher,
    WebhookConfig,
    MonitoringPattern,
    DataFingerprint,
    PasteEntry,
    MonitoringMatch,
    DarkWebAlert,
    BaseScraper,
    PastebinScraper,
    GenericPasteScraper,
    SourceType,
    MatchType,
    AlertSeverity,
    BUILTIN_PATTERNS,
    create_monitor,
    quick_scan,
)


class TestMonitoringPattern:
    """Tests for MonitoringPattern."""
    
    def test_keyword_pattern(self):
        """Test keyword pattern matching."""
        pattern = MonitoringPattern(
            id="test1",
            name="Test Keyword",
            pattern="secret",
            match_type=MatchType.KEYWORD,
        )
        
        matches = pattern.matches("This is a secret message with secret data")
        assert len(matches) == 2
        assert all(m[0].lower() == "secret" for m in matches)
    
    def test_regex_pattern(self):
        """Test regex pattern matching."""
        pattern = MonitoringPattern(
            id="test2",
            name="Email Pattern",
            pattern=r"[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}",
            match_type=MatchType.REGEX,
        )
        
        matches = pattern.matches("Contact: user@example.com or admin@test.org")
        assert len(matches) == 2
        assert "user@example.com" in [m[0] for m in matches]
    
    def test_invalid_regex(self):
        """Test invalid regex pattern is disabled."""
        pattern = MonitoringPattern(
            id="test3",
            name="Invalid Regex",
            pattern="[invalid(regex",
            match_type=MatchType.REGEX,
        )
        
        assert not pattern.enabled
        assert pattern.matches("any text") == []
    
    def test_pattern_to_dict(self):
        """Test pattern serialization."""
        pattern = MonitoringPattern(
            id="test4",
            name="Test",
            pattern="test",
            match_type=MatchType.KEYWORD,
            severity=AlertSeverity.HIGH,
            tags=["test", "demo"],
        )
        
        d = pattern.to_dict()
        assert d["id"] == "test4"
        assert d["severity"] == "high"
        assert "test" in d["tags"]


class TestDataFingerprint:
    """Tests for DataFingerprint."""
    
    def test_from_content(self):
        """Test creating fingerprint from content."""
        content = "This is test content"
        fp = DataFingerprint.from_content(content, "Test Doc")
        
        assert fp.name == "Test Doc"
        assert fp.algorithm == "sha256"
        assert len(fp.hash_value) == 64
    
    def test_from_content_md5(self):
        """Test MD5 fingerprint."""
        content = "Test content"
        fp = DataFingerprint.from_content(
            content, "MD5 Doc", algorithm="md5"
        )
        
        assert fp.algorithm == "md5"
        assert len(fp.hash_value) == 32
    
    def test_matches(self):
        """Test fingerprint matching."""
        content = "Exact match required"
        fp = DataFingerprint.from_content(content, "Test")
        
        assert fp.matches(content)
        assert not fp.matches(content + " ")
        assert not fp.matches("Different content")
    
    def test_matches_bytes(self):
        """Test fingerprint matching with bytes."""
        content = b"Binary content"
        fp = DataFingerprint.from_content(content, "Binary")
        
        assert fp.matches(content)
        assert fp.matches(content.decode())  # String version
    
    def test_to_dict(self):
        """Test fingerprint serialization."""
        fp = DataFingerprint.from_content("test", "Test")
        d = fp.to_dict()
        
        assert "hash_value" in d
        assert d["algorithm"] == "sha256"


class TestPasteEntry:
    """Tests for PasteEntry."""
    
    def test_create_paste(self):
        """Test creating paste entry."""
        paste = PasteEntry(
            id="abc123",
            source="pastebin",
            title="Test Paste",
            author="anon",
            content="Test content here",
            url="https://pastebin.com/abc123",
            timestamp=datetime.now(),
        )
        
        assert paste.size == len("Test content here")
    
    def test_to_dict(self):
        """Test paste serialization."""
        paste = PasteEntry(
            id="test",
            source="test",
            title=None,
            author=None,
            content="content",
            url="http://test",
            timestamp=datetime.now(),
        )
        
        d = paste.to_dict()
        assert d["id"] == "test"
        assert "timestamp" in d


class TestTorController:
    """Tests for TorController."""
    
    def test_init(self):
        """Test controller initialization."""
        controller = TorController()
        
        assert controller.socks_port == 9050
        assert controller.control_port == 9051
        assert not controller.is_connected
    
    def test_custom_ports(self):
        """Test custom port configuration."""
        controller = TorController(
            socks_port=9150,
            control_port=9151,
        )
        
        assert controller.socks_port == 9150
        assert controller.control_port == 9151
    
    def test_proxy_config(self):
        """Test proxy configuration generation."""
        controller = TorController()
        config = controller.get_proxy_config()
        
        assert "http" in config
        assert "https" in config
        assert "9050" in config["http"]


class TestWebhookConfig:
    """Tests for WebhookConfig."""
    
    def test_default_config(self):
        """Test default webhook configuration."""
        config = WebhookConfig(url="https://example.com/webhook")
        
        assert config.method == "POST"
        assert config.enabled
        assert config.retry_count == 3
    
    def test_custom_config(self):
        """Test custom webhook configuration."""
        config = WebhookConfig(
            url="https://example.com",
            method="PUT",
            min_severity=AlertSeverity.CRITICAL,
            retry_count=5,
        )
        
        assert config.method == "PUT"
        assert config.min_severity == AlertSeverity.CRITICAL
        assert config.retry_count == 5
    
    def test_to_dict(self):
        """Test webhook config serialization."""
        config = WebhookConfig(url="https://test.com")
        d = config.to_dict()
        
        assert d["url"] == "https://test.com"
        assert "enabled" in d


class TestAlertDispatcher:
    """Tests for AlertDispatcher."""
    
    def test_add_webhook(self):
        """Test adding webhook."""
        dispatcher = AlertDispatcher()
        config = WebhookConfig(url="https://test.com")
        
        dispatcher.add_webhook(config)
        assert len(dispatcher._webhooks) == 1
    
    def test_add_callback(self):
        """Test adding callback."""
        dispatcher = AlertDispatcher()
        callback = MagicMock()
        
        dispatcher.add_callback(callback)
        assert callback in dispatcher._callbacks
    
    def test_severity_filtering(self):
        """Test severity-based webhook filtering."""
        dispatcher = AlertDispatcher()
        config = WebhookConfig(
            url="https://test.com",
            min_severity=AlertSeverity.HIGH,
        )
        
        low_alert = DarkWebAlert(
            id="1",
            title="Low",
            description="Low severity",
            severity=AlertSeverity.LOW,
            matches=[],
            source="test",
            source_type=SourceType.PASTE_SITE,
        )
        
        high_alert = DarkWebAlert(
            id="2",
            title="High",
            description="High severity",
            severity=AlertSeverity.HIGH,
            matches=[],
            source="test",
            source_type=SourceType.PASTE_SITE,
        )
        
        assert not dispatcher._should_send(low_alert, config)
        assert dispatcher._should_send(high_alert, config)


class TestDarkWebMonitor:
    """Tests for DarkWebMonitor."""
    
    def test_create_monitor(self):
        """Test monitor creation."""
        monitor = DarkWebMonitor()
        
        assert not monitor.is_running
        assert len(monitor._scrapers) > 0
    
    def test_add_keyword(self):
        """Test adding keyword pattern."""
        monitor = DarkWebMonitor()
        pattern_id = monitor.add_keyword("secret-key")
        
        assert pattern_id in monitor._patterns
        assert monitor._patterns[pattern_id].match_type == MatchType.KEYWORD
    
    def test_add_email_domain(self):
        """Test adding email domain pattern."""
        monitor = DarkWebMonitor()
        pattern_id = monitor.add_email_domain("company.com")
        
        assert pattern_id in monitor._patterns
        pattern = monitor._patterns[pattern_id]
        assert pattern.match_type == MatchType.EMAIL_DOMAIN
    
    def test_add_regex(self):
        """Test adding regex pattern."""
        monitor = DarkWebMonitor()
        pattern_id = monitor.add_regex(r"API[_-]KEY[_-]\d+", "API Keys")
        
        assert pattern_id in monitor._patterns
    
    def test_enable_builtin_pattern(self):
        """Test enabling built-in pattern."""
        monitor = DarkWebMonitor()
        
        # Built-in patterns are disabled by default
        assert not monitor._patterns["builtin_email"].enabled
        
        monitor.enable_builtin_pattern("email")
        assert monitor._patterns["builtin_email"].enabled
    
    def test_add_fingerprint(self):
        """Test adding data fingerprint."""
        monitor = DarkWebMonitor()
        fp_id = monitor.add_fingerprint_from_content(
            "Secret document",
            "Secret Doc",
        )
        
        assert fp_id in monitor._fingerprints
    
    def test_add_webhook(self):
        """Test adding webhook."""
        monitor = DarkWebMonitor()
        monitor.add_webhook("https://hooks.example.com")
        
        assert len(monitor._dispatcher._webhooks) == 1
    
    def test_scan_content(self):
        """Test content scanning."""
        monitor = DarkWebMonitor()
        monitor.add_keyword("password")
        
        matches = asyncio.run(monitor.scan_content(
            "The password is: secret123",
            source="test",
        ))
        
        assert len(matches) == 1
        assert "password" in matches[0].matched_text.lower()
    
    def test_scan_with_fingerprint(self):
        """Test scanning with fingerprint."""
        monitor = DarkWebMonitor()
        
        secret_content = "This is the exact secret content"
        monitor.add_fingerprint_from_content(secret_content, "Secret")
        
        matches = asyncio.run(monitor.scan_content(secret_content))
        
        assert len(matches) == 1
        assert matches[0].fingerprint is not None
    
    def test_get_stats(self):
        """Test getting statistics."""
        monitor = DarkWebMonitor()
        stats = monitor.get_stats()
        
        assert "scans_completed" in stats
        assert "patterns_active" in stats
        assert "webhooks_configured" in stats
    
    def test_remove_pattern(self):
        """Test removing pattern."""
        monitor = DarkWebMonitor()
        pattern_id = monitor.add_keyword("test")
        
        assert pattern_id in monitor._patterns
        
        monitor.remove_pattern(pattern_id)
        assert pattern_id not in monitor._patterns
    
    def test_disable_pattern(self):
        """Test disabling pattern."""
        monitor = DarkWebMonitor()
        pattern_id = monitor.add_keyword("test")
        
        assert monitor._patterns[pattern_id].enabled
        
        monitor.disable_pattern(pattern_id)
        assert not monitor._patterns[pattern_id].enabled


class TestBuiltinPatterns:
    """Tests for built-in patterns."""
    
    def test_email_pattern(self):
        """Test email pattern."""
        pattern = BUILTIN_PATTERNS["email"]
        regex = MonitoringPattern(
            id="test",
            name="test",
            pattern=pattern,
            match_type=MatchType.REGEX,
        )
        
        matches = regex.matches("Contact: user@example.com")
        assert len(matches) == 1
    
    def test_credit_card_pattern(self):
        """Test credit card pattern."""
        pattern = BUILTIN_PATTERNS["credit_card"]
        regex = MonitoringPattern(
            id="test",
            name="test",
            pattern=pattern,
            match_type=MatchType.REGEX,
        )
        
        matches = regex.matches("Card: 4111-1111-1111-1111")
        assert len(matches) == 1
    
    def test_aws_key_pattern(self):
        """Test AWS key pattern."""
        pattern = BUILTIN_PATTERNS["aws_key"]
        regex = MonitoringPattern(
            id="test",
            name="test",
            pattern=pattern,
            match_type=MatchType.REGEX,
        )
        
        matches = regex.matches("AWS: AKIAIOSFODNN7EXAMPLE")
        assert len(matches) == 1
    
    def test_private_key_pattern(self):
        """Test private key pattern."""
        pattern = BUILTIN_PATTERNS["private_key"]
        regex = MonitoringPattern(
            id="test",
            name="test",
            pattern=pattern,
            match_type=MatchType.REGEX,
        )
        
        matches = regex.matches("-----BEGIN RSA PRIVATE KEY-----")
        assert len(matches) == 1


class TestConvenienceFunctions:
    """Tests for convenience functions."""
    
    def test_create_monitor(self):
        """Test create_monitor function."""
        monitor = create_monitor()
        
        assert isinstance(monitor, DarkWebMonitor)
        assert not monitor.use_tor
    
    def test_create_monitor_with_tor(self):
        """Test create_monitor with Tor."""
        monitor = create_monitor(use_tor=True)
        
        assert monitor.use_tor


class TestDarkWebAlert:
    """Tests for DarkWebAlert."""
    
    def test_create_alert(self):
        """Test alert creation."""
        alert = DarkWebAlert(
            id="alert1",
            title="Test Alert",
            description="Test description",
            severity=AlertSeverity.HIGH,
            matches=[],
            source="test",
            source_type=SourceType.PASTE_SITE,
        )
        
        assert alert.id == "alert1"
        assert not alert.acknowledged
        assert not alert.webhook_sent
    
    def test_alert_to_dict(self):
        """Test alert serialization."""
        alert = DarkWebAlert(
            id="alert2",
            title="Test",
            description="Desc",
            severity=AlertSeverity.CRITICAL,
            matches=[],
            source="test",
            source_type=SourceType.PASTE_SITE,
            recommendations=["Do something"],
        )
        
        d = alert.to_dict()
        assert d["severity"] == "critical"
        assert len(d["recommendations"]) == 1


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