"""Tests for development utilities."""

import tempfile
import unittest
from pathlib import Path

from pygeai_orchestration.core.base import BasePattern, PatternResult, PatternType
from pygeai_orchestration.dev.debug import DebugTracer, PatternInspector
from pygeai_orchestration.dev.templates import PatternTemplate, TemplateGenerator
from pygeai_orchestration.dev.testing import (
    AsyncMockHelper,
    MockAgent,
    MockPattern,
    PatternTestCase,
    TestDataBuilder,
)


class TestTemplateGenerator(unittest.TestCase):
    def test_generate_reflection_pattern(self):
        code = TemplateGenerator.generate_reflection_pattern(
            "MyReflection",
            "Test reflection"
        )
        self.assertIn("MyReflection", code)
        self.assertIn("ReflectionPattern", code)
        self.assertIn("reflect", code)
        self.assertIn("improve", code)

    def test_generate_react_pattern(self):
        code = TemplateGenerator.generate_react_pattern(
            "MyReAct",
            "Test ReAct"
        )
        self.assertIn("MyReAct", code)
        self.assertIn("ReActPattern", code)
        self.assertIn("think", code)
        self.assertIn("act", code)

    def test_generate_custom_pattern(self):
        code = TemplateGenerator.generate_custom_pattern(
            "MyCustom",
            "Test custom"
        )
        self.assertIn("MyCustom", code)
        self.assertIn("OrchestrationPattern", code)
        self.assertIn("_execute_impl", code)

    def test_generate_test_file(self):
        code = TemplateGenerator.generate_test_file(
            "MyPattern",
            PatternTemplate.REFLECTION
        )
        self.assertIn("TestMyPattern", code)
        self.assertIn("PatternTestCase", code)
        self.assertIn("test_basic_execution", code)

    def test_save_template(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            output_path = Path(tmpdir) / "test_pattern.py"
            code = "# Test code"

            TemplateGenerator.save_template(code, output_path)

            self.assertTrue(output_path.exists())
            self.assertEqual(output_path.read_text(), code)

    def test_save_template_no_overwrite(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            output_path = Path(tmpdir) / "test_pattern.py"
            output_path.write_text("existing")

            with self.assertRaises(FileExistsError):
                TemplateGenerator.save_template("new", output_path)

    def test_save_template_overwrite(self):
        with tempfile.TemporaryDirectory() as tmpdir:
            output_path = Path(tmpdir) / "test_pattern.py"
            output_path.write_text("existing")

            TemplateGenerator.save_template("new", output_path, overwrite=True)

            self.assertEqual(output_path.read_text(), "new")

    def test_create_pattern(self):
        files = TemplateGenerator.create_pattern(
            PatternTemplate.REFLECTION,
            "TestPattern",
            "Test description",
            include_tests=True
        )

        self.assertEqual(len(files), 2)
        self.assertIn("testpattern.py", files)
        self.assertIn("test_testpattern.py", files)

    def test_create_pattern_no_tests(self):
        files = TemplateGenerator.create_pattern(
            PatternTemplate.CUSTOM,
            "NoTests",
            "No tests",
            include_tests=False
        )

        self.assertEqual(len(files), 1)
        self.assertIn("notests.py", files)


class TestDebugTracer(unittest.TestCase):
    def setUp(self):
        self.tracer = DebugTracer()

    def test_trace_event(self):
        self.tracer.trace("test_event", "TestPattern", {"key": "value"})

        traces = self.tracer.get_traces()
        self.assertEqual(len(traces), 1)
        self.assertEqual(traces[0]["event"], "test_event")
        self.assertEqual(traces[0]["pattern"], "TestPattern")

    def test_disabled_tracer(self):
        tracer = DebugTracer(enabled=False)
        tracer.trace("event")

        self.assertEqual(len(tracer.get_traces()), 0)

    def test_get_traces_filtered(self):
        self.tracer.trace("event1", "Pattern1")
        self.tracer.trace("event2", "Pattern2")
        self.tracer.trace("event3", "Pattern1")

        pattern1_traces = self.tracer.get_traces("Pattern1")
        self.assertEqual(len(pattern1_traces), 2)

    def test_clear_traces(self):
        self.tracer.trace("event")
        self.assertEqual(len(self.tracer.get_traces()), 1)

        self.tracer.clear()
        self.assertEqual(len(self.tracer.get_traces()), 0)

    def test_format_traces(self):
        self.tracer.trace("event1", "Pattern1", {"data": "value"})

        formatted = self.tracer.format_traces()
        self.assertIn("Debug Traces", formatted)
        self.assertIn("event1", formatted)
        self.assertIn("Pattern1", formatted)

    def test_trace_execution_success(self):
        with self.tracer.trace_execution("TestPattern", "test task"):
            pass

        traces = self.tracer.get_traces()
        self.assertEqual(len(traces), 2)
        self.assertEqual(traces[0]["event"], "execution_start")
        self.assertEqual(traces[1]["event"], "execution_end")

    def test_trace_execution_error(self):
        with self.assertRaises(ValueError):
            with self.tracer.trace_execution("TestPattern", "test"):
                raise ValueError("Test error")

        traces = self.tracer.get_traces()
        self.assertGreaterEqual(len(traces), 2)
        error_trace = [t for t in traces if t["event"] == "execution_error"]
        self.assertEqual(len(error_trace), 1)


class TestPatternInspector(unittest.TestCase):
    async def test_inspect_pattern(self):
        pattern = MockPattern()

        inspection = PatternInspector.inspect_pattern(pattern)

        self.assertEqual(inspection["class_name"], "MockPattern")
        self.assertIn("methods", inspection)
        self.assertIn("config", inspection)

    async def test_format_inspection(self):
        pattern = MockPattern()
        inspection = PatternInspector.inspect_pattern(pattern)

        formatted = PatternInspector.format_inspection(inspection)

        self.assertIn("MockPattern", formatted)
        self.assertIn("Methods:", formatted)

    async def test_compare_results(self):
        result1 = PatternResult(
            success=True,
            result="result1",
            iterations=1,
            metadata={"key1": "value1"}
        )

        result2 = PatternResult(
            success=True,
            result="result2",
            iterations=2,
            metadata={"key2": "value2"}
        )

        comparison = PatternInspector.compare_results(result1, result2)

        self.assertTrue(comparison["success_match"])
        self.assertFalse(comparison["result_match"])
        self.assertEqual(comparison["iterations_diff"], -1)


class TestMockAgent(unittest.IsolatedAsyncioTestCase):
    async def test_generate(self):
        agent = MockAgent(["response1", "response2"])

        result1 = await agent.generate("prompt1")
        result2 = await agent.generate("prompt2")

        self.assertEqual(result1, "response1")
        self.assertEqual(result2, "response2")
        self.assertEqual(agent.call_count, 2)

    async def test_generate_default_response(self):
        agent = MockAgent()
        result = await agent.generate("prompt")

        self.assertEqual(result, "Mock response")

    async def test_chat(self):
        agent = MockAgent(["chat response"])
        messages = [{"role": "user", "content": "test"}]

        result = await agent.chat(messages)

        self.assertEqual(result, "chat response")

    async def test_reset(self):
        agent = MockAgent()
        await agent.generate("test")

        agent.reset()

        self.assertEqual(agent.call_count, 0)
        self.assertEqual(len(agent.calls), 0)


class TestMockPattern(unittest.IsolatedAsyncioTestCase):
    async def test_mock_pattern_execution(self):
        pattern = MockPattern(result="test result", success=True)

        result = await pattern.execute("test task")

        self.assertTrue(result.success)
        self.assertEqual(result.result, "test result")
        self.assertEqual(pattern.execution_count, 1)

    async def test_mock_pattern_failure(self):
        pattern = MockPattern(success=False)

        result = await pattern.execute("test task")

        self.assertFalse(result.success)


class TestPatternTestCase(PatternTestCase):
    async def test_create_mock_agent(self):
        agent = self.create_mock_agent(["response"])

        result = await agent.generate("prompt")

        self.assertEqual(result, "response")

    async def test_assert_pattern_result(self):
        result = PatternResult(
            success=True,
            result="output",
            iterations=1
        )

        self.assert_pattern_result(result, success=True)

    async def test_assert_agent_called(self):
        agent = MockAgent()
        await agent.generate("test prompt")

        self.assert_agent_called(agent, min_calls=1, contains="test")


class TestCreateTestPattern(unittest.TestCase):
    def test_create_test_pattern_direct(self):
        pattern = MockPattern(result="test")

        self.assertIsInstance(pattern, MockPattern)
        self.assertIsInstance(pattern, BasePattern)


class TestAsyncMockHelper(unittest.IsolatedAsyncioTestCase):
    async def test_create_async_mock(self):
        mock = AsyncMockHelper.create_async_mock("return value")

        result = await mock()

        self.assertEqual(result, "return value")

    async def test_create_agent_mock(self):
        agent = AsyncMockHelper.create_agent_mock(["resp1", "resp2"])

        result1 = await agent.generate("prompt1")
        result2 = await agent.generate("prompt2")

        self.assertEqual(result1, "resp1")
        self.assertEqual(result2, "resp2")


class TestTestDataBuilder(unittest.TestCase):
    def test_build_pattern_result(self):
        result = TestDataBuilder.build_pattern_result(
            success=True,
            result="output",
            custom_key="custom_value"
        )

        self.assertTrue(result.success)
        self.assertEqual(result.result, "output")
        self.assertEqual(result.metadata["custom_key"], "custom_value")

    def test_build_pattern_config(self):
        config = TestDataBuilder.build_pattern_config(
            name="TestPattern",
            pattern_type=PatternType.REACT
        )

        self.assertEqual(config.name, "TestPattern")
        self.assertEqual(config.pattern_type, PatternType.REACT)


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