"""Tests for bot_client_proxy.py — permission-aware Bot API proxy."""

import json
import os
import sys
import tempfile
import unittest
from unittest.mock import patch

sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..", "scripts"))

from bot_client_proxy import send_message


def _create_permission(admin_dir, agent_id, operations=None):
    """Helper: create a minimal permissions.json in admin_dir."""
    os.makedirs(admin_dir, exist_ok=True)
    perms = {
        "version": 1,
        "agents": {
            agent_id: {
                "agent_id": agent_id,
                "display_name": "Test",
                "emoji": "🔧",
                "operations": operations or {
                    "get": True, "search": True,
                    "add": False, "update": False, "delete": False,
                    "visit": False, "visits": False, "stats": False,
                    "remind": False, "export": False, "import": False,
                },
                "scope": {"levels": [], "groups": [], "tags": []},
            }
        },
    }
    with open(os.path.join(admin_dir, "permissions.json"), "w") as f:
        json.dump(perms, f)


def _create_credentials(admin_dir, agent_id, url="http://localhost:8080/bot-api/chat"):
    """Helper: create bot_credentials.json in admin_dir/<agent_id>/."""
    creds_dir = os.path.join(admin_dir, agent_id)
    os.makedirs(creds_dir, exist_ok=True)
    creds = {
        "url": url,
        "secret": "test-secret",
        "remote_agent_id": "intern-helper",
    }
    with open(os.path.join(creds_dir, "bot_credentials.json"), "w") as f:
        json.dump(creds, f)


class TestSendMessagePermissionCheck(unittest.TestCase):
    """Test that send_message checks permissions before sending."""

    def setUp(self):
        self.tmpdir = tempfile.mkdtemp()
        self.admin_dir = os.path.join(self.tmpdir, "admin")
        self.agent_id = "test-agent"

    def tearDown(self):
        import shutil
        shutil.rmtree(self.tmpdir, ignore_errors=True)

    def test_rejects_unknown_agent(self):
        """Agent without permission record should be rejected."""
        os.makedirs(self.admin_dir, exist_ok=True)
        with open(os.path.join(self.admin_dir, "permissions.json"), "w") as f:
            json.dump({"version": 1, "agents": {}}, f)

        result = send_message(
            agent_id=self.agent_id,
            message="hello",
            admin_dir=self.admin_dir,
        )
        self.assertEqual(result["status"], "error")
        self.assertIn("权限记录", result["message"])

    def test_rejects_missing_credentials(self):
        """Agent with permissions but no credentials should be rejected."""
        _create_permission(self.admin_dir, self.agent_id)

        result = send_message(
            agent_id=self.agent_id,
            message="hello",
            admin_dir=self.admin_dir,
        )
        self.assertEqual(result["status"], "error")
        self.assertIn("凭据", result["message"])

    @patch("bot_client_proxy.subprocess.run")
    def test_sends_with_permissions(self, mock_run):
        """Valid agent should send request with senderPermissions attached."""
        _create_permission(self.admin_dir, self.agent_id, {
            "get": True, "search": True, "delete": False,
            "add": False, "update": False,
            "visit": False, "visits": False, "stats": False,
            "remind": False, "export": False, "import": False,
        })
        _create_credentials(self.admin_dir, self.agent_id)

        mock_run.return_value = type("R", (), {
            "returncode": 0,
            "stdout": json.dumps({"reply": "ok", "sessionKey": "sess-1"}),
            "stderr": "",
        })()

        result = send_message(
            agent_id=self.agent_id,
            message="查询客户",
            admin_dir=self.admin_dir,
        )
        self.assertEqual(result["status"], "ok")
        self.assertEqual(result["reply"], "ok")

        call_args = mock_run.call_args[0][0]
        body_json = call_args[-1]
        body = json.loads(body_json)
        self.assertIn("senderPermissions", body)
        self.assertTrue(body["senderPermissions"]["operations"]["get"])
        self.assertFalse(body["senderPermissions"]["operations"]["delete"])
        self.assertEqual(body["senderId"], self.agent_id)
        self.assertEqual(body["agentId"], "intern-helper")

    @patch("bot_client_proxy.subprocess.run")
    def test_stable_chat_id(self, mock_run):
        """chatId should always be agent_id to keep a stable session."""
        _create_permission(self.admin_dir, self.agent_id)
        _create_credentials(self.admin_dir, self.agent_id)

        mock_run.return_value = type("R", (), {
            "returncode": 0,
            "stdout": json.dumps({"reply": "first", "sessionKey": "agent:intern-helper:bot-api:test-agent-api:dm:test-agent"}),
            "stderr": "",
        })()

        send_message(
            agent_id=self.agent_id,
            message="first message",
            admin_dir=self.admin_dir,
        )

        call_args = mock_run.call_args[0][0]
        body = json.loads(call_args[-1])
        self.assertEqual(body["chatId"], self.agent_id)

        mock_run.return_value = type("R", (), {
            "returncode": 0,
            "stdout": json.dumps({"reply": "second", "sessionKey": "agent:intern-helper:bot-api:test-agent-api:dm:test-agent"}),
            "stderr": "",
        })()

        send_message(
            agent_id=self.agent_id,
            message="second message",
            admin_dir=self.admin_dir,
        )

        call_args = mock_run.call_args[0][0]
        body = json.loads(call_args[-1])
        self.assertEqual(body["chatId"], self.agent_id)

    @patch("bot_client_proxy.subprocess.run")
    def test_handles_curl_failure(self, mock_run):
        _create_permission(self.admin_dir, self.agent_id)
        _create_credentials(self.admin_dir, self.agent_id)

        mock_run.return_value = type("R", (), {
            "returncode": 7,
            "stdout": "",
            "stderr": "Connection refused",
        })()

        result = send_message(
            agent_id=self.agent_id,
            message="hello",
            admin_dir=self.admin_dir,
        )
        self.assertEqual(result["status"], "error")
        self.assertIn("curl", result["message"])

    @patch("bot_client_proxy.subprocess.run")
    def test_handles_invalid_json_response(self, mock_run):
        _create_permission(self.admin_dir, self.agent_id)
        _create_credentials(self.admin_dir, self.agent_id)

        mock_run.return_value = type("R", (), {
            "returncode": 0,
            "stdout": "not json",
            "stderr": "",
        })()

        result = send_message(
            agent_id=self.agent_id,
            message="hello",
            admin_dir=self.admin_dir,
        )
        self.assertEqual(result["status"], "error")
        self.assertIn("无法解析", result["message"])

    @patch("bot_client_proxy.subprocess.run")
    def test_handles_timeout(self, mock_run):
        import subprocess
        _create_permission(self.admin_dir, self.agent_id)
        _create_credentials(self.admin_dir, self.agent_id)

        mock_run.side_effect = subprocess.TimeoutExpired(cmd="curl", timeout=120)

        result = send_message(
            agent_id=self.agent_id,
            message="hello",
            admin_dir=self.admin_dir,
        )
        self.assertEqual(result["status"], "error")
        self.assertIn("超时", result["message"])

    @patch("bot_client_proxy.subprocess.run")
    def test_scope_included_in_request(self, mock_run):
        """Sender scope restrictions should be forwarded."""
        os.makedirs(self.admin_dir, exist_ok=True)
        perms = {
            "version": 1,
            "agents": {
                self.agent_id: {
                    "agent_id": self.agent_id,
                    "display_name": "Scoped",
                    "emoji": "🔧",
                    "operations": {"get": True, "search": True},
                    "scope": {"levels": ["normal", "new"], "groups": [], "tags": []},
                }
            },
        }
        with open(os.path.join(self.admin_dir, "permissions.json"), "w") as f:
            json.dump(perms, f)
        _create_credentials(self.admin_dir, self.agent_id)

        mock_run.return_value = type("R", (), {
            "returncode": 0,
            "stdout": json.dumps({"reply": "ok", "sessionKey": ""}),
            "stderr": "",
        })()

        send_message(
            agent_id=self.agent_id,
            message="查询",
            admin_dir=self.admin_dir,
        )

        body = json.loads(mock_run.call_args[0][0][-1])
        self.assertEqual(
            body["senderPermissions"]["scope"]["levels"], ["normal", "new"]
        )


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