"""Tests for the Honeypot MCP Server."""

from __future__ import annotations

import json
import urllib.request
import time

import pytest

from navil.honeypot.server import HoneypotMCPServer, HoneypotRecord
from navil.honeypot.collector import HoneypotCollector


# ── HoneypotRecord ───────────────────────────────────────────


class TestHoneypotRecord:
    """Tests for HoneypotRecord."""

    def test_creation(self):
        record = HoneypotRecord(
            tool_name="read_file",
            arguments={"path": "/etc/passwd"},
            source_ip="192.168.1.1",
            request_headers={"Content-Type": "application/json"},
        )
        assert record.tool_name == "read_file"
        assert record.source_ip == "192.168.1.1"
        assert record.timestamp != ""

    def test_to_dict(self):
        record = HoneypotRecord(
            tool_name="exec_command",
            arguments={"cmd": "ls"},
            source_ip="10.0.0.1",
            request_headers={},
        )
        d = record.to_dict()
        assert d["tool_name"] == "exec_command"
        assert "timestamp" in d
        assert "source_ip" in d


# ── HoneypotMCPServer ───────────────────────────────────────


class TestHoneypotMCPServer:
    """Tests for the honeypot server."""

    def test_load_dev_tools_profile(self):
        server = HoneypotMCPServer(profile="dev_tools")
        assert len(server.tools) > 0
        assert "read_file" in server.tools

    def test_load_cloud_creds_profile(self):
        server = HoneypotMCPServer(profile="cloud_creds")
        assert "get_aws_config" in server.tools

    def test_load_db_admin_profile(self):
        server = HoneypotMCPServer(profile="db_admin")
        assert "query_db" in server.tools

    def test_tool_list(self):
        server = HoneypotMCPServer(profile="dev_tools")
        tools = server.tool_list
        assert len(tools) >= 5
        names = [t["name"] for t in tools]
        assert "read_file" in names

    def test_handle_tools_list(self):
        server = HoneypotMCPServer(profile="dev_tools")
        response = server.handle_request(
            {"jsonrpc": "2.0", "id": 1, "method": "tools/list"},
            "127.0.0.1",
            {},
        )
        assert response["jsonrpc"] == "2.0"
        assert response["id"] == 1
        assert "tools" in response["result"]

    def test_handle_tools_call(self):
        server = HoneypotMCPServer(profile="dev_tools")
        response = server.handle_request(
            {
                "jsonrpc": "2.0",
                "id": 2,
                "method": "tools/call",
                "params": {"name": "read_file", "arguments": {"path": "/etc/passwd"}},
            },
            "192.168.1.1",
            {"Content-Type": "application/json"},
        )
        assert response["jsonrpc"] == "2.0"
        assert "result" in response

    def test_handle_unknown_method(self):
        server = HoneypotMCPServer(profile="dev_tools")
        response = server.handle_request(
            {"jsonrpc": "2.0", "id": 3, "method": "unknown/method"},
            "127.0.0.1",
            {},
        )
        assert "error" in response
        assert response["error"]["code"] == -32601

    def test_interactions_are_recorded(self):
        server = HoneypotMCPServer(profile="dev_tools")
        server.handle_request(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "tools/call",
                "params": {"name": "read_file", "arguments": {"path": "/tmp/test"}},
            },
            "10.0.0.1",
            {},
        )
        records = server.records
        assert len(records) == 1
        assert records[0].tool_name == "read_file"
        assert records[0].source_ip == "10.0.0.1"

    def test_custom_profile_dict(self):
        custom = {
            "my_tool": {
                "description": "Custom tool",
                "response": {"status": "ok"},
            }
        }
        server = HoneypotMCPServer(profile=custom)
        assert "my_tool" in server.tools
        response = server.handle_request(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "tools/call",
                "params": {"name": "my_tool", "arguments": {}},
            },
            "127.0.0.1",
            {},
        )
        assert response["result"] == {"status": "ok"}

    def test_collector_integration(self):
        collector = HoneypotCollector()
        server = HoneypotMCPServer(profile="dev_tools", collector=collector)

        server.handle_request(
            {
                "jsonrpc": "2.0",
                "id": 1,
                "method": "tools/call",
                "params": {"name": "exec_command", "arguments": {"cmd": "whoami"}},
            },
            "172.16.0.1",
            {},
        )

        assert collector.current_count == 1
        records = collector.records
        assert records[0]["tool_name"] == "exec_command"
        assert records[0]["source_ip"] == "172.16.0.1"


# ── HTTP Server Integration ─────────────────────────────────


class TestHoneypotHTTPServer:
    """Integration tests using the actual HTTP server."""

    @pytest.fixture
    def server(self):
        srv = HoneypotMCPServer(profile="dev_tools", host="127.0.0.1", port=0)
        srv.start_background()
        time.sleep(0.1)  # brief wait for server to bind
        yield srv
        srv.stop()

    def test_http_tools_list(self, server):
        req_body = json.dumps({"jsonrpc": "2.0", "id": 1, "method": "tools/list"}).encode()
        req = urllib.request.Request(
            server.url,
            data=req_body,
            headers={"Content-Type": "application/json"},
            method="POST",
        )
        with urllib.request.urlopen(req) as resp:
            data = json.loads(resp.read())
            assert "result" in data
            assert "tools" in data["result"]

    def test_http_tools_call(self, server):
        req_body = json.dumps({
            "jsonrpc": "2.0",
            "id": 2,
            "method": "tools/call",
            "params": {"name": "read_file", "arguments": {"path": "/tmp/test"}},
        }).encode()
        req = urllib.request.Request(
            server.url,
            data=req_body,
            headers={"Content-Type": "application/json"},
            method="POST",
        )
        with urllib.request.urlopen(req) as resp:
            data = json.loads(resp.read())
            assert "result" in data

    def test_context_manager(self):
        with HoneypotMCPServer(profile="dev_tools", host="127.0.0.1", port=0) as srv:
            assert srv.port > 0
            req_body = json.dumps({"jsonrpc": "2.0", "id": 1, "method": "tools/list"}).encode()
            req = urllib.request.Request(
                srv.url,
                data=req_body,
                headers={"Content-Type": "application/json"},
                method="POST",
            )
            with urllib.request.urlopen(req) as resp:
                data = json.loads(resp.read())
                assert "result" in data


# ── HoneypotCollector ────────────────────────────────────────


class TestHoneypotCollector:
    """Tests for the collector."""

    def test_record_and_retrieve(self):
        collector = HoneypotCollector()
        record = HoneypotRecord(
            tool_name="test_tool",
            arguments={"key": "value"},
            source_ip="1.2.3.4",
            request_headers={},
        )
        collector.record(record)
        assert collector.current_count == 1
        assert collector.count == 1

    def test_max_records(self):
        collector = HoneypotCollector(max_records=5)
        record = HoneypotRecord("tool", {}, "1.2.3.4", {})
        for _ in range(10):
            collector.record(record)
        assert collector.current_count == 5
        assert collector.count == 10

    def test_tool_call_counts(self):
        collector = HoneypotCollector()
        for tool in ["read_file", "read_file", "exec_command"]:
            record = HoneypotRecord(tool, {}, "1.2.3.4", {})
            collector.record(record)
        counts = collector.get_tool_call_counts()
        assert counts["read_file"] == 2
        assert counts["exec_command"] == 1

    def test_source_ip_counts(self):
        collector = HoneypotCollector()
        for ip in ["1.2.3.4", "1.2.3.4", "5.6.7.8"]:
            record = HoneypotRecord("tool", {}, ip, {})
            collector.record(record)
        counts = collector.get_source_ip_counts()
        assert counts["1.2.3.4"] == 2
        assert counts["5.6.7.8"] == 1

    def test_export_json(self):
        collector = HoneypotCollector()
        record = HoneypotRecord("test", {"k": "v"}, "1.2.3.4", {})
        collector.record(record)
        exported = collector.export_json()
        parsed = json.loads(exported)
        assert len(parsed) == 1
        assert parsed[0]["tool_name"] == "test"

    def test_clear(self):
        collector = HoneypotCollector()
        record = HoneypotRecord("tool", {}, "1.2.3.4", {})
        for _ in range(5):
            collector.record(record)
        cleared = collector.clear()
        assert cleared == 5
        assert collector.current_count == 0
