"""
Test fraud detection functionality for payment processing pipeline.
"""

import pytest
from datetime import datetime, timedelta
from atomingest.testing import PipelineTestCase


class TestFraudDetection(PipelineTestCase):
    pipeline_config = "pipeline.yaml"

    def setup_method(self):
        """Set up test data before each test"""
        self.valid_transaction = {
            "transaction_id": "TEST_001",
            "merchant_id": "MERCH_12345",
            "card_number": "4532015112830366",
            "card_expiry": "12/28",
            "cardholder_name": "JOHN SMITH",
            "amount": "89.99",
            "currency": "USD",
            "transaction_type": "PURCHASE",
            "merchant_category_code": "5411",
            "billing_address": '{"street": "123 Main St", "city": "New York", "state": "NY", "zip": "10001"}',
            "ip_address": "192.168.1.100",
            "device_fingerprint": "fp_abc123def456",
        }

    def test_normal_transaction_processing(self):
        """Test that normal transactions are processed successfully"""
        result = self.pipeline.process_record(self.valid_transaction)

        assert result.success
        assert result.data["status"] == "APPROVED"
        assert result.data["risk_score"] < 0.5
        assert len(result.data.get("fraud_indicators", [])) == 0

    def test_high_amount_fraud_detection(self):
        """Test fraud detection for high-value transactions"""
        high_amount_transaction = self.valid_transaction.copy()
        high_amount_transaction["amount"] = "15000.00"  # Very high amount
        high_amount_transaction["transaction_id"] = "TEST_HIGH_AMOUNT"

        result = self.pipeline.process_record(high_amount_transaction)

        assert result.success
        assert result.data["risk_score"] > 0.7
        assert "HIGH_AMOUNT" in result.data.get("fraud_indicators", [])
        assert result.data["status"] == "FLAGGED"

    def test_velocity_fraud_detection(self):
        """Test velocity-based fraud detection"""
        # Process multiple transactions from same card quickly
        base_transaction = self.valid_transaction.copy()

        # First transaction should pass
        base_transaction["transaction_id"] = "TEST_VEL_001"
        result1 = self.pipeline.process_record(base_transaction)
        assert result1.success
        assert result1.data["status"] == "APPROVED"

        # Subsequent rapid transactions should be flagged
        for i in range(2, 12):  # 10 more transactions (exceeding velocity limit)
            velocity_transaction = base_transaction.copy()
            velocity_transaction["transaction_id"] = f"TEST_VEL_{i:03d}"
            velocity_transaction["amount"] = "100.00"

            result = self.pipeline.process_record(velocity_transaction)

            if i > 10:  # Should start flagging after 10 transactions per hour
                assert result.data["risk_score"] > 0.6
                assert "VELOCITY_EXCEEDED" in result.data.get("fraud_indicators", [])

    def test_suspicious_merchant_detection(self):
        """Test detection of transactions with suspicious merchants"""
        suspicious_transaction = self.valid_transaction.copy()
        suspicious_transaction["merchant_id"] = "MERCH_FRAUD"
        suspicious_transaction["transaction_id"] = "TEST_SUSPICIOUS_MERCHANT"

        result = self.pipeline.process_record(suspicious_transaction)

        assert result.success
        assert result.data["risk_score"] > 0.8
        assert "SUSPICIOUS_MERCHANT" in result.data.get("fraud_indicators", [])
        assert result.data["status"] == "FLAGGED"

    def test_geographic_anomaly_detection(self):
        """Test geographic anomaly detection"""
        # First transaction from New York
        ny_transaction = self.valid_transaction.copy()
        ny_transaction["transaction_id"] = "TEST_GEO_001"
        ny_transaction["ip_address"] = "192.168.1.100"  # New York IP

        result1 = self.pipeline.process_record(ny_transaction)
        assert result1.success

        # Immediate transaction from different location
        ca_transaction = self.valid_transaction.copy()
        ca_transaction["transaction_id"] = "TEST_GEO_002"
        ca_transaction["ip_address"] = "10.0.0.1"  # California IP
        ca_transaction["billing_address"] = (
            '{"street": "456 Oak Ave", "city": "Los Angeles", "state": "CA", "zip": "90210"}'
        )

        result2 = self.pipeline.process_record(ca_transaction)

        # Should detect impossible travel
        assert result2.data["risk_score"] > 0.6
        assert "GEOGRAPHIC_ANOMALY" in result2.data.get("fraud_indicators", [])

    def test_card_testing_pattern_detection(self):
        """Test detection of card testing patterns (multiple small amounts)"""
        base_transaction = self.valid_transaction.copy()

        # Multiple small transactions that look like card testing
        for i in range(1, 21):  # 20 small transactions
            test_transaction = base_transaction.copy()
            test_transaction["transaction_id"] = f"TEST_CARDTEST_{i:03d}"
            test_transaction["amount"] = f"{i}.00"  # Small, sequential amounts

            result = self.pipeline.process_record(test_transaction)

            if i > 15:  # Should detect pattern after several attempts
                assert result.data["risk_score"] > 0.7
                assert "CARD_TESTING_PATTERN" in result.data.get("fraud_indicators", [])

    def test_expired_card_validation(self):
        """Test validation of expired cards"""
        expired_card_transaction = self.valid_transaction.copy()
        expired_card_transaction["card_expiry"] = "01/20"  # Expired card
        expired_card_transaction["transaction_id"] = "TEST_EXPIRED"

        result = self.pipeline.process_record(expired_card_transaction)

        assert not result.success
        assert "EXPIRED_CARD" in result.errors
        assert result.data.get("decline_reason") == "EXPIRED_CARD"

    def test_invalid_luhn_check(self):
        """Test Luhn algorithm validation for card numbers"""
        invalid_card_transaction = self.valid_transaction.copy()
        invalid_card_transaction["card_number"] = "4532015112830367"  # Invalid Luhn
        invalid_card_transaction["transaction_id"] = "TEST_INVALID_LUHN"

        result = self.pipeline.process_record(invalid_card_transaction)

        assert not result.success
        assert "INVALID_CARD_NUMBER" in result.errors

    def test_restricted_mcc_detection(self):
        """Test detection of restricted merchant category codes"""
        restricted_mcc_transaction = self.valid_transaction.copy()
        restricted_mcc_transaction["merchant_category_code"] = (
            "6010"  # Money transfer (restricted)
        )
        restricted_mcc_transaction["transaction_id"] = "TEST_RESTRICTED_MCC"

        result = self.pipeline.process_record(restricted_mcc_transaction)

        assert result.success  # May still process but flagged
        assert result.data["risk_score"] > 0.8
        assert "RESTRICTED_MCC" in result.data.get("fraud_indicators", [])
        assert result.data["status"] == "FLAGGED"

    def test_weekend_high_amount_detection(self):
        """Test detection of high amounts during weekends"""
        # Mock weekend transaction
        weekend_transaction = self.valid_transaction.copy()
        weekend_transaction["amount"] = "5000.00"
        weekend_transaction["transaction_id"] = "TEST_WEEKEND_HIGH"

        # This would need the pipeline to calculate if it's weekend
        # For now, we'll simulate the expected behavior
        result = self.pipeline.process_record(weekend_transaction)

        # During weekends, high amounts should have higher risk scores
        assert result.success
        if self._is_weekend():
            assert result.data["risk_score"] > 0.6
            assert "WEEKEND_HIGH_AMOUNT" in result.data.get("fraud_indicators", [])

    def test_device_fingerprint_anomaly(self):
        """Test device fingerprint anomaly detection"""
        # First transaction with known device
        known_device_transaction = self.valid_transaction.copy()
        known_device_transaction["transaction_id"] = "TEST_DEVICE_001"

        result1 = self.pipeline.process_record(known_device_transaction)
        assert result1.success

        # Second transaction with different device but same card
        unknown_device_transaction = self.valid_transaction.copy()
        unknown_device_transaction["device_fingerprint"] = "fp_unknown_device"
        unknown_device_transaction["transaction_id"] = "TEST_DEVICE_002"

        result2 = self.pipeline.process_record(unknown_device_transaction)

        # Should flag as suspicious device change
        assert result2.data["risk_score"] > 0.5
        assert "DEVICE_ANOMALY" in result2.data.get("fraud_indicators", [])

    def _is_weekend(self):
        """Helper method to check if current day is weekend"""
        return datetime.now().weekday() >= 5  # Saturday = 5, Sunday = 6


class TestFraudRules:
    """Test individual fraud detection rules"""

    def test_amount_threshold_rule(self):
        """Test amount-based fraud rule"""
        from payment_processing.fraud_rules import AmountThresholdRule

        rule = AmountThresholdRule(threshold=1000.00)

        # Normal amount
        normal_transaction = {"amount": 50.00}
        assert rule.evaluate(normal_transaction).risk_score < 0.5

        # High amount
        high_transaction = {"amount": 5000.00}
        result = rule.evaluate(high_transaction)
        assert result.risk_score > 0.7
        assert "HIGH_AMOUNT" in result.indicators

    def test_velocity_rule(self):
        """Test velocity-based fraud rule"""
        from payment_processing.fraud_rules import VelocityRule

        rule = VelocityRule(max_transactions=5, time_window=3600)  # 5 per hour

        # Simulate transaction history
        transaction_history = [
            {"timestamp": datetime.now() - timedelta(minutes=10)},
            {"timestamp": datetime.now() - timedelta(minutes=20)},
            {"timestamp": datetime.now() - timedelta(minutes=30)},
            {"timestamp": datetime.now() - timedelta(minutes=40)},
            {"timestamp": datetime.now() - timedelta(minutes=50)},
        ]

        current_transaction = {
            "card_number": "4532015112830366",
            "timestamp": datetime.now(),
        }

        result = rule.evaluate(current_transaction, history=transaction_history)
        assert result.risk_score > 0.8
        assert "VELOCITY_EXCEEDED" in result.indicators

    def test_geographic_rule(self):
        """Test geographic anomaly detection rule"""
        from payment_processing.fraud_rules import GeographicRule

        rule = GeographicRule(max_distance_miles=500, time_threshold_minutes=30)

        # Previous transaction in New York
        previous_transaction = {
            "ip_address": "192.168.1.100",  # New York
            "timestamp": datetime.now() - timedelta(minutes=5),
        }

        # Current transaction in California (impossible travel)
        current_transaction = {
            "ip_address": "10.0.0.1",  # California
            "timestamp": datetime.now(),
        }

        result = rule.evaluate(current_transaction, previous=previous_transaction)
        assert result.risk_score > 0.9
        assert "IMPOSSIBLE_TRAVEL" in result.indicators


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