"""
Comprehensive tests for domain enhancement features.

Tests:
1. Domain registry name normalization
2. Company name -> domain lookup with confidence levels  
3. Domain suggestion from company names
4. Dedupe domain derivation
5. L2A domain enhancement
6. Routing engine name fallback
7. Learned mapping persistence
"""

import pytest
import os
import sys
import tempfile
import yaml
import pandas as pd

# Add src to path
sys.path.insert(0, os.path.join(os.path.dirname(__file__), '..', 'src'))

from fmatch.saas.services.domain_family import (
    DomainFamilyRegistry,
    CONFIDENCE_EXACT_MATCH,
    CONFIDENCE_NORMALIZED_MATCH
)


class TestDomainNormalization:
    """Test company name normalization logic"""
    
    def test_removes_accents(self):
        reg = DomainFamilyRegistry()
        assert reg._normalize_name("Café") == "cafe"
        assert reg._normalize_name("François") == "francois"
        assert reg._normalize_name("São Paulo") == "sao paulo"
    
    def test_removes_regional_tokens(self):
        reg = DomainFamilyRegistry()
        assert reg._normalize_name("Adobe EMEA") == "adobe"
        assert reg._normalize_name("Microsoft UK") == "microsoft"
        assert reg._normalize_name("Google APAC") == "google"
        assert reg._normalize_name("Salesforce Global") == "salesforce"
    
    def test_removes_corp_suffixes(self):
        reg = DomainFamilyRegistry()
        assert reg._normalize_name("Adobe Inc.") == "adobe"
        assert reg._normalize_name("Microsoft Corporation") == "microsoft"
        assert reg._normalize_name("SAP AG") == "sap"
        assert reg._normalize_name("Spotify AB") == "spotify"
    
    def test_preserves_meaningful_tokens(self):
        reg = DomainFamilyRegistry()
        assert "systems" in reg._normalize_name("Cisco Systems")
        assert "labs" in reg._normalize_name("Bell Labs")
        assert "networks" in reg._normalize_name("Juniper Networks")
    
    def test_handles_ampersands(self):
        reg = DomainFamilyRegistry()
        assert reg._normalize_name("AT&T") == "at&t"
        assert reg._normalize_name("Johnson & Johnson") == "johnson & johnson"

    def test_collapses_possessive_apostrophes(self):
        reg = DomainFamilyRegistry()
        assert reg._normalize_name("Phoenix Children's Hospital") == "phoenix childrens hospital"
        assert reg._normalize_name('Phoenix Children\u2019s Hospital') == "phoenix childrens hospital"

class TestCompanyNameLookup:
    """Test company name -> family lookup with confidence"""
    
    def setup_method(self):
        """Set up test registry with sample data"""
        self.reg = DomainFamilyRegistry()
        # Populate test data
        self.reg.raw_name_to_family["adobe"] = "adobe_family"
        self.reg.raw_name_to_family["adobe inc."] = "adobe_family"
        self.reg.name_to_family["adobe"] = "adobe_family"
        self.reg.alias_to_family["adobe.com"] = "adobe_family"
        self.reg._family_to_domains = {"adobe_family": {"adobe.com", "adobe.io"}}
    
    def test_exact_match_confidence(self):
        family, confidence = self.reg.lookup_family_by_name("Adobe")
        assert family == "adobe_family"
        assert confidence == CONFIDENCE_EXACT_MATCH
        
        family, confidence = self.reg.lookup_family_by_name("Adobe Inc.")
        assert family == "adobe_family"
        assert confidence == CONFIDENCE_EXACT_MATCH
    
    def test_normalized_match_confidence(self):
        # This matches after normalization (removes EMEA)
        family, confidence = self.reg.lookup_family_by_name("Adobe EMEA")
        assert family == "adobe_family"
        assert confidence == CONFIDENCE_NORMALIZED_MATCH
        
        family, confidence = self.reg.lookup_family_by_name("Adobe Corporation")
        assert family == "adobe_family"
        assert confidence == CONFIDENCE_NORMALIZED_MATCH
    
    def test_no_match(self):
        family, confidence = self.reg.lookup_family_by_name("Unknown Company")
        assert family is None
        assert confidence == 0.0
    
    def test_empty_input(self):
        family, confidence = self.reg.lookup_family_by_name("")
        assert family is None
        assert confidence == 0.0
        
        family, confidence = self.reg.lookup_family_by_name(None)
        assert family is None
        assert confidence == 0.0


class TestDomainSuggestion:
    """Test domain suggestions from company names"""
    
    def setup_method(self):
        self.reg = DomainFamilyRegistry()
        self.reg.raw_name_to_family["google"] = "google_family"
        self.reg.name_to_family["google"] = "google_family"
        self.reg._family_to_domains = {
            "google_family": {"google.com", "youtube.com", "gmail.com"}
        }
    
    def test_suggest_domains_filters_personal(self):
        """Personal domains like gmail.com should be filtered out"""
        suggestions = self.reg.suggest_domains("Google")
        domains = [d for d, _ in suggestions]
        
        assert "google.com" in domains
        assert "youtube.com" in domains
        assert "gmail.com" not in domains  # Filtered as personal domain
    
    def test_suggest_domains_with_confidence(self):
        suggestions = self.reg.suggest_domains("Google")
        assert len(suggestions) > 0
        for domain, confidence in suggestions:
            assert confidence == CONFIDENCE_EXACT_MATCH
    
    def test_no_suggestions_for_unknown(self):
        suggestions = self.reg.suggest_domains("Unknown Company")
        assert suggestions == []


class TestDomainEnhancement:
    
    def setup_method(self):
        """Create a test registry with sample data"""
        self.test_data = [
            {
                "family_id": "adobe",
                "parent": "adobe.com",
                "aliases": ["adobe.io", "behance.net"],
                "names": [
                    "Adobe",
                    "Adobe Inc.",
                    "Adobe Systems",
                    "Adobe EMEA",
                    "Adobe UK"
                ]
            },
            {
                "family_id": "cisco",
                "parent": "cisco.com",
                "aliases": ["webex.com"],
                "names": [
                    "Cisco",
                    "Cisco Systems",
                    "Cisco EMEA"
                ]
            },
            {
                "family_id": "bae",
                "parent": "baesystems.com",
                "aliases": [],
                "names": [
                    "BAE Systems",
                    "BAE Systems Inc",
                    "BAE Systems UK"
                ]
            }
        ]
        
        # Create temporary YAML file
        self.temp_file = tempfile.NamedTemporaryFile(mode='w', suffix='.yaml', delete=False)
        yaml.dump(self.test_data, self.temp_file)
        self.temp_file.close()
        
        self.registry = DomainFamilyRegistry(seed_path=self.temp_file.name)
    
    def teardown_method(self):
        """Clean up temp file"""
        if hasattr(self, 'temp_file'):
            os.unlink(self.temp_file.name)
    
    def test_name_normalization(self):
        """Test that 'Systems' is kept but regional tokens are removed"""
        reg = self.registry
        
        # "Systems" should be kept
        assert "systems" in reg._normalize_name("Cisco Systems")
        assert "systems" in reg._normalize_name("BAE Systems")
        
        # Regional tokens removed
        assert "emea" not in reg._normalize_name("Adobe EMEA")
        assert "uk" not in reg._normalize_name("Adobe UK")
        
        # Corp suffixes removed
        assert "inc" not in reg._normalize_name("Adobe Inc")
        assert "inc" not in reg._normalize_name("Adobe Inc.")
        
        # Combined test
        norm = reg._normalize_name("Adobe Systems Inc. EMEA")
        assert norm == "adobe systems"  # Systems kept, Inc. and EMEA removed
    
    def test_unicode_handling(self):
        """Test unicode normalization"""
        reg = self.registry
        
        # Add a unicode company
        reg.raw_name_to_family["müller ag"] = "mueller"
        reg.name_to_family["muller"] = "mueller"  # Normalized form
        
        # Exact unicode match (lowercase)
        family, conf = reg.lookup_family_by_name("Müller AG")
        assert family == "mueller"
        assert conf == CONFIDENCE_EXACT_MATCH
        
        # Test accent removal in normalization
        assert reg._normalize_name("Café Systems") == "cafe systems"
        assert reg._normalize_name("Zürich AG") == "zurich"
    
    def test_lookup_confidence_levels(self):
        """Test different confidence levels for lookups"""
        reg = self.registry
        
        # Exact match should have higher confidence
        family, conf = reg.lookup_family_by_name("Adobe EMEA")
        assert family == "adobe"
        assert conf == CONFIDENCE_EXACT_MATCH  # Exact match
        
        # Normalized match should have slightly lower confidence
        # Add a name that's not in raw but normalizes to existing
        test_name = "Adobe Corporation EMEA"  # Not in raw names
        family, conf = reg.lookup_family_by_name(test_name)
        assert family == "adobe"  # Should match via normalization
        assert conf == CONFIDENCE_NORMALIZED_MATCH  # Normalized match
        
        # Non-match should have zero confidence
        family, conf = reg.lookup_family_by_name("Unknown Company")
        assert family is None
        assert conf == 0.0
    
    def test_suggest_domains(self):
        """Test domain suggestions from company names"""
        reg = self.registry
        
        # Test exact company name
        suggestions = reg.suggest_domains("Adobe EMEA")
        assert len(suggestions) > 0
        domains = [d for d, _ in suggestions]
        assert "adobe.com" in domains
        assert "adobe.io" in domains
        
        # Test confidence is passed through
        for domain, confidence in suggestions:
            assert confidence == CONFIDENCE_EXACT_MATCH  # Exact match confidence
        
        # Test normalized match
        suggestions = reg.suggest_domains("Adobe Corporation")
        assert len(suggestions) > 0
        for domain, confidence in suggestions:
            assert confidence == CONFIDENCE_NORMALIZED_MATCH  # Normalized match confidence
        
        # Test no match
        suggestions = reg.suggest_domains("Unknown Company")
        assert len(suggestions) == 0
    
    def test_coverage_thresholds(self):
        """Test that coverage thresholds work correctly"""
        # Create test data
        data = pd.DataFrame({
            "Name": ["Adobe", "Adobe EMEA", "Unknown Corp", "Another Unknown", "Cisco Systems"],
            "Id": [1, 2, 3, 4, 5]
        })
        
        # Derive domains
        def derive_domain(name):
            suggestions = self.registry.suggest_domains(name)
            if suggestions and suggestions[0][1] >= CONFIDENCE_EXACT_MATCH:
                return suggestions[0][0]
            return None
        
        data["__derived_domain"] = data["Name"].apply(derive_domain)
        coverage = data["__derived_domain"].notna().mean()
        
        # We have 3/5 matches (Adobe, Adobe EMEA, Cisco Systems)
        assert coverage == 0.6
        
        # Test threshold decisions
        assert coverage >= 0.10  # Should add mapping
        assert coverage >= 0.25  # Should use for blocking
    
    def test_derived_weight_reduction(self):
        """Verify derived domains get reduced weight"""
        # In dedupe: 0.8 * 0.7 = 0.56
        dedupe_weight = 0.8 * 0.7
        assert dedupe_weight == 0.56
        
        # In L2A: should be 0.50
        l2a_weight = 0.50
        assert l2a_weight == 0.50
        
        # Verify L2A weight is lower than dedupe
        assert l2a_weight < dedupe_weight
    
    def test_add_name_override(self):
        """Test adding custom name overrides"""
        reg = self.registry
        
        # Add a new name mapping
        reg.add_name_override("Custom Company EMEA", "adobe")
        
        # Should now resolve to adobe
        family, conf = reg.lookup_family_by_name("Custom Company EMEA")
        assert family == "adobe"
        # Note: Since we added it directly to name_to_family, 
        # it will be found as a normalized match
        
        # The normalized form should be in the registry
        norm = reg._normalize_name("Custom Company EMEA")
        assert norm in reg.name_to_family
        assert reg.name_to_family[norm] == "adobe"
    
    def test_systems_keyword_preservation(self):
        """Ensure 'Systems' and similar keywords are preserved"""
        reg = self.registry
        
        # These should be preserved
        preserved = ["Systems", "Labs", "Research", "Technologies", "Software"]
        for keyword in preserved:
            norm = reg._normalize_name(f"Test {keyword} Inc")
            assert keyword.lower() in norm
        
        # These should be removed
        removed = ["Inc", "Corp", "EMEA", "UK", "Ltd"]
        for keyword in removed:
            norm = reg._normalize_name(f"Test {keyword}")
            assert keyword.lower() not in norm
    
    def test_edge_cases(self):
        """Test edge cases and error handling"""
        reg = self.registry
        
        # Empty string
        family, conf = reg.lookup_family_by_name("")
        assert family is None
        assert conf == 0.0
        
        # None
        family, conf = reg.lookup_family_by_name(None)
        assert family is None
        assert conf == 0.0
        
        # Whitespace only
        family, conf = reg.lookup_family_by_name("   ")
        assert family is None
        assert conf == 0.0
        
        # Special characters only
        norm = reg._normalize_name("@#$%^&*()")
        assert norm == ""


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