"""Tests for ToolCallFrequencyAnomalyDetector (Priority #5, OWASP ASI09)."""

import pytest
from insa_its.detectors.tool_call_frequency import ToolCallFrequencyAnomalyDetector


class TestInit:
    def test_creates_detector(self):
        d = ToolCallFrequencyAnomalyDetector()
        assert d.name == "tool_call_frequency"
        assert d.enabled is True

    def test_initial_state(self):
        d = ToolCallFrequencyAnomalyDetector()
        assert len(d._agent_profiles) == 0
        assert len(d._agent_tool_scopes) == 0


class TestToolRegistration:
    def test_register_tool(self):
        d = ToolCallFrequencyAnomalyDetector()
        d.register_tool("read_file", "file_read")
        assert "read_file" in d._tool_metadata


class TestScopeViolation:
    def test_detects_out_of_scope_tool(self):
        d = ToolCallFrequencyAnomalyDetector()
        d.set_agent_scope("bot", ["read_file", "list_dir"])
        result = d.check(1, agent_id="bot", tool_name="delete_database")
        assert result["detected"] is True
        assert "scope_violation" in result["flags"]
        assert result["severity"] in ("high", "critical")

    def test_in_scope_tool_no_violation(self):
        d = ToolCallFrequencyAnomalyDetector()
        d.set_agent_scope("bot", ["read_file", "list_dir"])
        result = d.check(1, agent_id="bot", tool_name="read_file")
        assert "scope_violation" not in result.get("flags", [])

    def test_no_scope_no_violation(self):
        d = ToolCallFrequencyAnomalyDetector()
        result = d.check(1, agent_id="bot", tool_name="anything")
        assert "scope_violation" not in result.get("flags", [])


class TestSuspiciousArguments:
    def test_detects_path_traversal(self):
        d = ToolCallFrequencyAnomalyDetector()
        result = d.check(
            1, agent_id="bot",
            tool_name="read_file",
            tool_args="../../etc/passwd",
        )
        assert result["detected"] is True
        assert "path_traversal" in result["flags"]

    def test_detects_system_directory_access(self):
        d = ToolCallFrequencyAnomalyDetector()
        result = d.check(
            1, agent_id="bot",
            tool_name="read_file",
            tool_args="/etc/shadow",
        )
        assert result["detected"] is True
        assert "system_directory_access" in result["flags"]

    def test_detects_command_injection(self):
        d = ToolCallFrequencyAnomalyDetector()
        result = d.check(
            1, agent_id="bot",
            tool_name="git_diff",
            tool_args="; bash -c 'whoami'",
        )
        assert result["detected"] is True
        assert "command_injection" in result["flags"]
        assert result["severity"] == "critical"

    def test_detects_sql_injection(self):
        d = ToolCallFrequencyAnomalyDetector()
        result = d.check(
            1, agent_id="bot",
            tool_name="db_query",
            tool_args="' OR '1'='1",
        )
        assert result["detected"] is True
        assert "sql_injection" in result["flags"]

    def test_detects_ssrf(self):
        d = ToolCallFrequencyAnomalyDetector()
        result = d.check(
            1, agent_id="bot",
            tool_name="fetch_url",
            tool_args="http://169.254.169.254/latest/meta-data/iam/",
        )
        assert result["detected"] is True
        assert "ssrf_attempt" in result["flags"]

    def test_detects_encoded_payload(self):
        d = ToolCallFrequencyAnomalyDetector()
        result = d.check(
            1, agent_id="bot",
            tool_name="search",
            tool_args="query%2e%2e%2f%2e%2e%2fetc%2fpasswd",
        )
        assert result["detected"] is True
        assert "encoded_payload" in result["flags"]

    def test_clean_args_no_detection(self):
        d = ToolCallFrequencyAnomalyDetector()
        result = d.check(
            1, agent_id="bot",
            tool_name="read_file",
            tool_args="/home/user/documents/report.pdf",
        )
        has_injection_flags = any(
            f in result.get("flags", [])
            for f in ("path_traversal", "command_injection", "sql_injection", "ssrf_attempt")
        )
        assert has_injection_flags is False

    def test_detects_ssh_directory(self):
        d = ToolCallFrequencyAnomalyDetector()
        result = d.check(
            1, agent_id="bot",
            tool_name="git_init",
            tool_args=".ssh/",
        )
        assert result["detected"] is True
        assert "system_directory_access" in result["flags"]


class TestNewToolPostBaseline:
    def test_detects_new_tool_after_baseline(self):
        d = ToolCallFrequencyAnomalyDetector()
        # Warmup with consistent tool usage
        for i in range(d.WARMUP_CALLS):
            d.check(i + 1, agent_id="bot", tool_name="read_file")
        # After baseline, use a completely new tool
        result = d.check(d.WARMUP_CALLS + 1, agent_id="bot", tool_name="exec_shell")
        assert result["detected"] is True
        assert "new_tool_post_baseline" in result["flags"]

    def test_baseline_tool_no_flag(self):
        d = ToolCallFrequencyAnomalyDetector()
        for i in range(d.WARMUP_CALLS):
            d.check(i + 1, agent_id="bot", tool_name="read_file")
        result = d.check(d.WARMUP_CALLS + 1, agent_id="bot", tool_name="read_file")
        assert "new_tool_post_baseline" not in result.get("flags", [])


class TestPerAgentTracking:
    def test_independent_profiles(self):
        d = ToolCallFrequencyAnomalyDetector()
        d.check(1, agent_id="agent_a", tool_name="tool_1")
        d.check(2, agent_id="agent_b", tool_name="tool_2")
        assert "agent_a" in d._agent_profiles
        assert "agent_b" in d._agent_profiles
        assert d._agent_profiles["agent_a"]["total_calls"] == 1
        assert d._agent_profiles["agent_b"]["total_calls"] == 1


class TestToolExtraction:
    def test_extracts_tool_from_text(self):
        d = ToolCallFrequencyAnomalyDetector()
        result = d.check(1, agent_id="bot", text="calling tool 'read_file' with path /docs")
        # Should extract the tool name from text
        assert d._agent_profiles["bot"]["total_calls"] == 1


class TestSeverity:
    def test_command_injection_is_critical(self):
        d = ToolCallFrequencyAnomalyDetector()
        result = d.check(1, agent_id="bot", tool_args="; bash exec 'rm -rf /'")
        assert result["severity"] == "critical"

    def test_scope_violation_is_high(self):
        d = ToolCallFrequencyAnomalyDetector()
        d.set_agent_scope("bot", ["safe_tool"])
        result = d.check(1, agent_id="bot", tool_name="dangerous_tool")
        assert result["severity"] == "high"

    def test_new_tool_is_medium(self):
        d = ToolCallFrequencyAnomalyDetector()
        for i in range(d.WARMUP_CALLS):
            d.check(i + 1, agent_id="bot", tool_name="normal")
        result = d.check(d.WARMUP_CALLS + 1, agent_id="bot", tool_name="unexpected")
        assert result["severity"] == "medium"


class TestReset:
    def test_reset_clears_all(self):
        d = ToolCallFrequencyAnomalyDetector()
        d.register_tool("t1", "cat")
        d.set_agent_scope("bot", ["t1"])
        d.check(1, agent_id="bot", tool_name="t1")
        d.reset()
        assert len(d._agent_profiles) == 0
        assert len(d._agent_tool_scopes) == 0
        assert len(d._tool_metadata) == 0


class TestResultFormat:
    def test_result_has_required_fields(self):
        d = ToolCallFrequencyAnomalyDetector()
        result = d.check(1, agent_id="bot", tool_name="test")
        assert "detected" in result
        assert "anomaly_type" in result
        assert "severity" in result
        assert "flags" in result
        assert "evidence" in result
        assert "timestamp_us" in result
        assert result["anomaly_type"] == "TOOL_CALL_ANOMALY"

    def test_timestamp_is_microseconds(self):
        d = ToolCallFrequencyAnomalyDetector()
        result = d.check(1, agent_id="bot")
        assert result["timestamp_us"] > 1_000_000_000_000
