from __future__ import annotations

import os
import unittest
from unittest.mock import patch

from src import tools


class ToolsUnitTests(unittest.TestCase):
    def test_parse_key_values_supports_quoted_values(self) -> None:
        message = 'mode=evaluation name="customer support flow" metadata.channel=prod'
        parsed = tools._parse_key_values(message)
        self.assertEqual(parsed["mode"], "evaluation")
        self.assertEqual(parsed["name"], "customer support flow")
        self.assertEqual(parsed["metadata.channel"], "prod")

    def test_parse_filters_with_metadata(self) -> None:
        message = 'mode=overview user_id=baem1n metadata.source=opencode limit=7'
        filters = tools._parse_filters(message)
        self.assertEqual(filters["mode"], "overview")
        self.assertEqual(filters["user_id"], "baem1n")
        self.assertEqual(filters["metadata"]["source"], "opencode")
        self.assertEqual(filters["limit"], 7)

    def test_time_regression_summary_detects_latency_and_cost_regression(self) -> None:
        traces = [
            {"timestamp": "2026-02-01T00:00:00+00:00", "latency": 10, "total_cost": 0.1, "observations": []},
            {"timestamp": "2026-02-01T00:05:00+00:00", "latency": 11, "total_cost": 0.1, "observations": []},
            {"timestamp": "2026-02-02T00:00:00+00:00", "latency": 30, "total_cost": 0.3, "observations": []},
            {"timestamp": "2026-02-02T00:05:00+00:00", "latency": 31, "total_cost": 0.3, "observations": []},
        ]
        summary = tools._time_regression_summary(traces)
        flags = summary["regression_flags"]
        self.assertIn("latency_regression", flags)
        self.assertIn("cost_regression", flags)

    def test_confidence_level(self) -> None:
        low = tools._confidence_level({"count": 3, "traces_with_observations": 3})
        high = tools._confidence_level({"count": 25, "traces_with_observations": 20})
        self.assertEqual(low, "low")
        self.assertEqual(high, "high")

    def test_parse_filters_supports_prompt_value(self) -> None:
        message = 'mode=prompt prompt="system prompt here" metadata.channel=prod'
        filters = tools._parse_filters(message)
        self.assertEqual(filters["mode"], "prompt_engineering")
        self.assertEqual(filters["prompt"], "system prompt here")

    def test_prompt_candidates_are_ranked_and_have_scores(self) -> None:
        metrics = {"error_rate": 0.25, "avg_latency_ms": 4200, "missing_user": 2, "missing_session": 1}
        baseline = "사용자 문의를 정확히 답변해줘."
        ranked = tools._score_prompt_candidates(tools._rewrite_prompt_candidates(baseline, metrics, None), metrics)
        self.assertEqual(len(ranked), 3)
        self.assertGreaterEqual(ranked[0]["scores"]["total"], ranked[-1]["scores"]["total"])
        self.assertIn("clarity", ranked[0]["scores"])

    def test_prompt_section_contains_recommended_prompt(self) -> None:
        trace = {"id": "tr-1", "name": "demo", "metadata": {"prompt": "요청을 해결해줘"}}
        metrics = {"error_rate": 0.1, "avg_latency_ms": 1500, "missing_user": 0, "missing_session": 0, "total_cost": 0.0}
        filters = {"prompt": None}
        with patch.dict(os.environ, {"AGENT_PROMPT_REVIEW_USE_MODEL": "0"}, clear=False):
            section = tools._polly_prompt_section({}, trace, metrics, filters, traces=[trace])
        self.assertIn("Recommended prompt", section)
        self.assertIn("expected_total_delta", section)

    def test_parse_model_spec_supports_provider_prefix(self) -> None:
        spec = tools._parse_model_spec("anthropic:claude-3-5-sonnet-latest")
        self.assertEqual(spec["provider"], "anthropic")
        self.assertEqual(spec["model"], "claude-3-5-sonnet-latest")
        self.assertEqual(spec["alias"], "anthropic:claude-3-5-sonnet-latest")

    def test_parse_model_spec_supports_gemini_alias(self) -> None:
        spec = tools._parse_model_spec("gemini:gemini-2.0-flash")
        self.assertEqual(spec["provider"], "google_genai")
        self.assertEqual(spec["model"], "gemini-2.0-flash")

    def test_comparison_model_specs_fallbacks_to_env_keys(self) -> None:
        env = {
            "OPENAI_API_KEY": "x",
            "ANTHROPIC_API_KEY": "y",
            "GOOGLE_API_KEY": "z",
            "AGENT_LLM_COMPARISON_MODELS": "",
            "AGENT_MODEL_SPECS": "",
            "AGENT_OPENROUTER_MODELS": "",
            "OPENROUTER_MODELS": "",
        }
        with patch.dict(os.environ, env, clear=False):
            specs = tools._comparison_model_specs({})
        providers = [spec["provider"] for spec in specs]
        self.assertIn("openai", providers)
        self.assertIn("anthropic", providers)
        self.assertIn("google_genai", providers)

    def test_invoke_model_with_langchain_uses_init_chat_model(self) -> None:
        class _DummyResponse:
            content = "ok"

        class _DummyLLM:
            def invoke(self, _messages):
                return _DummyResponse()

        with patch("langchain.chat_models.init_chat_model", return_value=_DummyLLM()) as mocked_init:
            result = tools._invoke_model_with_langchain(
                profile={},
                spec={"provider": "openai", "model": "gpt-4o-mini", "alias": "openai:gpt-4o-mini"},
                context_payload={"hello": "world"},
            )
        self.assertEqual(result, "ok")
        mocked_init.assert_called_once()
        _, kwargs = mocked_init.call_args
        self.assertEqual(kwargs["model_provider"], "openai")
        self.assertEqual(kwargs["model"], "gpt-4o-mini")

    def test_invoke_model_with_langchain_supports_openrouter_provider(self) -> None:
        class _DummyResponse:
            content = "openrouter-ok"

        class _DummyLLM:
            def invoke(self, _messages):
                return _DummyResponse()

        with patch.dict(
            os.environ,
            {"OPENROUTER_API_KEY": "test-key", "OPENROUTER_BASE_URL": "https://openrouter.ai/api/v1"},
            clear=False,
        ):
            with patch("langchain.chat_models.init_chat_model", return_value=_DummyLLM()) as mocked_init:
                result = tools._invoke_model_with_langchain(
                    profile={},
                    spec={"provider": "openrouter", "model": "openai/gpt-4o-mini", "alias": "openrouter:openai/gpt-4o-mini"},
                    context_payload={"k": "v"},
                )
        self.assertEqual(result, "openrouter-ok")
        _, kwargs = mocked_init.call_args
        self.assertEqual(kwargs["model_provider"], "openai")
        self.assertEqual(kwargs["base_url"], "https://openrouter.ai/api/v1")


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