"""Tests for ActivityUtils utility functions."""

from cpmf_uips_xaml.platforms.uipath.activities import ActivityUtils


class TestGenerateActivityId:
    """Tests for ActivityUtils.generate_activity_id()."""

    def test_generate_basic_id(self):
        """Test generating basic activity ID."""
        result = ActivityUtils.generate_activity_id(
            project_id="test-project",
            workflow_path="Main.xaml",
            node_id="Activity/Sequence",
            activity_content="<Sequence />",
        )

        assert result.startswith("test-project#Main#Activity/Sequence#")
        assert len(result.split("#")) == 4  # 4 parts separated by #

    def test_generate_id_stable_hash(self):
        """Test that same content generates same hash."""
        content = "<Assign Value='test' />"
        result1 = ActivityUtils.generate_activity_id("proj", "Main.xaml", "node", content)
        result2 = ActivityUtils.generate_activity_id("proj", "Main.xaml", "node", content)

        assert result1 == result2

    def test_generate_id_different_content_different_hash(self):
        """Test that different content generates different hash."""
        result1 = ActivityUtils.generate_activity_id("proj", "Main.xaml", "node", "content1")
        result2 = ActivityUtils.generate_activity_id("proj", "Main.xaml", "node", "content2")

        # Same project, workflow, node but different content hash
        assert result1.split("#")[:3] == result2.split("#")[:3]
        assert result1.split("#")[3] != result2.split("#")[3]

    def test_generate_id_removes_xaml_extension(self):
        """Test that .xaml extension is removed from workflow path."""
        result = ActivityUtils.generate_activity_id(
            "proj", "Workflows/Process.xaml", "node", "content"
        )

        assert "Workflows/Process#" in result
        assert ".xaml" not in result

    def test_generate_id_normalizes_windows_paths(self):
        """Test that Windows backslashes are converted to forward slashes."""
        result = ActivityUtils.generate_activity_id(
            "proj", "Workflows\\Subfolder\\Process.xaml", "node", "content"
        )

        assert "Workflows/Subfolder/Process#" in result
        assert "\\" not in result

    def test_generate_id_hash_length(self):
        """Test that content hash is 8 characters."""
        result = ActivityUtils.generate_activity_id("proj", "Main.xaml", "node", "content")

        hash_part = result.split("#")[-1]
        assert len(hash_part) == 8

    def test_generate_id_complex_example(self):
        """Test complex real-world example."""
        result = ActivityUtils.generate_activity_id(
            project_id="frozenchlorine-1082950b",
            workflow_path="Process\\Calculator\\ClickListOfCharacters.xaml",
            node_id="Activity/Sequence/ForEach/Sequence/NApplicationCard/Sequence/If/Sequence/NClick",
            activity_content="<ui:Click Selector='...' />",
        )

        assert result.startswith("frozenchlorine-1082950b#")
        assert "Process/Calculator/ClickListOfCharacters#" in result
        assert ".xaml" not in result


class TestExtractExpressionsFromText:
    """Tests for ActivityUtils.extract_expressions_from_text()."""

    def test_extract_from_empty_text(self):
        """Test extracting from empty text."""
        result = ActivityUtils.extract_expressions_from_text("")
        assert result == []

    def test_extract_from_none(self):
        """Test extracting from None."""
        result = ActivityUtils.extract_expressions_from_text(None)
        assert result == []

    def test_extract_vb_bracket_expressions(self):
        """Test extracting VB.NET bracket expressions."""
        text = "Set value to [variableName] and [otherVariable]"
        result = ActivityUtils.extract_expressions_from_text(text)

        assert "variableName" in result
        assert "otherVariable" in result

    def test_extract_method_calls(self):
        """Test extracting method calls."""
        text = "Call DateTime.Now() and String.Format() methods"
        result = ActivityUtils.extract_expressions_from_text(text)

        assert "DateTime.Now()" in result
        assert "String.Format()" in result

    def test_extract_string_format_calls(self):
        """Test extracting String.Format calls."""
        text = 'Use string.Format("Hello {0}", name) for formatting'
        result = ActivityUtils.extract_expressions_from_text(text)

        assert any("string.Format" in expr for expr in result)

    def test_extract_removes_duplicates(self):
        """Test that duplicate expressions are removed."""
        text = "[variable] and [variable] and [variable]"
        result = ActivityUtils.extract_expressions_from_text(text)

        assert len(result) == 1
        assert "variable" in result

    def test_extract_mixed_expressions(self):
        """Test extracting mixed expression types."""
        text = "[myVar] uses obj.Method() and string.Format('{0}', val)"
        result = ActivityUtils.extract_expressions_from_text(text)

        assert len(result) >= 2  # At least bracket and method call

    def test_extract_complex_bracket_expression(self):
        """Test extracting complex bracket expression."""
        text = "[DateTime.Now.ToString('yyyy-MM-dd')]"
        result = ActivityUtils.extract_expressions_from_text(text)

        assert any("DateTime.Now.ToString" in expr for expr in result)

    def test_extract_no_expressions(self):
        """Test text without expressions returns empty list."""
        text = "Just plain text without any expressions"
        result = ActivityUtils.extract_expressions_from_text(text)

        # Might be empty or have minimal matches depending on implementation
        assert isinstance(result, list)


class TestExtractVariableReferences:
    """Tests for ActivityUtils.extract_variable_references()."""

    def test_extract_from_empty_text(self):
        """Test extracting from empty text."""
        result = ActivityUtils.extract_variable_references("")
        assert result == []

    def test_extract_from_none(self):
        """Test extracting from None."""
        result = ActivityUtils.extract_variable_references(None)
        assert result == []

    def test_extract_bracket_variables(self):
        """Test extracting variables from brackets."""
        text = "[myVariable]"
        result = ActivityUtils.extract_variable_references(text)

        assert "myVariable" in result

    def test_extract_property_access_variables(self):
        """Test extracting variables from property access."""
        text = "myVariable.Property"
        result = ActivityUtils.extract_variable_references(text)

        assert "myVariable" in result

    def test_extract_method_call_variables(self):
        """Test extracting variables from method calls."""
        text = "myVariable.Method()"
        result = ActivityUtils.extract_variable_references(text)

        assert "myVariable" in result

    def test_extract_assignment_variables(self):
        """Test extracting variables from assignments."""
        text = "myVariable = value"
        result = ActivityUtils.extract_variable_references(text)

        assert "myVariable" in result

    def test_extract_excludes_keywords(self):
        """Test that common keywords are excluded."""
        text = "String.Format(DateTime.Now, New String())"
        result = ActivityUtils.extract_variable_references(text)

        # These should be filtered out
        assert "String" not in result
        assert "DateTime" not in result
        assert "New" not in result

    def test_extract_filters_short_names(self):
        """Test that single-character names are filtered."""
        text = "a = b"
        result = ActivityUtils.extract_variable_references(text)

        # Single characters should be filtered
        assert len(result) == 0 or all(len(var) > 1 for var in result)

    def test_extract_removes_duplicates(self):
        """Test that duplicate variable names are removed."""
        text = "myVar.Property and myVar.Method() and myVar = value"
        result = ActivityUtils.extract_variable_references(text)

        assert result.count("myVar") <= 1  # Should appear at most once

    def test_extract_complex_expression(self):
        """Test extracting from complex expression."""
        text = "If(userInput.Trim().Length > 0, resultData.ToString(), defaultValue)"
        result = ActivityUtils.extract_variable_references(text)

        # Should find variables (may capture partial names due to regex matching)
        # Check that we found some of the key variables
        assert any("userInput" in var or "userInpu" in var for var in result)
        assert any("resultData" in var or "resultDat" in var for var in result)
        assert "defaultValue" in result

    def test_extract_does_not_include_false_positives(self):
        """Test that common false positives are excluded."""
        text = "If(condition, True, False)"
        result = ActivityUtils.extract_variable_references(text)

        assert "True" not in result
        assert "False" not in result
        assert "If" not in result

    def test_extract_with_chained_calls(self):
        """Test extracting from chained method calls."""
        text = "myVariable.Method1().Property.Method2()"
        result = ActivityUtils.extract_variable_references(text)

        assert "myVariable" in result


class TestExtractSelectorsFromConfig:
    """Tests for ActivityUtils.extract_selectors_from_config()."""

    def test_extract_from_empty_config(self):
        """Test extracting from empty configuration."""
        result = ActivityUtils.extract_selectors_from_config({})
        assert result == {}

    def test_extract_full_selector(self):
        """Test extracting FullSelector field."""
        config = {"FullSelector": "<html><body><button /></body></html>"}
        result = ActivityUtils.extract_selectors_from_config(config)

        assert "FullSelector" in result
        assert "<html>" in result["FullSelector"]

    def test_extract_fuzzy_selector(self):
        """Test extracting FuzzySelector field."""
        config = {"FuzzySelector": "<fuzzy accuracy='0.8' />"}
        result = ActivityUtils.extract_selectors_from_config(config)

        assert "FuzzySelector" in result

    def test_extract_nested_selector(self):
        """Test extracting selector from nested structure."""
        config = {"Target": {"Selector": "<html><button /></html>"}}
        result = ActivityUtils.extract_selectors_from_config(config)

        assert "Target.Selector" in result

    def test_extract_multiple_selectors(self):
        """Test extracting multiple selector types."""
        config = {
            "FullSelector": "selector1",
            "FuzzySelector": "selector2",
            "Nested": {"TargetSelector": "selector3"},
        }
        result = ActivityUtils.extract_selectors_from_config(config)

        assert len(result) == 3
        assert "FullSelector" in result
        assert "FuzzySelector" in result
        assert "Nested.TargetSelector" in result

    def test_extract_ignores_non_selector_fields(self):
        """Test that non-selector fields are ignored."""
        config = {
            "FullSelector": "selector",
            "SomeOtherField": "value",
            "DisplayName": "Activity Name",
        }
        result = ActivityUtils.extract_selectors_from_config(config)

        assert len(result) == 1
        assert "FullSelector" in result
        assert "SomeOtherField" not in result

    def test_extract_from_list(self):
        """Test extracting selectors from list in config."""
        config = {"Targets": [{"Selector": "selector1"}, {"Selector": "selector2"}]}
        result = ActivityUtils.extract_selectors_from_config(config)

        assert "Targets[0].Selector" in result
        assert "Targets[1].Selector" in result

    def test_extract_deeply_nested(self):
        """Test extracting from deeply nested structure."""
        config = {"Level1": {"Level2": {"Level3": {"FullSelector": "deep selector"}}}}
        result = ActivityUtils.extract_selectors_from_config(config)

        assert "Level1.Level2.Level3.FullSelector" in result

    def test_extract_only_string_selectors(self):
        """Test that only string selector values are extracted."""
        config = {"FullSelector": "valid", "OtherSelector": 123, "AnotherSelector": None}
        result = ActivityUtils.extract_selectors_from_config(config)

        # Only string values should be extracted
        assert "FullSelector" in result
        assert len(result) == 1


class TestClassifyActivityType:
    """Tests for ActivityUtils.classify_activity_type()."""

    def test_classify_ui_automation_click(self):
        """Test classifying click activities."""
        assert ActivityUtils.classify_activity_type("Click") == "ui_automation"
        assert ActivityUtils.classify_activity_type("NClick") == "ui_automation"

    def test_classify_ui_automation_type(self):
        """Test classifying type activities."""
        assert ActivityUtils.classify_activity_type("TypeInto") == "ui_automation"
        assert ActivityUtils.classify_activity_type("TypeText") == "ui_automation"

    def test_classify_ui_automation_get(self):
        """Test classifying get activities."""
        assert ActivityUtils.classify_activity_type("GetText") == "ui_automation"
        assert ActivityUtils.classify_activity_type("GetValue") == "ui_automation"

    def test_classify_flow_control_sequence(self):
        """Test classifying sequence activities."""
        assert ActivityUtils.classify_activity_type("Sequence") == "flow_control"

    def test_classify_flow_control_if(self):
        """Test classifying If activities."""
        assert ActivityUtils.classify_activity_type("If") == "flow_control"

    def test_classify_flow_control_loops(self):
        """Test classifying loop activities."""
        assert ActivityUtils.classify_activity_type("ForEach") == "flow_control"
        assert ActivityUtils.classify_activity_type("While") == "flow_control"

    def test_classify_flow_control_flowchart(self):
        """Test classifying flowchart activities."""
        assert ActivityUtils.classify_activity_type("Flowchart") == "flow_control"

    def test_classify_data_assign(self):
        """Test classifying Assign activities."""
        assert ActivityUtils.classify_activity_type("Assign") == "data_processing"

    def test_classify_data_invoke(self):
        """Test classifying Invoke activities."""
        assert ActivityUtils.classify_activity_type("InvokeWorkflowFile") == "data_processing"
        assert ActivityUtils.classify_activity_type("InvokeMethod") == "data_processing"

    def test_classify_system_log(self):
        """Test classifying log activities."""
        assert ActivityUtils.classify_activity_type("LogMessage") == "system"

    def test_classify_system_message(self):
        """Test classifying message activities."""
        assert ActivityUtils.classify_activity_type("MessageBox") == "system"

    def test_classify_exception_try_catch(self):
        """Test classifying exception handling activities."""
        assert ActivityUtils.classify_activity_type("TryCatch") == "exception_handling"
        assert ActivityUtils.classify_activity_type("Throw") == "exception_handling"
        assert ActivityUtils.classify_activity_type("Rethrow") == "exception_handling"

    def test_classify_case_insensitive(self):
        """Test that classification is case-insensitive."""
        assert ActivityUtils.classify_activity_type("CLICK") == "ui_automation"
        assert ActivityUtils.classify_activity_type("sequence") == "flow_control"
        assert ActivityUtils.classify_activity_type("Assign") == "data_processing"

    def test_classify_unknown_type(self):
        """Test classifying unknown activity type."""
        assert ActivityUtils.classify_activity_type("CustomActivity") == "other"
        assert ActivityUtils.classify_activity_type("UnknownType") == "other"

    def test_classify_namespaced_activity(self):
        """Test classifying activity with namespace prefix."""
        assert ActivityUtils.classify_activity_type("ui:Click") == "ui_automation"
        assert (
            ActivityUtils.classify_activity_type("System.Activities.Statements.Sequence")
            == "flow_control"
        )
