"""Tests for ihyee-langchain tools using mocked ihyee SDK."""
from __future__ import annotations

import os
import unittest
from unittest.mock import MagicMock, patch

from ihyee.models import (
    ContentItem,
    ExtractedLink,
    FetchResponse,
    ResponseMeta,
    SearchResponse,
    TextContent,
)

from ihyee_langchain import IhyeeFetchTool, IhyeeRenderTool, IhyeeSearchTool
from ihyee_langchain.formatter import (
    format_fetch_results,
    format_search_results,
)


def _make_search_response(query="test query", n=2):
    items = []
    for i in range(1, n + 1):
        items.append(
            ContentItem(
                type="text",
                url=f"https://example.com/page{i}",
                text=TextContent(
                    full_text=f"Full text content for page {i}.",
                    summary=f"Summary of page {i}.",
                    author=f"Author {i}",
                    date_published=f"2025-01-0{i}",
                    links=[
                        ExtractedLink(
                            url=f"https://example.com/link{i}",
                            text=f"Link {i}",
                        )
                    ],
                ),
            )
        )
    return SearchResponse(
        query=query,
        results_count=n,
        content=items,
        meta=ResponseMeta(search_time_ms=123, pages_fetched=n, content_mode="both"),
    )


def _make_fetch_response(n=1):
    items = []
    for i in range(1, n + 1):
        items.append(
            ContentItem(
                type="text",
                url=f"https://example.com/fetched{i}",
                text=TextContent(
                    full_text=f"Fetched content {i}.",
                    summary=f"Fetched summary {i}.",
                ),
            )
        )
    return FetchResponse(
        content=items,
        meta=ResponseMeta(pages_fetched=n, content_mode="both"),
    )


class TestFormatter(unittest.TestCase):

    def test_format_search_results_basic(self):
        resp = _make_search_response(n=2)
        text = format_search_results(resp)
        self.assertIn("Found 2 results", text)
        self.assertIn("Result 1", text)
        self.assertIn("Result 2", text)
        self.assertIn("https://example.com/page1", text)
        self.assertIn("Summary of page 1", text)
        self.assertIn("Author 1", text)

    def test_format_search_results_empty(self):
        resp = SearchResponse(
            query="nothing", results_count=0, content=[],
            meta=ResponseMeta(content_mode="both"),
        )
        text = format_search_results(resp)
        self.assertIn("No results found", text)

    def test_format_fetch_results_basic(self):
        resp = _make_fetch_response(n=1)
        text = format_fetch_results(resp)
        self.assertIn("Result 1", text)
        self.assertIn("https://example.com/fetched1", text)
        self.assertIn("Fetched summary 1", text)

    def test_format_fetch_results_empty(self):
        resp = FetchResponse(
            content=[], meta=ResponseMeta(content_mode="both"),
        )
        text = format_fetch_results(resp)
        self.assertIn("No content extracted", text)

    def test_format_truncates_long_text(self):
        resp = FetchResponse(
            content=[
                ContentItem(
                    type="text", url="https://example.com/long",
                    text=TextContent(full_text="x" * 5000),
                )
            ],
            meta=ResponseMeta(content_mode="both"),
        )
        text = format_fetch_results(resp)
        self.assertIn("[truncated]", text)
        self.assertLess(len(text), 5000)

    def test_format_error_item(self):
        resp = FetchResponse(
            content=[
                ContentItem(
                    type="text", url="https://example.com/bad",
                    text=TextContent(error="Connection timed out"),
                )
            ],
            meta=ResponseMeta(content_mode="both"),
        )
        text = format_fetch_results(resp)
        self.assertIn("Error: Connection timed out", text)

    def test_format_links(self):
        resp = _make_search_response(n=1)
        text = format_search_results(resp)
        self.assertIn("Links:", text)
        self.assertIn("Link 1", text)


class TestIhyeeSearchTool(unittest.TestCase):

    @patch("ihyee_langchain.tools.Ihyee")
    def test_invoke_calls_search(self, MockIhyee):
        mock_client = MockIhyee.return_value
        mock_client.search.return_value = _make_search_response()

        tool = IhyeeSearchTool(api_key="test-key")
        result = tool.invoke("transformer architecture")

        MockIhyee.assert_called_once_with(api_key="test-key")
        mock_client.search.assert_called_once_with(
            "transformer architecture", max_results=5, content_mode="both",
        )
        self.assertIn("Found 2 results", result)

    @patch("ihyee_langchain.tools.Ihyee")
    def test_custom_max_results(self, MockIhyee):
        mock_client = MockIhyee.return_value
        mock_client.search.return_value = _make_search_response(n=3)

        tool = IhyeeSearchTool(api_key="test-key", max_results=3)
        tool.invoke("query")

        mock_client.search.assert_called_once_with(
            "query", max_results=3, content_mode="both",
        )

    @patch("ihyee_langchain.tools.Ihyee")
    def test_custom_content_mode(self, MockIhyee):
        mock_client = MockIhyee.return_value
        mock_client.search.return_value = _make_search_response()

        tool = IhyeeSearchTool(api_key="test-key", content_mode="text")
        tool.invoke("query")

        mock_client.search.assert_called_once_with(
            "query", max_results=5, content_mode="text",
        )

    @patch.dict(os.environ, {"IHYEE_API_KEY": "env-key"})
    @patch("ihyee_langchain.tools.Ihyee")
    def test_api_key_from_env(self, MockIhyee):
        mock_client = MockIhyee.return_value
        mock_client.search.return_value = _make_search_response()

        tool = IhyeeSearchTool()
        tool.invoke("query")

        MockIhyee.assert_called_once_with(api_key="env-key")

    def test_tool_metadata(self):
        tool = IhyeeSearchTool(api_key="k")
        self.assertEqual(tool.name, "ihyee_search")
        self.assertIn("search", tool.description.lower())


class TestIhyeeFetchTool(unittest.TestCase):

    @patch("ihyee_langchain.tools.Ihyee")
    def test_invoke_single_url(self, MockIhyee):
        mock_client = MockIhyee.return_value
        mock_client.fetch.return_value = _make_fetch_response()

        tool = IhyeeFetchTool(api_key="test-key")
        result = tool.invoke("https://example.com/page1")

        mock_client.fetch.assert_called_once_with(
            ["https://example.com/page1"], content_mode="both",
        )
        self.assertIn("Result 1", result)

    @patch("ihyee_langchain.tools.Ihyee")
    def test_invoke_multiple_urls(self, MockIhyee):
        mock_client = MockIhyee.return_value
        mock_client.fetch.return_value = _make_fetch_response(n=2)

        tool = IhyeeFetchTool(api_key="test-key")
        tool.invoke("https://a.com, https://b.com")

        mock_client.fetch.assert_called_once_with(
            ["https://a.com", "https://b.com"], content_mode="both",
        )

    def test_tool_metadata(self):
        tool = IhyeeFetchTool(api_key="k")
        self.assertEqual(tool.name, "ihyee_fetch")
        self.assertIn("fetch", tool.description.lower())


class TestIhyeeRenderTool(unittest.TestCase):

    @patch("ihyee_langchain.tools.Ihyee")
    def test_invoke_calls_render(self, MockIhyee):
        mock_client = MockIhyee.return_value
        mock_client.render.return_value = _make_fetch_response()

        tool = IhyeeRenderTool(api_key="test-key")
        result = tool.invoke("https://spa.example.com")

        mock_client.render.assert_called_once_with(
            "https://spa.example.com",
            content_mode="both", wait_for="networkidle", timeout_ms=30000,
        )
        self.assertIn("Result 1", result)

    @patch("ihyee_langchain.tools.Ihyee")
    def test_custom_wait_for(self, MockIhyee):
        mock_client = MockIhyee.return_value
        mock_client.render.return_value = _make_fetch_response()

        tool = IhyeeRenderTool(
            api_key="test-key", wait_for="domcontentloaded", timeout_ms=15000,
        )
        tool.invoke("https://spa.example.com")

        mock_client.render.assert_called_once_with(
            "https://spa.example.com",
            content_mode="both", wait_for="domcontentloaded", timeout_ms=15000,
        )

    def test_tool_metadata(self):
        tool = IhyeeRenderTool(api_key="k")
        self.assertEqual(tool.name, "ihyee_render")
        self.assertIn("render", tool.description.lower())


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