"""The egress denial-guidance helper must turn machine reason codes into clear,
actionable user messages (what was blocked + how to allow it)."""

from __future__ import annotations

import pytest

from local_deep_research.security.egress.guidance import denial_guidance


@pytest.mark.parametrize(
    "reason,target,must_contain",
    [
        # Scope blocks name the scope AND the setting to change.
        (
            "scope_mismatch_private_only",
            "arxiv",
            ["Private only", "Egress Scope"],
        ),
        (
            "scope_mismatch_public_only",
            "library",
            ["Public only", "Egress Scope"],
        ),
        ("strict_not_primary", "wikipedia", ["Strict", "primary"]),
        # Inference blocks name the require-local toggle.
        ("provider_cloud_only", "openai", ["Require local LLM", "openai"]),
        ("provider_cloud", "openai", ["Require local embeddings"]),
        ("provider_remote", "ollama", ["local"]),
        # Hard rule: metadata cannot be overridden.
        ("blocked_metadata_ip", None, ["cannot be overridden", "metadata"]),
        ("elasticsearch_cloud_id_public_egress", None, ["Cloud ID", "hosts"]),
        ("unknown_egress_scope", None, ["unrecognised", "Egress Scope"]),
    ],
)
def test_guidance_is_clear_and_actionable(reason, target, must_contain):
    msg = denial_guidance(reason, target=target)
    assert msg and len(msg) > 20
    for needle in must_contain:
        assert needle in msg, f"{reason}: expected '{needle}' in: {msg}"


def test_guidance_inserts_target():
    msg = denial_guidance("scope_mismatch_private_only", target="arxiv")
    assert "arxiv" in msg


def test_non_policy_reasons_get_honest_explanation_not_setting_advice():
    # A malformed URL isn't a policy block the user fixes via settings.
    msg = denial_guidance("url_malformed")
    assert "malformed" in msg
    assert "Egress Scope" not in msg


def test_unknown_reason_is_safe_and_nonempty():
    msg = denial_guidance("brand_new_code_42", target="X")
    assert "X" in msg
    assert "brand_new_code_42" in msg  # surfaced for support
    assert msg  # never empty


def test_target_none_falls_back_to_this_action():
    msg = denial_guidance("scope_mismatch_private_only")  # no target
    assert msg.startswith("This action")
    assert "Egress Scope" in msg


@pytest.mark.parametrize(
    "reason,needle",
    [
        ("no_hostname", "no host"),
        ("unsupported_scheme", "http/https"),
        ("dangerous_scheme", "non-web scheme"),
        ("host_unclassified", "resolved or classified"),
        ("internal_error", "internal error"),
    ],
)
def test_all_non_policy_reasons_explain_without_setting_advice(reason, needle):
    msg = denial_guidance(reason)
    assert needle in msg
    # These are parse/format failures, not a policy the user toggles.
    assert "Egress Scope" not in msg
