#!/usr/bin/env python3


import pytest
import pandas as pd
import tempfile
import os
import sys
import unittest
from unittest.mock import Mock, patch

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

# Import the classes to test
from excel_filter.filter import ExcelFilter
from excel_filter.gui_components.config_tab import ConfigTab
from excel_filter.translations import Translations


class TestNumericFilter:
    """Test cases for numeric filtering functionality"""

    def setup_method(self):
        """Set up test fixtures"""
        # Create test data
        self.test_data = {
            'Name': ['Alice', 'Bob', 'Charlie', 'David', 'Eve'],
            'Age': [25, 30, 35, 40, 45],
            'Salary': [50000.50, 60000.75, 70000.25, 80000.00, 90000.90],
            'Score': [85.5, 92.0, 78.3, 96.7, 88.1],
            'Department': ['HR', 'IT', 'Finance', 'IT', 'HR']
        }
        self.df = pd.DataFrame(self.test_data)

    def test_numeric_filter_greater_than(self):
        """Test filtering values greater than a threshold"""
        numeric_filter = {'column': 'Age', 'operator': '>', 'value': 30}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        result = excel_filter._apply_numeric_filter(self.df)

        expected_ages = [35, 40, 45]  # Ages > 30
        assert len(result) == 3
        assert result['Age'].tolist() == expected_ages

    def test_numeric_filter_less_than(self):
        """Test filtering values less than a threshold"""
        numeric_filter = {'column': 'Salary', 'operator': '<', 'value': 70000.00}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        result = excel_filter._apply_numeric_filter(self.df)

        expected_salaries = [50000.50, 60000.75]  # Salaries < 70000
        assert len(result) == 2
        assert result['Salary'].tolist() == expected_salaries

    def test_numeric_filter_greater_equal(self):
        """Test filtering values greater than or equal to a threshold"""
        numeric_filter = {'column': 'Score', 'operator': '>=', 'value': 88.1}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        result = excel_filter._apply_numeric_filter(self.df)

        expected_scores = [92.0, 96.7, 88.1]  # Scores >= 88.1
        assert len(result) == 3
        assert result['Score'].tolist() == expected_scores

    def test_numeric_filter_less_equal(self):
        """Test filtering values less than or equal to a threshold"""
        numeric_filter = {'column': 'Age', 'operator': '<=', 'value': 30}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        result = excel_filter._apply_numeric_filter(self.df)

        expected_ages = [25, 30]  # Ages <= 30
        assert len(result) == 2
        assert result['Age'].tolist() == expected_ages

    def test_numeric_filter_equal(self):
        """Test filtering values equal to a specific value"""
        numeric_filter = {'column': 'Age', 'operator': '=', 'value': 35}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        result = excel_filter._apply_numeric_filter(self.df)

        assert len(result) == 1
        assert result['Age'].iloc[0] == 35
        assert result['Name'].iloc[0] == 'Charlie'

    def test_numeric_filter_combined_with_regex(self):
        """Test combining numeric filter with regex filter"""
        numeric_filter = {'column': 'Age', 'operator': '>', 'value': 30}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            pattern=r'HR',  # Regex pattern for HR department
            numeric_filter=numeric_filter
        )

        result = excel_filter.filter_dataframe(self.df)

        # Should find people in HR department who are older than 30
        # Alice (25, HR) - too young
        # Eve (45, HR) - matches both criteria
        assert len(result) == 1
        assert result['Name'].iloc[0] == 'Eve'
        assert result['Age'].iloc[0] == 45
        assert result['Department'].iloc[0] == 'HR'

    def test_numeric_filter_no_matches(self):
        """Test numeric filter that matches no rows"""
        numeric_filter = {'column': 'Age', 'operator': '>', 'value': 100}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        result = excel_filter._apply_numeric_filter(self.df)

        assert len(result) == 0
        assert result.shape[0] == 0

    def test_numeric_filter_invalid_column(self):
        """Test numeric filter with non-existent column"""
        numeric_filter = {'column': 'NonExistentColumn', 'operator': '>', 'value': 30}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        with pytest.raises(ValueError, match="Spalte 'NonExistentColumn' existiert nicht"):
            excel_filter._apply_numeric_filter(self.df)

    def test_numeric_filter_invalid_operator(self):
        """Test numeric filter with invalid operator"""
        numeric_filter = {'column': 'Age', 'operator': 'invalid', 'value': 30}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        with pytest.raises(ValueError, match="Unbekannter Operator"):
            excel_filter._apply_numeric_filter(self.df)

    def test_numeric_filter_non_numeric_values(self):
        """Test numeric filter on column with non-numeric values"""
        # Add a column with mixed data types
        df_mixed = self.df.copy()
        df_mixed['Mixed'] = ['text', 25, 30.5, '45', None]

        numeric_filter = {'column': 'Mixed', 'operator': '>', 'value': 25}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        result = excel_filter._apply_numeric_filter(df_mixed)

        # pandas to_numeric with errors='coerce' converts '45' to 45.0
        # So we get both 30.5 and 45.0 as matches for > 25
        assert len(result) == 2

        # Check that we have the expected numeric values (ignoring non-numeric)
        numeric_values = []
        for val in result['Mixed']:
            try:
                numeric_values.append(float(val))
            except (ValueError, TypeError):
                continue  # Skip non-numeric values

        assert sorted(numeric_values) == [30.5, 45.0]

    def test_numeric_filter_with_decimals(self):
        """Test numeric filter with decimal values"""
        numeric_filter = {'column': 'Salary', 'operator': '>', 'value': 65000.50}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        result = excel_filter._apply_numeric_filter(self.df)

        expected_salaries = [70000.25, 80000.00, 90000.90]  # Salaries > 65000.50
        assert len(result) == 3
        assert result['Salary'].tolist() == expected_salaries

    def test_numeric_filter_boundary_values(self):
        """Test numeric filter with boundary values"""
        # Test exactly equal to boundary
        numeric_filter = {'column': 'Score', 'operator': '=', 'value': 88.1}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        result = excel_filter._apply_numeric_filter(self.df)

        assert len(result) == 1
        assert result['Score'].iloc[0] == 88.1

        # Test greater than with boundary
        numeric_filter = {'column': 'Score', 'operator': '>', 'value': 88.1}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        result = excel_filter._apply_numeric_filter(self.df)

        expected_scores = [92.0, 96.7]  # Scores > 88.1
        assert len(result) == 2
        assert result['Score'].tolist() == expected_scores

    def test_numeric_filter_all_columns(self):
        """Test numeric filter applied to all columns"""
        # Test filtering across all columns for values > 30
        numeric_filter = {'column': 'Alle Spalten', 'operator': '>', 'value': 30}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        result = excel_filter._apply_numeric_filter(self.df)

        # Should find rows where ANY column has a value > 30
        # All rows have Salary and Score values > 30, so all rows match
        assert len(result) == 5  # All rows match

        # Verify all rows are included
        names = sorted(result['Name'].tolist())
        assert names == ['Alice', 'Bob', 'Charlie', 'David', 'Eve']

    def test_numeric_filter_all_columns_less_than(self):
        """Test numeric filter applied to all columns with less than"""
        # Test filtering across all columns for values < 30
        numeric_filter = {'column': 'Alle Spalten', 'operator': '<', 'value': 30}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        result = excel_filter._apply_numeric_filter(self.df)

        # Should find rows where ANY column has a value < 30
        # Row 0: Age=25 (Age < 30)
        # Row 1: Age=30 (Age = 30, not < 30), but Score=92.0? Wait, let me check data
        # Actually, looking at the data: Row 0 has Age=25 (< 30), others don't have values < 30
        # Row 4 has Score=88.1 but that's not < 30
        # So only Row 0 should match
        assert len(result) == 1
        assert result['Age'].iloc[0] == 25
        assert result['Name'].iloc[0] == 'Alice'

    def test_numeric_filter_all_columns_no_matches(self):
        """Test numeric filter on all columns with no matches"""
        numeric_filter = {'column': 'Alle Spalten', 'operator': '>', 'value': 100000}

        excel_filter = ExcelFilter(
            input_file=None, output_file=None,
            numeric_filter=numeric_filter
        )

        result = excel_filter._apply_numeric_filter(self.df)

        assert len(result) == 0


class TestNumericFilterLogic:
    """Test cases for numeric filter logic without UI dependencies"""

    def setup_method(self):
        """Set up test fixtures"""
        self.translations = Translations()

    @pytest.mark.parametrize("display_op,expected_op", [
        ("> (größer als)", ">"),
        ("< (kleiner als)", "<"),
        (">= (größer/gleich)", ">="),
        ("<= (kleiner/gleich)", "<="),
        ("= (gleich)", "=")
    ])
    def test_numeric_filter_settings_conversion(self, display_op, expected_op):
        """Test conversion of UI display values to filter settings"""
        # Create operator mapping dict
        operator_map = {
            "> (größer als)": ">",
            "< (kleiner als)": "<",
            ">= (größer/gleich)": ">=",
            "<= (kleiner/gleich)": "<=",
            "= (gleich)": "="
        }

        # Simulate the logic from get_numeric_filter_settings method
        enabled = True
        column = 'TestColumn'
        operator = display_op
        value = '123.45'

        # Test the logic
        result = (
            None if not enabled
            else {
                'column': column,
                'operator': operator_map.get(operator, operator),
                'value': float(value)
            } if column and operator and value
            else None
        )

        assert result is not None
        assert result['operator'] == expected_op
        assert result['column'] == 'TestColumn'
        assert result['value'] == 123.45


class TestNumericFilterIntegration:
    """Integration tests for numeric filtering with full workflow"""

    def setup_method(self):
        """Set up test fixtures"""
        self.test_data = {
            'Product': ['Widget A', 'Widget B', 'Widget C', 'Widget D', 'Widget E'],
            'Price': [10.99, 25.50, 15.75, 30.00, 8.25],
            'Stock': [100, 250, 75, 300, 50],
            'Rating': [4.2, 4.8, 3.9, 4.9, 3.5],
            'Category': ['Electronics', 'Tools', 'Electronics', 'Tools', 'Accessories']
        }

    def test_full_workflow_regex_and_numeric(self):
        """Test complete workflow with both regex and numeric filtering"""
        # Create test Excel file
        df = pd.DataFrame(self.test_data)

        with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as temp_input:
            with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as temp_output:
                input_file = temp_input.name
                output_file = temp_output.name

                try:
                    # Save test data to input file
                    df.to_excel(input_file, index=False)

                    # Apply filters: Category contains 'Tool' AND Price > 20
                    numeric_filter = {'column': 'Price', 'operator': '>', 'value': 20.0}

                    excel_filter = ExcelFilter(
                        input_file=input_file,
                        output_file=output_file,
                        pattern=r'Tool',  # Regex for category
                        numeric_filter=numeric_filter
                    )

                    success = excel_filter.process()
                    assert success

                    # Load and verify results
                    result_df = pd.read_excel(output_file)

                    # Should find Widget B (Tools, $25.50) and Widget D (Tools, $30.00)
                    assert len(result_df) == 2
                    assert 'Widget B' in result_df['Product'].tolist()
                    assert 'Widget D' in result_df['Product'].tolist()

                    # Verify prices are > 20
                    for price in result_df['Price']:
                        assert price > 20.0

                finally:
                    # Clean up
                    try:
                        os.unlink(input_file)
                        os.unlink(output_file)
                    except:
                        pass

    def test_numeric_filter_only(self):
        """Test numeric filtering without regex"""
        df = pd.DataFrame(self.test_data)

        with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as temp_input:
            with tempfile.NamedTemporaryFile(suffix='.xlsx', delete=False) as temp_output:
                input_file = temp_input.name
                output_file = temp_output.name

                try:
                    # Save test data
                    df.to_excel(input_file, index=False)

                    # Filter: Stock >= 100
                    numeric_filter = {'column': 'Stock', 'operator': '>=', 'value': 100}

                    excel_filter = ExcelFilter(
                        input_file=input_file,
                        output_file=output_file,
                        numeric_filter=numeric_filter
                    )

                    success = excel_filter.process()
                    assert success

                    # Verify results
                    result_df = pd.read_excel(output_file)

                    # Should find Widget A (100), Widget B (250), Widget D (300)
                    assert len(result_df) == 3
                    for stock in result_df['Stock']:
                        assert stock >= 100

                finally:
                    try:
                        os.unlink(input_file)
                        os.unlink(output_file)
                    except:
                        pass


if __name__ == '__main__':
    unittest.main()
