from __future__ import annotations

import sys
import tempfile
import unittest
from pathlib import Path
from unittest.mock import patch

from memnixa.tools import build_default_tool_registry


class FakeHTTPResponse:
    def __init__(self, body: str, *, url: str, status: int = 200) -> None:
        self._body = body
        self._url = url
        self.status = status
        self.headers = {"Content-Type": "text/plain; charset=utf-8"}

    def read(self) -> bytes:
        return self._body.encode("utf-8")

    def geturl(self) -> str:
        return self._url

    def __enter__(self) -> "FakeHTTPResponse":
        return self

    def __exit__(self, exc_type, exc, tb) -> None:
        return None


class ToolRegistryTests(unittest.TestCase):
    def test_validation_rejects_unexpected_fields(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            workspace = Path(temp_dir)
            (workspace / "hello.txt").write_text("hello", encoding="utf-8")
            registry = build_default_tool_registry(workspace)

            result = registry.execute(
                "read_file",
                {"path": "hello.txt", "unexpected": True},
            )

        self.assertFalse(result.ok)
        self.assertEqual(result.error, {"code": "validation_error", "message": "unexpected fields: 'unexpected'"})

    def test_read_file_returns_paginated_structured_result(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            workspace = Path(temp_dir)
            content = "a" * 2500
            (workspace / "big.txt").write_text(content, encoding="utf-8")
            registry = build_default_tool_registry(workspace)

            result = registry.execute(
                "read_file",
                {"path": "big.txt", "page": 2, "page_size": 1000},
            )

        self.assertTrue(result.ok)
        self.assertEqual(result.data["path"], "big.txt")
        self.assertEqual(result.data["content"], content[1000:2000])
        self.assertEqual(result.meta["pagination"]["page"], 2)
        self.assertEqual(result.meta["pagination"]["total_pages"], 3)
        self.assertTrue(result.meta["pagination"]["has_next_page"])

    def test_run_command_supports_follow_up_pagination_without_rerun(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            workspace = Path(temp_dir)
            registry = build_default_tool_registry(workspace)
            command = (
                f'{sys.executable} -c '
                "\"print(''.join(str(i % 10) for i in range(5000)))\""
            )

            first = registry.execute(
                "run_command",
                {"command": command, "stream": "stdout", "page_size": 1999},
            )

            self.assertTrue(first.ok)
            execution_id = first.data["execution_id"]

            second = registry.execute(
                "run_command",
                {
                    "execution_id": execution_id,
                    "stream": "stdout",
                    "page": 2,
                    "page_size": 1999,
                },
            )

        self.assertTrue(second.ok)
        self.assertEqual(second.data["execution_id"], execution_id)
        self.assertEqual(second.meta["pagination"]["page"], 2)
        self.assertEqual(second.meta["pagination"]["total_pages"], 3)
        self.assertEqual(len(first.data["content"]), 1999)
        self.assertEqual(len(second.data["content"]), 1999)
        self.assertNotEqual(first.data["content"], second.data["content"])

    def test_read_url_fetches_paginated_web_content(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            workspace = Path(temp_dir)
            registry = build_default_tool_registry(workspace)
            captured_request = {}

            def fake_urlopen(request, timeout=0):
                captured_request["url"] = request.full_url
                captured_request["timeout"] = timeout
                return FakeHTTPResponse(
                    "A" * 5000,
                    url=request.full_url,
                )

            with patch("urllib.request.urlopen", side_effect=fake_urlopen):
                result = registry.execute(
                    "read_url",
                    {"url": "example.com/post", "page": 2, "page_size": 2000},
                )

        self.assertTrue(result.ok)
        self.assertEqual(result.data["url"], "https://example.com/post")
        self.assertEqual(len(result.data["content"]), 2000)
        self.assertEqual(result.meta["pagination"]["page"], 2)
        self.assertEqual(result.meta["pagination"]["total_pages"], 3)
        self.assertEqual(
            captured_request["url"],
            "https://r.jina.ai/https://example.com/post",
        )

    def test_search_web_is_registered_without_key_and_returns_config_guidance(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            workspace = Path(temp_dir)
            registry = build_default_tool_registry(workspace)
            schema_names = {
                item["function"]["name"]
                for item in registry.schemas()
            }

            result = registry.execute(
                "search_web",
                {"query": "memnixa project", "page_size": 4000},
            )

        self.assertIn("search_web", schema_names)
        self.assertFalse(result.ok)
        self.assertEqual(result.error["code"], "config_required")
        self.assertIn("jina_api_key", result.error["message"])
        self.assertEqual(result.meta["config_path"], "config.json")
        self.assertEqual(result.meta["env_var"], "JINA_API_KEY")

    def test_search_web_uses_jina_authorization_when_key_is_configured(self) -> None:
        with tempfile.TemporaryDirectory() as temp_dir:
            workspace = Path(temp_dir)
            registry = build_default_tool_registry(workspace, jina_api_key="jina-key")
            schema_names = {
                item["function"]["name"]
                for item in registry.schemas()
            }
            captured_request = {}

            def fake_urlopen(request, timeout=0):
                captured_request["url"] = request.full_url
                captured_request["authorization"] = request.headers.get("Authorization")
                captured_request["timeout"] = timeout
                return FakeHTTPResponse(
                    "Search result body",
                    url=request.full_url,
                )

            with patch("urllib.request.urlopen", side_effect=fake_urlopen):
                result = registry.execute(
                    "search_web",
                    {"query": "memnixa project", "page_size": 4000},
                )

        self.assertIn("search_web", schema_names)
        self.assertTrue(result.ok)
        self.assertEqual(result.data["query"], "memnixa project")
        self.assertEqual(
            captured_request["url"],
            "https://s.jina.ai/memnixa%20project",
        )
        self.assertEqual(captured_request["authorization"], "Bearer jina-key")


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