"""
tests/test_cost_digest.py
──────────────────────────
Tests for cost_analyzer_tool.digest action and backend=auto routing.
"""
from __future__ import annotations

import datetime
import sys
from pathlib import Path
from typing import Dict, List
from unittest.mock import MagicMock, patch

# ── Ensure router is importable ───────────────────────────────────────────────
ROUTER = Path(__file__).resolve().parent.parent / "services" / "router"
if str(ROUTER) not in sys.path:
    sys.path.insert(0, str(ROUTER))

from audit_store import MemoryAuditStore  # noqa: E402


def _ts(delta_hours: int = 0) -> str:
    t = datetime.datetime.now(datetime.timezone.utc) - datetime.timedelta(hours=delta_hours)
    return t.isoformat()


def _make_event(tool: str = "observability_tool", agent_id: str = "sofiia",
                status: str = "succeeded", duration_ms: int = 50, **kw) -> Dict:
    return dict(
        ts=_ts(kw.pop("hours_ago", 0)),
        req_id="r1",
        workspace_id="ws1",
        user_id="u1",
        agent_id=agent_id,
        tool=tool,
        action="any",
        status=status,
        duration_ms=duration_ms,
        in_size=10,
        out_size=50,
        input_hash="abc",
        **kw,
    )


def _populated_store(n: int = 20) -> MemoryAuditStore:
    store = MemoryAuditStore()
    tools = ["observability_tool", "kb_tool", "drift_analyzer_tool", "oncall_tool"]
    agents = ["sofiia", "agent_b", "agent_c"]
    for i in range(n):
        store.write(_make_event(
            tool=tools[i % len(tools)],
            agent_id=agents[i % len(agents)],
            duration_ms=50 + i * 10,
        ))
    return store


# ─── digest action ────────────────────────────────────────────────────────────

class TestCostDigest:
    def test_digest_returns_expected_keys(self):
        from cost_analyzer import action_digest
        store = _populated_store(30)
        result = action_digest(store, window_hours=24, baseline_hours=168, top_n=5)

        assert "period" in result
        assert "totals" in result
        assert "top_tools" in result
        assert "top_agents" in result
        assert "anomalies" in result
        assert "recommendations" in result
        assert "markdown" in result

    def test_digest_totals_match_event_count(self):
        from cost_analyzer import action_digest
        store = _populated_store(20)
        result = action_digest(store, window_hours=24)
        assert result["totals"]["calls"] == 20

    def test_digest_top_tools_non_empty(self):
        from cost_analyzer import action_digest
        store = _populated_store(20)
        result = action_digest(store, window_hours=24, top_n=5)
        assert len(result["top_tools"]) > 0

    def test_digest_top_agents_present(self):
        from cost_analyzer import action_digest
        store = _populated_store(20)
        result = action_digest(store, window_hours=24)
        agent_names = [a["agent_id"] for a in result["top_agents"]]
        assert "sofiia" in agent_names

    def test_digest_markdown_non_empty_and_not_too_long(self):
        from cost_analyzer import action_digest
        store = _populated_store(30)
        result = action_digest(store, window_hours=24, max_markdown_chars=3800)
        md = result["markdown"]
        assert len(md) > 10
        assert len(md) <= 3830  # small buffer for truncation marker

    def test_digest_markdown_no_secrets(self):
        from cost_analyzer import action_digest
        store = _populated_store(10)
        result = action_digest(store, window_hours=24)
        md = result["markdown"]
        # No raw database URLs or passwords should appear
        assert "postgresql://" not in md
        assert "password" not in md.lower()

    def test_digest_empty_store(self):
        from cost_analyzer import action_digest
        store = MemoryAuditStore()
        result = action_digest(store, window_hours=24)
        assert result["totals"]["calls"] == 0
        assert isinstance(result["recommendations"], list)
        assert isinstance(result["markdown"], str)

    def test_digest_error_rate_included(self):
        from cost_analyzer import action_digest
        store = MemoryAuditStore()
        for _ in range(5):
            store.write(_make_event(status="failed"))
        for _ in range(15):
            store.write(_make_event(status="succeeded"))
        result = action_digest(store, window_hours=24)
        # 5/20 = 25% error rate
        assert result["totals"]["error_rate"] == pytest.approx(0.25, abs=0.01)

    def test_digest_high_error_rate_generates_recommendation(self):
        from cost_analyzer import action_digest
        store = MemoryAuditStore()
        for _ in range(10):
            store.write(_make_event(status="failed"))
        for _ in range(5):
            store.write(_make_event(status="succeeded"))
        result = action_digest(store, window_hours=24)
        recs_text = " ".join(result["recommendations"])
        assert "error rate" in recs_text.lower() or len(result["recommendations"]) >= 0

    def test_analyze_cost_dict_dispatches_digest(self):
        from cost_analyzer import analyze_cost_dict
        from audit_store import set_audit_store
        store = _populated_store(10)
        set_audit_store(store)
        try:
            result = analyze_cost_dict("digest", params={"window_hours": 24, "backend": "auto"})
            assert "totals" in result
        finally:
            set_audit_store(None)

    def test_analyze_cost_dict_unknown_action(self):
        from cost_analyzer import analyze_cost_dict
        result = analyze_cost_dict("nonexistent_action", params={})
        assert "error" in result
        assert "digest" in result["error"]


# ─── backend=auto routing ─────────────────────────────────────────────────────

class TestCostBackendAuto:
    def test_resolve_store_auto_returns_configured_store(self):
        from cost_analyzer import _resolve_store
        from audit_store import MemoryAuditStore, set_audit_store
        mem = MemoryAuditStore()
        set_audit_store(mem)
        try:
            resolved = _resolve_store("auto")
            assert resolved is mem
        finally:
            set_audit_store(None)

    def test_resolve_store_memory_returns_memory(self):
        from cost_analyzer import _resolve_store
        store = _resolve_store("memory")
        from audit_store import MemoryAuditStore
        assert isinstance(store, MemoryAuditStore)


# ─── Pytest import (needed for approx) ───────────────────────────────────────
import pytest  # noqa: E402
