"""
Tests for release_check recurrence_watch gate (warn/strict/off behavior via GatePolicy).
"""
from __future__ import annotations

import asyncio
import os
import sys
from pathlib import Path
from typing import Dict, Optional
from unittest.mock import AsyncMock, MagicMock

import pytest

ROUTER_DIR = Path(__file__).parent.parent / "services" / "router"
REPO_ROOT = Path(__file__).parent.parent
sys.path.insert(0, str(ROUTER_DIR))
sys.path.insert(0, str(REPO_ROOT))

os.environ.setdefault("REPO_ROOT", str(REPO_ROOT))
os.environ["AUDIT_BACKEND"] = "memory"
os.environ["INCIDENT_BACKEND"] = "memory"


# ─── Helpers ─────────────────────────────────────────────────────────────────

class _FR:
    def __init__(self, data, success=True, error=None):
        self.success = success
        self.result = data
        self.error = error


def _recurrence_result(
    high_sigs=None, high_kinds=None,
    warn_sigs=None, warn_kinds=None,
    max_sev="P3", total=0,
):
    return _FR({
        "high_recurrence": {
            "signatures": high_sigs or [],
            "kinds": high_kinds or [],
        },
        "warn_recurrence": {
            "signatures": warn_sigs or [],
            "kinds": warn_kinds or [],
        },
        "max_severity_seen": max_sev,
        "total_incidents": total,
    })


def _make_tool_side_effect(
    high_sigs=None, high_kinds=None,
    warn_sigs=None, warn_kinds=None,
    max_sev="P3", total=0,
    recurrence_error=False,
):
    async def _exec(tool_name, args, agent_id=None):
        if tool_name == "pr_reviewer_tool":
            return _FR({"approved": True, "verdict": "LGTM", "issues": []})
        if tool_name == "config_linter_tool":
            return _FR({"pass": True, "errors": [], "warnings": []})
        if tool_name == "dependency_scanner_tool":
            return _FR({"pass": True, "summary": "ok", "vulnerabilities": []})
        if tool_name == "contract_tool":
            return _FR({"pass": True, "breaking_changes": [], "warnings": []})
        if tool_name == "threatmodel_tool":
            return _FR({"risk_level": "low", "threats": []})
        if tool_name == "data_governance_tool":
            return _FR({"pass": True, "findings": [], "recommendations": [], "stats": {}})
        if tool_name == "cost_analyzer_tool":
            return _FR({"anomalies": [], "anomaly_count": 0})
        if tool_name == "observability_tool":
            return _FR({"violations": [], "metrics": {}, "thresholds": {}, "skipped": True})
        if tool_name == "oncall_tool":
            action = args.get("action", "")
            if action == "incident_followups_summary":
                return _FR({"stats": {"open_incidents": 0, "overdue": 0,
                                      "total_open_followups": 0},
                            "open_incidents": [], "overdue_followups": []})
            return _FR({})
        if tool_name == "incident_intelligence_tool":
            if recurrence_error:
                return _FR({}, success=False, error="store unavailable")
            return _recurrence_result(
                high_sigs=high_sigs, high_kinds=high_kinds,
                warn_sigs=warn_sigs, warn_kinds=warn_kinds,
                max_sev=max_sev, total=total,
            )
        return _FR({})

    return _exec


async def _run(
    inputs: Dict,
    high_sigs=None, high_kinds=None,
    warn_sigs=None, warn_kinds=None,
    max_sev="P3", total=0,
    recurrence_error=False,
):
    from release_check_runner import run_release_check, _reload_gate_policy
    _reload_gate_policy()

    tm = MagicMock()
    tm.execute_tool = AsyncMock(side_effect=_make_tool_side_effect(
        high_sigs=high_sigs, high_kinds=high_kinds,
        warn_sigs=warn_sigs, warn_kinds=warn_kinds,
        max_sev=max_sev, total=total,
        recurrence_error=recurrence_error,
    ))
    return await run_release_check(tm, inputs, agent_id="sofiia")


# ─── Warn mode ────────────────────────────────────────────────────────────────

def test_recurrence_warn_mode_passes():
    """dev profile: warn mode — high recurrence adds recommendation but pass=True."""
    high_kinds = [{"kind": "error_rate", "count": 8, "services": ["gateway"]}]
    report = asyncio.run(_run(
        {
            "diff_text": "x", "gate_profile": "dev",
            "run_recurrence_watch": True, "fail_fast": False,
            "service_name": "gateway",
        },
        high_kinds=high_kinds, max_sev="P1", total=8,
    ))
    assert report["pass"] is True, "warn mode must not block release"
    gate_names = [g["name"] for g in report["gates"]]
    assert "recurrence_watch" in gate_names

    rw = next(g for g in report["gates"] if g["name"] == "recurrence_watch")
    assert rw["status"] == "pass"
    assert rw.get("has_high_recurrence") is True
    assert any("recurrence" in r.lower() or "gateway" in r.lower()
               for r in report.get("recommendations", []))


def test_recurrence_warn_adds_recommendation_for_warn_level():
    """Warn-level recurrence (not high) also adds recommendations in warn mode."""
    warn_sigs = [{"signature": "aabbccdd1234", "count": 3, "services": ["router"],
                  "last_seen": "2026-02-20T10:00:00", "severity_min": "P2"}]
    report = asyncio.run(_run(
        {"diff_text": "x", "gate_profile": "dev", "run_recurrence_watch": True,
         "fail_fast": False, "service_name": "router"},
        warn_sigs=warn_sigs, max_sev="P2", total=3,
    ))
    assert report["pass"] is True
    rw = next((g for g in report["gates"] if g["name"] == "recurrence_watch"), None)
    assert rw is not None
    assert rw.get("has_warn_recurrence") is True


# ─── Strict mode ─────────────────────────────────────────────────────────────

def test_recurrence_strict_blocks_on_high_and_p1():
    """staging: strict mode — high recurrence with P1 incident → release fails."""
    high_kinds = [{"kind": "error_rate", "count": 8, "services": ["gateway"]}]
    report = asyncio.run(_run(
        {
            "diff_text": "x", "gate_profile": "staging",
            "run_recurrence_watch": True, "fail_fast": False,
            "service_name": "gateway",
        },
        high_kinds=high_kinds, max_sev="P1", total=8,
    ))
    assert report["pass"] is False, "staging strict: high recurrence with P1 must fail"
    rw = next(g for g in report["gates"] if g["name"] == "recurrence_watch")
    assert rw.get("has_high_recurrence") is True


def test_recurrence_strict_passes_when_no_high():
    """staging: strict mode — warn-only recurrence (no high) → release passes."""
    warn_kinds = [{"kind": "latency", "count": 4, "services": ["router"]}]
    report = asyncio.run(_run(
        {
            "diff_text": "x", "gate_profile": "staging",
            "run_recurrence_watch": True, "fail_fast": False,
            "service_name": "router",
        },
        warn_kinds=warn_kinds, max_sev="P2", total=4,
    ))
    assert report["pass"] is True, "staging strict: warn-only recurrence should not block"


def test_recurrence_strict_passes_when_high_but_low_severity():
    """staging: strict mode — high recurrence but only P2/P3 → pass (fail_on P0/P1 only)."""
    high_sigs = [{"signature": "aabb1122ccdd", "count": 7, "services": ["svc"],
                  "last_seen": "2026-02-20T12:00:00", "severity_min": "P2"}]
    report = asyncio.run(_run(
        {
            "diff_text": "x", "gate_profile": "staging",
            "run_recurrence_watch": True, "fail_fast": False,
            "service_name": "svc",
        },
        high_sigs=high_sigs, max_sev="P2", total=7,
    ))
    assert report["pass"] is True, "staging strict: high recurrence with P2 should NOT block"


# ─── Off mode ─────────────────────────────────────────────────────────────────

def test_recurrence_off_mode_skips():
    """run_recurrence_watch=False → gate not called, not in output."""
    high_kinds = [{"kind": "error_rate", "count": 99, "services": ["gateway"]}]
    report = asyncio.run(_run(
        {
            "diff_text": "x", "gate_profile": "staging",
            "run_recurrence_watch": False, "fail_fast": False,
        },
        high_kinds=high_kinds, max_sev="P0", total=99,
    ))
    assert report["pass"] is True
    gate_names = [g["name"] for g in report["gates"]]
    assert "recurrence_watch" not in gate_names


def test_recurrence_watch_mode_override_off():
    """recurrence_watch_mode=off input override skips gate even in staging."""
    high_kinds = [{"kind": "error_rate", "count": 50, "services": ["svc"]}]
    report = asyncio.run(_run(
        {
            "diff_text": "x", "gate_profile": "staging",
            "run_recurrence_watch": True,
            "recurrence_watch_mode": "off",
            "fail_fast": False,
        },
        high_kinds=high_kinds, max_sev="P0", total=50,
    ))
    assert report["pass"] is True
    gate_names = [g["name"] for g in report["gates"]]
    assert "recurrence_watch" not in gate_names


# ─── Non-fatal error behavior ─────────────────────────────────────────────────

def test_recurrence_watch_error_is_nonfatal():
    """If intelligence tool fails → gate skips non-fatally, release still passes."""
    report = asyncio.run(_run(
        {
            "diff_text": "x", "gate_profile": "staging",
            "run_recurrence_watch": True, "fail_fast": False,
        },
        recurrence_error=True,
    ))
    assert report["pass"] is True, "Error in recurrence_watch must not block release"
    rw = next((g for g in report["gates"] if g["name"] == "recurrence_watch"), None)
    if rw:
        assert rw.get("skipped") is True


# ─── Prod profile ────────────────────────────────────────────────────────────

def test_recurrence_prod_profile_is_warn():
    """prod profile: recurrence_watch mode=warn → no blocking even with P0."""
    high_kinds = [{"kind": "slo_breach", "count": 20, "services": ["gateway"]}]
    report = asyncio.run(_run(
        {
            "diff_text": "x", "gate_profile": "prod",
            "run_recurrence_watch": True, "fail_fast": False,
        },
        high_kinds=high_kinds, max_sev="P0", total=20,
    ))
    assert report["pass"] is True, "prod profile: recurrence_watch is warn-only"
