"""
Pytest tests for fuzzy matching with lead-to-account and company-to-company data
"""
import pytest
import pandas as pd
import time
from pathlib import Path
from typing import Dict

# Test data paths
TEST_DATA_BASE = Path("test_data/synthetic")

class TestFuzzyMatchAccuracy:
    """Test fuzzy matching accuracy with synthetic test data."""
    
    @pytest.fixture(scope="class")
    def test_data_paths(self):
        """Fixture to provide test data paths."""
        return {
            'lead_to_account': {
                '10k': {
                    'leads': TEST_DATA_BASE / "lead_to_account/10k/leads_10k.csv",
                    'accounts': TEST_DATA_BASE / "lead_to_account/10k/accounts_10k.csv",
                    'expected': TEST_DATA_BASE / "lead_to_account/10k/expected_matches_10k.csv"
                },
                '50k': {
                    'leads': TEST_DATA_BASE / "lead_to_account/50k/leads_50k.csv",
                    'accounts': TEST_DATA_BASE / "lead_to_account/50k/accounts_50k.csv",
                    'expected': TEST_DATA_BASE / "lead_to_account/50k/expected_matches_50k.csv"
                },
                '100k': {
                    'leads': TEST_DATA_BASE / "lead_to_account/100k/leads_100k.csv",
                    'accounts': TEST_DATA_BASE / "lead_to_account/100k/accounts_100k.csv",
                    'expected': TEST_DATA_BASE / "lead_to_account/100k/expected_matches_100k.csv"
                }
            },
            'company_to_company': {
                '10k': {
                    'source': TEST_DATA_BASE / "10k/CRMData_10k.csv",
                    'reference': TEST_DATA_BASE / "10k/ForeignData_10k.csv"
                },
                '50k': {
                    'source': TEST_DATA_BASE / "50k/CRMData_50k.csv",
                    'reference': TEST_DATA_BASE / "50k/ForeignData_50k.csv"
                },
                '100k': {
                    'source': TEST_DATA_BASE / "100k/CRMData_100k.csv",
                    'reference': TEST_DATA_BASE / "100k/ForeignData_100k.csv"
                }
            }
        }
    
    def calculate_metrics(self, predicted_matches: pd.DataFrame, expected_matches: pd.DataFrame) -> Dict:
        """Calculate precision, recall, and F1 score."""
        # Extract match pairs from predicted results
        if 'source_id' in predicted_matches.columns:
            predicted_pairs = set(zip(predicted_matches['source_id'], predicted_matches['ref_id']))
        elif 'lead_id' in predicted_matches.columns:
            predicted_pairs = set(zip(predicted_matches['lead_id'], predicted_matches['account_id']))
        else:
            # Fallback for simple merge results
            predicted_pairs = set(zip(predicted_matches.iloc[:, 0], predicted_matches.iloc[:, 1]))
        
        # Extract expected pairs
        expected_pairs = set(zip(expected_matches.iloc[:, 0], expected_matches.iloc[:, 1]))
        
        # Calculate metrics
        true_positives = len(predicted_pairs & expected_pairs)
        false_positives = len(predicted_pairs - expected_pairs)
        false_negatives = len(expected_pairs - predicted_pairs)
        
        precision = true_positives / (true_positives + false_positives) if (true_positives + false_positives) > 0 else 0
        recall = true_positives / (true_positives + false_negatives) if (true_positives + false_negatives) > 0 else 0
        f1 = 2 * (precision * recall) / (precision + recall) if (precision + recall) > 0 else 0
        
        return {
            'precision': precision,
            'recall': recall,
            'f1_score': f1,
            'true_positives': true_positives,
            'false_positives': false_positives,
            'false_negatives': false_negatives
        }
    
    @pytest.mark.parametrize("size", ['10k', '50k', '100k'])
    def test_lead_to_account_baseline(self, test_data_paths, size):
        """Test baseline lead-to-account matching with exact domain match."""
        paths = test_data_paths['lead_to_account'][size]
        
        # Load data
        leads = pd.read_csv(paths['leads'])
        accounts = pd.read_csv(paths['accounts'])
        expected = pd.read_csv(paths['expected'])
        
        # Simple exact domain matching as baseline
        leads_with_domain = leads[leads['domain'].notna() & (leads['domain'] != '')]
        accounts_with_domain = accounts[accounts['domain'].notna() & (accounts['domain'] != '')]
        
        start_time = time.time()
        matches = pd.merge(
            leads_with_domain[['lead_id', 'domain']], 
            accounts_with_domain[['account_id', 'domain']], 
            on='domain'
        )
        runtime = time.time() - start_time
        
        # Calculate metrics
        metrics = self.calculate_metrics(matches, expected)
        
        # Assert minimum performance thresholds for baseline
        assert metrics['recall'] >= 0.7, f"Baseline recall too low: {metrics['recall']:.3f}"
        assert runtime < 5.0, f"Baseline matching took too long: {runtime:.2f}s"
        
        print(f"\n{size} Baseline Results:")
        print(f"  Precision: {metrics['precision']:.3f}")
        print(f"  Recall: {metrics['recall']:.3f}")
        print(f"  F1 Score: {metrics['f1_score']:.3f}")
        print(f"  Runtime: {runtime:.3f}s")
    
    @pytest.mark.fuzzy
    @pytest.mark.parametrize("size", ['10k', '50k', '100k'])
    def test_lead_to_account_fuzzy(self, test_data_paths, size):
        """Test fuzzy lead-to-account matching."""
        pytest.skip("Implement after integrating fuzzy matcher")
        
        paths = test_data_paths['lead_to_account'][size]
        
        # Load data
        leads = pd.read_csv(paths['leads'])
        accounts = pd.read_csv(paths['accounts'])
        expected = pd.read_csv(paths['expected'])
        
        # TODO: Import and use your fuzzy matcher
        # from foundrymatch.core.engine import FuzzyMatcher
        # matcher = FuzzyMatcher(algorithm='WRatio', threshold=80)
        # matches = matcher.match(leads, accounts, 
        #                        source_cols=['company_name', 'domain'],
        #                        ref_cols=['company_name', 'domain'])
        
        # Calculate metrics
        # metrics = self.calculate_metrics(matches, expected)
        
        # Assert performance improvements over baseline
        # assert metrics['recall'] >= 0.85, f"Fuzzy recall too low: {metrics['recall']:.3f}"
        # assert metrics['precision'] >= 0.80, f"Fuzzy precision too low: {metrics['precision']:.3f}"
    
    @pytest.mark.parametrize("size", ['10k', '50k', '100k'])
    def test_data_integrity(self, test_data_paths, size):
        """Test that all required test data files exist and are valid."""
        # Check lead-to-account files
        l2a_paths = test_data_paths['lead_to_account'][size]
        for file_type, path in l2a_paths.items():
            assert path.exists(), f"Missing {file_type} file: {path}"
            
            # Check file is not empty
            df = pd.read_csv(path)
            assert len(df) > 0, f"Empty {file_type} file: {path}"
            
            # Check required columns
            if file_type == 'leads':
                required_cols = ['lead_id', 'email', 'company_name']
                for col in required_cols:
                    assert col in df.columns, f"Missing column '{col}' in leads file"
            elif file_type == 'accounts':
                required_cols = ['account_id', 'company_name', 'domain']
                for col in required_cols:
                    assert col in df.columns, f"Missing column '{col}' in accounts file"
        
        # Check company-to-company files
        c2c_paths = test_data_paths['company_to_company'][size]
        for file_type, path in c2c_paths.items():
            assert path.exists(), f"Missing {file_type} file: {path}"
            df = pd.read_csv(path)
            assert len(df) > 0, f"Empty {file_type} file: {path}"

class TestFuzzyMatchPerformance:
    """Test fuzzy matching performance and scalability."""
    
    @pytest.mark.performance
    @pytest.mark.parametrize("size,max_time", [
        ('10k', 10.0),   # 10k should complete in 10 seconds
        ('50k', 30.0),   # 50k should complete in 30 seconds
        ('100k', 60.0),  # 100k should complete in 60 seconds
    ])
    def test_matching_performance(self, size, max_time):
        """Test that matching completes within reasonable time."""
        lead_path = TEST_DATA_BASE / f"lead_to_account/{size}/leads_{size}.csv"
        account_path = TEST_DATA_BASE / f"lead_to_account/{size}/accounts_{size}.csv"
        
        leads = pd.read_csv(lead_path)
        accounts = pd.read_csv(account_path)
        
        # Simple performance test with domain matching
        start_time = time.time()
        
        # Simulate processing
        leads_domains = set(leads[leads['domain'].notna()]['domain'].unique())
        accounts_domains = set(accounts[accounts['domain'].notna()]['domain'].unique())
        common_domains = leads_domains & accounts_domains
        
        runtime = time.time() - start_time
        
        assert runtime < max_time, f"{size} processing took {runtime:.2f}s, exceeding limit of {max_time}s"
        print(f"\n{size} processed in {runtime:.3f}s")

if __name__ == "__main__":
    # Run specific test
    pytest.main([__file__, "-v", "-k", "test_lead_to_account_baseline"])
