"""
Tests for followup_watch gate integration in release_check_runner.
"""
import os
import sys
import asyncio
import json
from datetime import datetime, timedelta
from pathlib import Path
from unittest.mock import AsyncMock, MagicMock, patch

import pytest

ROOT = Path(__file__).resolve().parent.parent
ROUTER = ROOT / "services" / "router"
if str(ROUTER) not in sys.path:
    sys.path.insert(0, str(ROUTER))


class MockToolResult:
    def __init__(self, success, result=None, error=None):
        self.success = success
        self.result = result
        self.error = error


class MockToolManager:
    """Minimal mock for tool_manager.execute_tool, customizable per test."""
    def __init__(self, followup_data=None, always_pass_others=True):
        self.followup_data = followup_data or {
            "open_incidents": [],
            "overdue_followups": [],
            "stats": {"open_incidents": 0, "overdue": 0, "total_open_followups": 0},
        }
        self.always_pass_others = always_pass_others
        self.calls = []

    async def execute_tool(self, tool_name, args, agent_id="test"):
        self.calls.append((tool_name, args.get("action")))
        if tool_name == "oncall_tool" and args.get("action") == "incident_followups_summary":
            return MockToolResult(True, self.followup_data)
        if self.always_pass_others:
            return MockToolResult(True, {
                "pass": True, "blocking_count": 0, "breaking_count": 0,
                "unmitigated_high_count": 0, "summary": "ok",
            })
        return MockToolResult(False, error="skipped")


@pytest.fixture
def reset_policy_cache():
    from release_check_runner import _reload_gate_policy
    _reload_gate_policy()
    yield
    _reload_gate_policy()


def _run_check(tm, inputs, agent="test"):
    from release_check_runner import run_release_check
    return asyncio.run(run_release_check(tm, inputs, agent))


class TestFollowupWatchGateWarn:
    """followup_watch in warn mode: release passes regardless."""

    def test_release_passes_with_open_p1(self, reset_policy_cache):
        data = {
            "open_incidents": [
                {"id": "inc_1", "severity": "P1", "status": "open",
                 "started_at": "2025-01-01", "title": "Outage"}
            ],
            "overdue_followups": [],
            "stats": {"open_incidents": 1, "overdue": 0, "total_open_followups": 0},
        }
        tm = MockToolManager(followup_data=data)

        with patch("release_check_runner.load_gate_policy") as mock_policy:
            mock_policy.return_value = {
                "_profile": "dev",
                "_default_mode": "warn",
                "followup_watch": {"mode": "warn", "fail_on": ["P0", "P1"]},
                "privacy_watch": {"mode": "off"},
                "cost_watch": {"mode": "off"},
                "get": lambda name: {"mode": "warn"},
            }
            result = _run_check(tm, {"service_name": "gateway"})
        assert result["pass"] is True
        gate_names = [g["name"] for g in result["gates"]]
        assert "followup_watch" in gate_names
        fw_gate = next(g for g in result["gates"] if g["name"] == "followup_watch")
        assert fw_gate["status"] == "pass"
        assert "Open critical incidents" in " ".join(result["recommendations"])

    def test_release_passes_with_overdue(self, reset_policy_cache):
        data = {
            "open_incidents": [],
            "overdue_followups": [
                {"incident_id": "inc_1", "title": "Fix it", "due_date": "2025-01-01",
                 "priority": "P1", "owner": "sofiia"}
            ],
            "stats": {"open_incidents": 0, "overdue": 1, "total_open_followups": 1},
        }
        tm = MockToolManager(followup_data=data)

        with patch("release_check_runner.load_gate_policy") as mock_policy:
            mock_policy.return_value = {
                "_profile": "dev",
                "_default_mode": "warn",
                "followup_watch": {"mode": "warn", "fail_on": ["P0", "P1"]},
                "privacy_watch": {"mode": "off"},
                "cost_watch": {"mode": "off"},
                "get": lambda name: {"mode": "warn"},
            }
            result = _run_check(tm, {"service_name": "gateway"})
        assert result["pass"] is True
        assert "Overdue follow-ups" in " ".join(result["recommendations"])


class TestFollowupWatchGateStrict:
    """followup_watch in strict mode: blocks release on P0/P1 or overdue."""

    def test_release_blocked_by_open_p1(self, reset_policy_cache):
        data = {
            "open_incidents": [
                {"id": "inc_1", "severity": "P1", "status": "open",
                 "started_at": "2025-01-01", "title": "Outage"}
            ],
            "overdue_followups": [],
            "stats": {"open_incidents": 1, "overdue": 0, "total_open_followups": 0},
        }
        tm = MockToolManager(followup_data=data)

        with patch("release_check_runner.load_gate_policy") as mock_policy:
            mock_policy.return_value = {
                "_profile": "staging",
                "_default_mode": "warn",
                "followup_watch": {"mode": "strict", "fail_on": ["P0", "P1"]},
                "privacy_watch": {"mode": "off"},
                "cost_watch": {"mode": "off"},
                "get": lambda name: {"mode": "warn"},
            }
            result = _run_check(tm, {"service_name": "gateway", "fail_fast": True})
        assert result["pass"] is False

    def test_release_blocked_by_overdue_followups(self, reset_policy_cache):
        data = {
            "open_incidents": [],
            "overdue_followups": [
                {"incident_id": "inc_1", "title": "Migrate DB", "due_date": "2025-01-01",
                 "priority": "P1", "owner": "sofiia"}
            ],
            "stats": {"open_incidents": 0, "overdue": 1, "total_open_followups": 1},
        }
        tm = MockToolManager(followup_data=data)

        with patch("release_check_runner.load_gate_policy") as mock_policy:
            mock_policy.return_value = {
                "_profile": "staging",
                "_default_mode": "warn",
                "followup_watch": {"mode": "strict", "fail_on": ["P0", "P1"]},
                "privacy_watch": {"mode": "off"},
                "cost_watch": {"mode": "off"},
                "get": lambda name: {"mode": "warn"},
            }
            result = _run_check(tm, {"service_name": "gateway", "fail_fast": True})
        assert result["pass"] is False

    def test_release_passes_when_no_issues(self, reset_policy_cache):
        data = {
            "open_incidents": [],
            "overdue_followups": [],
            "stats": {"open_incidents": 0, "overdue": 0, "total_open_followups": 0},
        }
        tm = MockToolManager(followup_data=data)

        with patch("release_check_runner.load_gate_policy") as mock_policy:
            mock_policy.return_value = {
                "_profile": "staging",
                "_default_mode": "warn",
                "followup_watch": {"mode": "strict", "fail_on": ["P0", "P1"]},
                "privacy_watch": {"mode": "off"},
                "cost_watch": {"mode": "off"},
                "get": lambda name: {"mode": "warn"},
            }
            result = _run_check(tm, {"service_name": "gateway"})
        assert result["pass"] is True


class TestFollowupWatchGateOff:
    """followup_watch in off mode: gate is skipped entirely."""

    def test_gate_skipped_when_off(self, reset_policy_cache):
        tm = MockToolManager()

        with patch("release_check_runner.load_gate_policy") as mock_policy:
            mock_policy.return_value = {
                "_profile": "dev",
                "_default_mode": "warn",
                "followup_watch": {"mode": "off"},
                "privacy_watch": {"mode": "off"},
                "cost_watch": {"mode": "off"},
                "get": lambda name: {"mode": "off"},
            }
            result = _run_check(tm, {"service_name": "gateway"})
        gate_names = [g["name"] for g in result["gates"]]
        assert "followup_watch" not in gate_names
        called_actions = [c[1] for c in tm.calls]
        assert "incident_followups_summary" not in called_actions
