"""
Tests for multi-industry policy support:

  1. PolicyEngine — directory loading with path-based metadata injection
  2. PolicyEngine — real policies/ directory (45 policies, 4 industries, 12 categories)
  3. API — industry/category filter params on GET /v1/policies
  4. API — industry/category fields in CRUD and version history
  5. API — evaluate with real multi-industry agent roles
"""

import pathlib
import textwrap

import pytest
from fastapi.testclient import TestClient

from autopil.api.app import create_app
from autopil.models import ContextRequest, Decision, SensitivityLevel
from autopil.policy_engine import PolicyEngine

# Path to the real policies directory
POLICIES_DIR = pathlib.Path(__file__).parent.parent / "policies"


# ── helpers ───────────────────────────────────────────────────────────────────

def make_req(agent_role, source_id, sensitivity=SensitivityLevel.LOW, task_type=None):
    return ContextRequest(
        query="test",
        agent_role=agent_role,
        user_id="u_test",
        source_id=source_id,
        sensitivity_level=sensitivity,
        task_type=task_type,
    )


# ── fixtures ──────────────────────────────────────────────────────────────────

@pytest.fixture
def policy_dir(tmp_path):
    """
    Creates a minimal two-industry directory structure:

      tmp/
        financial_services/
          consumer_banking.yaml   (analyst role)
        healthcare/
          clinical_operations.yaml  (clinician role)
    """
    fs = tmp_path / "financial_services"
    fs.mkdir()
    (fs / "consumer_banking.yaml").write_text(textwrap.dedent("""
        policies:
          - name: analyst_policy
            agent_role: analyst
            allowed_sources: [reports, market_data]
            denied_sources: [executive_comms]
            max_sensitivity: high
    """))

    hc = tmp_path / "healthcare"
    hc.mkdir()
    (hc / "clinical_operations.yaml").write_text(textwrap.dedent("""
        policies:
          - name: clinician_policy
            agent_role: clinician
            allowed_sources: [ehr_summaries, lab_results]
            denied_sources: [billing_records]
            max_sensitivity: high
    """))

    return str(tmp_path)


@pytest.fixture
def dir_engine(policy_dir):
    return PolicyEngine(policy_dir)


@pytest.fixture
def real_engine():
    """PolicyEngine backed by the real policies/ directory."""
    return PolicyEngine(str(POLICIES_DIR))


@pytest.fixture
def dir_client(policy_dir, audit_db, api_key):
    """TestClient using the multi-industry policy directory."""
    app = create_app(policy_path=policy_dir, audit_db=audit_db)
    return TestClient(app, headers={"X-API-Key": api_key})


@pytest.fixture
def real_client(audit_db, api_key):
    """TestClient backed by the real policies/ directory."""
    app = create_app(policy_path=str(POLICIES_DIR), audit_db=audit_db)
    return TestClient(app, headers={"X-API-Key": api_key})


# ═══════════════════════════════════════════════════════════════════════════════
# 1. PolicyEngine — directory loading & metadata injection
# ═══════════════════════════════════════════════════════════════════════════════

class TestDirectoryLoading:
    def test_loads_policies_from_directory(self, dir_engine):
        """Engine built from a directory finds policies in nested YAML files."""
        decision, policy, _ = dir_engine.evaluate(make_req("analyst", "reports"))
        assert decision == Decision.ALLOW
        assert policy == "analyst_policy"

    def test_loads_policies_from_second_industry(self, dir_engine):
        decision, policy, _ = dir_engine.evaluate(make_req("clinician", "ehr_summaries"))
        assert decision == Decision.ALLOW
        assert policy == "clinician_policy"

    def test_denied_source_blocked_across_industries(self, dir_engine):
        decision, _, reason = dir_engine.evaluate(make_req("analyst", "executive_comms"))
        assert decision == Decision.DENY
        assert "explicitly denied" in reason

        decision2, _, _ = dir_engine.evaluate(make_req("clinician", "billing_records"))
        assert decision2 == Decision.DENY

    def test_industry_injected_from_directory_path(self, policy_dir):
        """Policies loaded from industry/ subdir get industry metadata injected."""
        engine = PolicyEngine(policy_dir)
        # Access internal policy list to verify metadata
        analyst_pol = next(p for p in engine.policies if p["agent_role"] == "analyst")
        assert analyst_pol.get("industry") == "financial_services"

    def test_category_injected_from_filename(self, policy_dir):
        """Category is the YAML filename stem (without extension)."""
        engine = PolicyEngine(policy_dir)
        analyst_pol = next(p for p in engine.policies if p["agent_role"] == "analyst")
        assert analyst_pol.get("category") == "consumer_banking"

        clinician_pol = next(p for p in engine.policies if p["agent_role"] == "clinician")
        assert clinician_pol.get("category") == "clinical_operations"

    def test_setdefault_does_not_overwrite_explicit_industry(self, tmp_path):
        """A policy with an explicit industry field keeps its value."""
        d = tmp_path / "telecom"
        d.mkdir()
        (d / "network.yaml").write_text(textwrap.dedent("""
            policies:
              - name: explicit_pol
                agent_role: net_agent
                industry: custom_industry
                allowed_sources: [alarms]
                denied_sources: []
                max_sensitivity: low
        """))
        engine = PolicyEngine(str(tmp_path))
        pol = next(p for p in engine.policies if p["agent_role"] == "net_agent")
        assert pol["industry"] == "custom_industry"

    def test_unknown_role_default_deny_in_directory_mode(self, dir_engine):
        decision, policy, _ = dir_engine.evaluate(make_req("rogue_agent", "reports"))
        assert decision == Decision.DENY
        assert policy == "default_deny"

    def test_directory_and_single_file_produce_same_decisions(self, policy_dir, tmp_path):
        """Flattened YAML and directory give identical ALLOW/DENY for the same requests."""
        flat = tmp_path / "flat.yaml"
        flat.write_text(textwrap.dedent("""
            policies:
              - name: analyst_policy
                agent_role: analyst
                allowed_sources: [reports, market_data]
                denied_sources: [executive_comms]
                max_sensitivity: high
        """))
        dir_eng  = PolicyEngine(policy_dir)
        flat_eng = PolicyEngine(str(flat))

        for source in ("reports", "market_data", "executive_comms", "unknown"):
            d_dir,  _, _ = dir_eng.evaluate(make_req("analyst", source))
            d_flat, _, _ = flat_eng.evaluate(make_req("analyst", source))
            assert d_dir == d_flat, f"Mismatch for source={source!r}"


# ═══════════════════════════════════════════════════════════════════════════════
# 2. Real policies/ directory — counts and metadata
# ═══════════════════════════════════════════════════════════════════════════════

class TestRealPoliciesDirectory:
    def test_loads_all_115_policies(self, real_engine):
        assert len(real_engine.policies) == 115

    def test_all_eleven_industries_present(self, real_engine):
        industries = {p["industry"] for p in real_engine.policies}
        assert industries == {
            "financial_services", "healthcare", "telecom", "logistics", "insurance",
            "retail", "energy", "manufacturing", "real_estate", "pharmacy", "public_sector",
        }

    def test_all_thirty_three_categories_present(self, real_engine):
        categories = {p["category"] for p in real_engine.policies}
        expected = {
            "consumer_banking", "wealth", "risk_compliance", "operations",
            "clinical_operations", "revenue_cycle", "compliance_privacy",
            "network_operations", "customer_experience", "fraud_assurance",
            "supply_chain", "fleet_operations", "customs_compliance",
            "underwriting", "claims_management", "fraud_compliance",
            "merchandising", "loss_prevention",
            "grid_operations", "trading_compliance", "field_safety",
            "quality_control", "supply_chain_ops", "safety_compliance",
            "property_management", "transactions", "valuation_compliance",
            "dispensing", "clinical_review", "regulatory_compliance",
            "citizen_services", "records_management", "procurement_compliance",
        }
        assert categories == expected

    def test_financial_services_has_16_policies(self, real_engine):
        fs = [p for p in real_engine.policies if p["industry"] == "financial_services"]
        assert len(fs) == 16

    def test_healthcare_has_11_policies(self, real_engine):
        hc = [p for p in real_engine.policies if p["industry"] == "healthcare"]
        assert len(hc) == 11

    def test_telecom_has_9_policies(self, real_engine):
        tc = [p for p in real_engine.policies if p["industry"] == "telecom"]
        assert len(tc) == 9

    def test_logistics_has_9_policies(self, real_engine):
        lg = [p for p in real_engine.policies if p["industry"] == "logistics"]
        assert len(lg) == 9

    def test_insurance_has_10_policies(self, real_engine):
        ins = [p for p in real_engine.policies if p["industry"] == "insurance"]
        assert len(ins) == 10

    def test_retail_has_10_policies(self, real_engine):
        assert len([p for p in real_engine.policies if p["industry"] == "retail"]) == 10

    def test_energy_has_10_policies(self, real_engine):
        assert len([p for p in real_engine.policies if p["industry"] == "energy"]) == 10

    def test_manufacturing_has_10_policies(self, real_engine):
        assert len([p for p in real_engine.policies if p["industry"] == "manufacturing"]) == 10

    def test_real_estate_has_10_policies(self, real_engine):
        assert len([p for p in real_engine.policies if p["industry"] == "real_estate"]) == 10

    def test_pharmacy_has_10_policies(self, real_engine):
        assert len([p for p in real_engine.policies if p["industry"] == "pharmacy"]) == 10

    def test_public_sector_has_10_policies(self, real_engine):
        assert len([p for p in real_engine.policies if p["industry"] == "public_sector"]) == 10

    def test_every_policy_has_industry_and_category(self, real_engine):
        for p in real_engine.policies:
            assert p.get("industry"), f"Missing industry: {p['name']}"
            assert p.get("category"), f"Missing category: {p['name']}"

    def test_every_policy_has_required_fields(self, real_engine):
        for p in real_engine.policies:
            assert "agent_role"     in p, f"Missing agent_role: {p['name']}"
            assert "allowed_sources" in p
            assert "denied_sources"  in p
            assert "max_sensitivity" in p


# ═══════════════════════════════════════════════════════════════════════════════
# 3. API — industry/category filter params
# ═══════════════════════════════════════════════════════════════════════════════

class TestPolicyFilters:
    def test_no_filter_returns_yaml_fallback_all_policies(self, real_client):
        r = real_client.get("/v1/policies")
        assert r.status_code == 200
        assert len(r.json()) == 115

    def test_filter_by_industry_financial_services(self, real_client):
        r = real_client.get("/v1/policies?industry=financial_services")
        assert r.status_code == 200
        policies = r.json()
        assert len(policies) == 16
        assert all(p["industry"] == "financial_services" for p in policies)

    def test_filter_by_industry_healthcare(self, real_client):
        r = real_client.get("/v1/policies?industry=healthcare")
        assert r.status_code == 200
        policies = r.json()
        assert len(policies) == 11
        assert all(p["industry"] == "healthcare" for p in policies)

    def test_filter_by_industry_telecom(self, real_client):
        r = real_client.get("/v1/policies?industry=telecom")
        assert r.status_code == 200
        assert len(r.json()) == 9

    def test_filter_by_industry_logistics(self, real_client):
        r = real_client.get("/v1/policies?industry=logistics")
        assert r.status_code == 200
        assert len(r.json()) == 9

    def test_filter_by_category_consumer_banking(self, real_client):
        r = real_client.get("/v1/policies?category=consumer_banking")
        assert r.status_code == 200
        policies = r.json()
        assert len(policies) == 4
        assert all(p["category"] == "consumer_banking" for p in policies)

    def test_filter_by_category_clinical_operations(self, real_client):
        r = real_client.get("/v1/policies?category=clinical_operations")
        assert r.status_code == 200
        assert len(r.json()) == 4

    def test_filter_industry_and_category_combined(self, real_client):
        r = real_client.get("/v1/policies?industry=logistics&category=fleet_operations")
        assert r.status_code == 200
        policies = r.json()
        assert len(policies) == 3
        assert all(p["industry"] == "logistics" for p in policies)
        assert all(p["category"] == "fleet_operations" for p in policies)

    def test_filter_by_industry_insurance(self, real_client):
        r = real_client.get("/v1/policies?industry=insurance")
        assert r.status_code == 200
        policies = r.json()
        assert len(policies) == 10
        assert all(p["industry"] == "insurance" for p in policies)

    def test_filter_by_category_underwriting(self, real_client):
        r = real_client.get("/v1/policies?category=underwriting")
        assert r.status_code == 200
        policies = r.json()
        assert len(policies) == 4
        assert all(p["category"] == "underwriting" for p in policies)

    def test_filter_insurance_and_claims_management(self, real_client):
        r = real_client.get("/v1/policies?industry=insurance&category=claims_management")
        assert r.status_code == 200
        policies = r.json()
        assert len(policies) == 3
        assert all(p["industry"] == "insurance" for p in policies)
        assert all(p["category"] == "claims_management" for p in policies)

    def test_filter_by_industry_retail(self, real_client):
        r = real_client.get("/v1/policies?industry=retail")
        assert r.status_code == 200
        assert len(r.json()) == 10
        assert all(p["industry"] == "retail" for p in r.json())

    def test_filter_by_industry_energy(self, real_client):
        r = real_client.get("/v1/policies?industry=energy")
        assert r.status_code == 200
        assert len(r.json()) == 10
        assert all(p["industry"] == "energy" for p in r.json())

    def test_filter_by_industry_manufacturing(self, real_client):
        r = real_client.get("/v1/policies?industry=manufacturing")
        assert r.status_code == 200
        assert len(r.json()) == 10
        assert all(p["industry"] == "manufacturing" for p in r.json())

    def test_filter_by_industry_real_estate(self, real_client):
        r = real_client.get("/v1/policies?industry=real_estate")
        assert r.status_code == 200
        assert len(r.json()) == 10
        assert all(p["industry"] == "real_estate" for p in r.json())

    def test_filter_by_industry_pharmacy(self, real_client):
        r = real_client.get("/v1/policies?industry=pharmacy")
        assert r.status_code == 200
        assert len(r.json()) == 10
        assert all(p["industry"] == "pharmacy" for p in r.json())

    def test_filter_by_industry_public_sector(self, real_client):
        r = real_client.get("/v1/policies?industry=public_sector")
        assert r.status_code == 200
        assert len(r.json()) == 10
        assert all(p["industry"] == "public_sector" for p in r.json())

    def test_unknown_industry_returns_empty(self, real_client):
        r = real_client.get("/v1/policies?industry=agriculture")
        assert r.status_code == 200
        assert r.json() == []

    def test_mismatched_industry_category_returns_empty(self, real_client):
        """healthcare + consumer_banking don't overlap."""
        r = real_client.get("/v1/policies?industry=healthcare&category=consumer_banking")
        assert r.status_code == 200
        assert r.json() == []

    def test_filter_by_industry_after_reload(self, real_client):
        """Filters work on DB-backed policies after reload."""
        real_client.post("/v1/policies/reload")
        r = real_client.get("/v1/policies?industry=telecom")
        assert r.status_code == 200
        policies = r.json()
        assert len(policies) == 9
        assert all(p["industry"] == "telecom" for p in policies)
        # DB-backed: real policy_ids
        assert all(p["policy_id"].startswith("pol_") for p in policies)


# ═══════════════════════════════════════════════════════════════════════════════
# 4. API — industry/category in CRUD and version history
# ═══════════════════════════════════════════════════════════════════════════════

INDUSTRY_PAYLOAD = {
    "name": "test_hc_policy",
    "agent_role": "test_clinician",
    "allowed_sources": ["ehr_summaries", "lab_results"],
    "denied_sources": ["billing_records"],
    "max_sensitivity": "high",
    "industry": "healthcare",
    "category": "clinical_operations",
    "allowed_tasks": ["chart_review"],
    "denied_tasks": ["billing_submission"],
}


class TestIndustryCategoryInCRUD:
    def test_create_policy_with_industry_and_category(self, client):
        r = client.post("/v1/policies", json=INDUSTRY_PAYLOAD)
        assert r.status_code == 200
        data = r.json()
        assert data["industry"] == "healthcare"
        assert data["category"] == "clinical_operations"

    def test_create_policy_with_tasks(self, client):
        r = client.post("/v1/policies", json=INDUSTRY_PAYLOAD)
        assert r.status_code == 200
        data = r.json()
        assert "chart_review" in data["allowed_tasks"]
        assert "billing_submission" in data["denied_tasks"]

    def test_list_shows_industry_and_category(self, client):
        client.post("/v1/policies", json=INDUSTRY_PAYLOAD)
        r = client.get("/v1/policies")
        policies = r.json()
        p = next(x for x in policies if x["name"] == "test_hc_policy")
        assert p["industry"] == "healthcare"
        assert p["category"] == "clinical_operations"

    def test_update_can_change_industry_and_category(self, client):
        client.post("/v1/policies", json=INDUSTRY_PAYLOAD)
        update = {
            "agent_role": "test_clinician",
            "allowed_sources": ["ehr_summaries"],
            "denied_sources": ["billing_records"],
            "max_sensitivity": "high",
            "industry": "telecom",
            "category": "customer_experience",
        }
        r = client.put("/v1/policies/test_hc_policy", json=update)
        assert r.status_code == 200
        data = r.json()
        assert data["industry"] == "telecom"
        assert data["category"] == "customer_experience"
        assert data["version"] == 2

    def test_history_includes_industry_and_category(self, client):
        client.post("/v1/policies", json=INDUSTRY_PAYLOAD)
        update = {
            "agent_role": "test_clinician",
            "allowed_sources": [],
            "denied_sources": [],
            "max_sensitivity": "medium",
            "industry": "logistics",
            "category": "supply_chain",
        }
        client.put("/v1/policies/test_hc_policy", json=update)

        r = client.get("/v1/policies/test_hc_policy/history")
        assert r.status_code == 200
        history = r.json()
        assert len(history) == 2
        # Newest entry (v2) has updated industry
        assert history[0]["industry"] == "logistics"
        # Original entry (v1) has original industry
        assert history[1]["industry"] == "healthcare"

    def test_reload_imports_industry_from_path(self, dir_client):
        """YAML reload into DB preserves industry/category from path structure."""
        r = dir_client.post("/v1/policies/reload")
        assert r.status_code == 200
        assert r.json()["policies_loaded"] == 2

        r = dir_client.get("/v1/policies")
        policies = r.json()

        analyst   = next(p for p in policies if p["agent_role"] == "analyst")
        clinician = next(p for p in policies if p["agent_role"] == "clinician")

        assert analyst["industry"]   == "financial_services"
        assert analyst["category"]   == "consumer_banking"
        assert clinician["industry"] == "healthcare"
        assert clinician["category"] == "clinical_operations"

    def test_policy_without_industry_category_is_null(self, client):
        """Creating a policy without industry/category returns null for those fields."""
        payload = {
            "name": "plain_policy",
            "agent_role": "plain_agent",
            "allowed_sources": ["source_a"],
            "denied_sources": [],
            "max_sensitivity": "low",
        }
        r = client.post("/v1/policies", json=payload)
        assert r.status_code == 200
        data = r.json()
        assert data["industry"] is None
        assert data["category"] is None


# ═══════════════════════════════════════════════════════════════════════════════
# 5. API evaluate — real multi-industry agent roles
# ═══════════════════════════════════════════════════════════════════════════════

class TestMultiIndustryEvaluate:
    """Spot-check evaluate decisions for each industry using the real policy files."""

    # ── Financial Services ────────────────────────────────────────────────────

    def test_fraud_analyst_allowed_transaction_history(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Flag unusual cross-border transfers",
            "agent_role": "fraud_analyst",
            "user_id": "u_test",
            "source_id": "transaction_history",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_fraud_analyst_denied_marketing_data(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Pull campaign engagement to correlate with fraud timing",
            "agent_role": "fraud_analyst",
            "user_id": "u_test",
            "source_id": "marketing_data",
            "sensitivity_level": "low",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    def test_collections_agent_allowed_delinquency_records(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "30-day past due balance review",
            "agent_role": "collections_agent",
            "user_id": "u_test",
            "source_id": "delinquency_records",
            "sensitivity_level": "medium",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_collections_agent_denied_credit_scores(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Pull FICO to assess payment plan eligibility",
            "agent_role": "collections_agent",
            "user_id": "u_test",
            "source_id": "credit_scores",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    def test_aml_investigator_allowed_restricted_source(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Structuring pattern check",
            "agent_role": "aml_investigator",
            "user_id": "u_test",
            "source_id": "transaction_history",
            "sensitivity_level": "restricted",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    # ── Healthcare ────────────────────────────────────────────────────────────

    def test_clinical_summary_agent_allowed_ehr(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Retrieve discharge summary for care transition",
            "agent_role": "clinical_summary_agent",
            "user_id": "u_test",
            "source_id": "ehr_summaries",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_clinical_summary_agent_denied_billing(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Pull billing history for coverage verification",
            "agent_role": "clinical_summary_agent",
            "user_id": "u_test",
            "source_id": "billing_records",
            "sensitivity_level": "medium",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    def test_triage_agent_allowed_symptom_intake(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "ESI scoring — chest pain onset 2h ago",
            "agent_role": "triage_agent",
            "user_id": "u_test",
            "source_id": "symptom_intake",
            "sensitivity_level": "medium",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_triage_agent_denied_ehr_summaries(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Pull full chart history to inform triage",
            "agent_role": "triage_agent",
            "user_id": "u_test",
            "source_id": "ehr_summaries",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    def test_hipaa_audit_agent_allowed_restricted_access_logs(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Minimum necessary check — nurse accessed records outside care team",
            "agent_role": "hipaa_audit_agent",
            "user_id": "u_test",
            "source_id": "access_logs",
            "sensitivity_level": "restricted",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_prior_auth_agent_denied_ehr_summaries(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Pull full chart to build PA narrative",
            "agent_role": "prior_auth_agent",
            "user_id": "u_test",
            "source_id": "ehr_summaries",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    # ── Telecom ───────────────────────────────────────────────────────────────

    def test_network_fault_agent_allowed_alarm_feeds(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "P1 alarm: fiber cut — 4,200 customers impacted",
            "agent_role": "network_fault_agent",
            "user_id": "u_test",
            "source_id": "alarm_feeds",
            "sensitivity_level": "medium",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_network_fault_agent_denied_customer_records(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Pull affected customer list for proactive outreach",
            "agent_role": "network_fault_agent",
            "user_id": "u_test",
            "source_id": "customer_records",
            "sensitivity_level": "medium",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    def test_subscription_fraud_agent_allowed_watchlist(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "New subscriber SSN matches prior fraud write-off",
            "agent_role": "subscription_fraud_agent",
            "user_id": "u_test",
            "source_id": "watchlist",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_churn_prediction_agent_denied_cdr_data(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Pull raw call records to build churn feature",
            "agent_role": "churn_prediction_agent",
            "user_id": "u_test",
            "source_id": "cdr_data",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    def test_revenue_assurance_agent_allowed_cdr(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "CDR audit — revenue leakage estimate",
            "agent_role": "revenue_assurance_agent",
            "user_id": "u_test",
            "source_id": "cdr_data",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    # ── Logistics ─────────────────────────────────────────────────────────────

    def test_route_optimization_agent_allowed_gps_telemetry(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Real-time reroute — I-35 closure, 14 active deliveries",
            "agent_role": "route_optimization_agent",
            "user_id": "u_test",
            "source_id": "gps_telemetry",
            "sensitivity_level": "low",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_route_optimization_agent_denied_driver_records(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Access driver history to assign high-value route",
            "agent_role": "route_optimization_agent",
            "user_id": "u_test",
            "source_id": "driver_records",
            "sensitivity_level": "medium",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    def test_driver_compliance_agent_allowed_hos_logs(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "HOS violation: driver at 10h 45m — needs break",
            "agent_role": "driver_compliance_agent",
            "user_id": "u_test",
            "source_id": "hos_logs",
            "sensitivity_level": "medium",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_sanctions_screening_agent_allowed_restricted_watchlist(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "BIS Entity List check — semiconductor export to flagged end-user",
            "agent_role": "sanctions_screening_agent",
            "user_id": "u_test",
            "source_id": "watchlist",
            "sensitivity_level": "restricted",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_trade_compliance_agent_denied_financial_ledgers(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Access landed cost model to quantify tariff impact",
            "agent_role": "trade_compliance_agent",
            "user_id": "u_test",
            "source_id": "financial_ledgers",
            "sensitivity_level": "medium",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    # ── Insurance ─────────────────────────────────────────────────────────────

    def test_underwriting_analyst_allowed_risk_profile(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Assess flood zone exposure for homeowner application",
            "agent_role": "underwriting_analyst",
            "user_id": "u_test",
            "source_id": "risk_profile_data",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_underwriting_analyst_denied_claims_history(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Pull prior loss runs to adjust premium",
            "agent_role": "underwriting_analyst",
            "user_id": "u_test",
            "source_id": "claims_history",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    def test_claims_adjuster_allowed_damage_assessments(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Pull structural damage report for total loss determination",
            "agent_role": "claims_adjuster",
            "user_id": "u_test",
            "source_id": "damage_assessments",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_claims_adjuster_denied_actuarial_models(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Use loss model to validate settlement offer",
            "agent_role": "claims_adjuster",
            "user_id": "u_test",
            "source_id": "actuarial_models",
            "sensitivity_level": "medium",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    def test_fraud_detection_agent_allowed_fraud_watchlist(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Match claimant against known fraud ring identifiers",
            "agent_role": "fraud_detection_agent",
            "user_id": "u_test",
            "source_id": "fraud_watchlist",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_compliance_officer_allowed_restricted_audit_logs(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "State DOI exam — pull 12-month access log for policy issuance workflow",
            "agent_role": "compliance_officer",
            "user_id": "u_test",
            "source_id": "audit_logs",
            "sensitivity_level": "restricted",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_compliance_officer_denied_individual_claims(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Review adjuster notes on disputed claim",
            "agent_role": "compliance_officer",
            "user_id": "u_test",
            "source_id": "individual_claims_data",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    # ── Retail ────────────────────────────────────────────────────────────────

    def test_personalization_agent_allowed_purchase_history(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Recommend products based on recent purchases",
            "agent_role": "personalization_agent",
            "user_id": "u_test",
            "source_id": "purchase_history",
            "sensitivity_level": "medium",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_personalization_agent_denied_payment_records(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Access payment method to tailor premium recommendations",
            "agent_role": "personalization_agent",
            "user_id": "u_test",
            "source_id": "payment_records",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    def test_shrinkage_detection_agent_allowed_inventory_deltas(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Flag unexplained inventory variance in aisle 7",
            "agent_role": "shrinkage_detection_agent",
            "user_id": "u_test",
            "source_id": "inventory_deltas",
            "sensitivity_level": "medium",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_shrinkage_detection_agent_denied_customer_pii(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Pull customer identity for suspected shoplifter",
            "agent_role": "shrinkage_detection_agent",
            "user_id": "u_test",
            "source_id": "customer_pii",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    # ── Energy ────────────────────────────────────────────────────────────────

    def test_grid_monitoring_agent_allowed_scada_feeds(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Check voltage deviation on transmission line 47B",
            "agent_role": "grid_monitoring_agent",
            "user_id": "u_test",
            "source_id": "scada_feeds",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_energy_trading_agent_denied_scada_feeds(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Access real-time grid state to inform trading position",
            "agent_role": "energy_trading_agent",
            "user_id": "u_test",
            "source_id": "scada_feeds",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    def test_energy_trading_agent_allowed_market_prices(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Pull day-ahead LMP for hedging analysis",
            "agent_role": "energy_trading_agent",
            "user_id": "u_test",
            "source_id": "market_prices",
            "sensitivity_level": "restricted",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    # ── Manufacturing ─────────────────────────────────────────────────────────

    def test_defect_detection_agent_allowed_sensor_data(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Inspect torque readings on assembly line 3",
            "agent_role": "defect_detection_agent",
            "user_id": "u_test",
            "source_id": "sensor_data",
            "sensitivity_level": "medium",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_procurement_agent_denied_quality_inspection_records(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Pull inspection failures to renegotiate supplier terms",
            "agent_role": "procurement_agent",
            "user_id": "u_test",
            "source_id": "quality_inspection_records",
            "sensitivity_level": "medium",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    # ── Real Estate ───────────────────────────────────────────────────────────

    def test_tenant_screening_agent_allowed_credit_reports(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Run credit check for lease applicant",
            "agent_role": "tenant_screening_agent",
            "user_id": "u_test",
            "source_id": "credit_reports",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_appraisal_agent_denied_borrower_financials(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Access borrower income to adjust appraisal",
            "agent_role": "appraisal_agent",
            "user_id": "u_test",
            "source_id": "borrower_financials",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    # ── Pharmacy ──────────────────────────────────────────────────────────────

    def test_prescription_verification_agent_allowed_rx_data(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Verify controlled substance Rx for DEA schedule compliance",
            "agent_role": "prescription_verification_agent",
            "user_id": "u_test",
            "source_id": "prescription_records",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_pharmacy_inventory_agent_denied_patient_records(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Access patient history to forecast drug demand",
            "agent_role": "pharmacy_inventory_agent",
            "user_id": "u_test",
            "source_id": "patient_records",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    def test_controlled_substance_agent_allowed_restricted_dispensing_logs(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Reconcile Schedule II dispense log for DEA Form 222",
            "agent_role": "controlled_substance_agent",
            "user_id": "u_test",
            "source_id": "dispensing_logs",
            "sensitivity_level": "restricted",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    # ── Public Sector ─────────────────────────────────────────────────────────

    def test_benefits_eligibility_agent_allowed_income_records(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Verify income threshold for SNAP eligibility",
            "agent_role": "benefits_eligibility_agent",
            "user_id": "u_test",
            "source_id": "income_records",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_benefits_eligibility_agent_denied_law_enforcement_data(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Cross-reference applicant against arrest records",
            "agent_role": "benefits_eligibility_agent",
            "user_id": "u_test",
            "source_id": "law_enforcement_data",
            "sensitivity_level": "restricted",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    def test_foia_response_agent_allowed_public_records(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Retrieve contract documents for FOIA request #2024-0147",
            "agent_role": "foia_response_agent",
            "user_id": "u_test",
            "source_id": "public_records",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "ALLOW"

    def test_foia_response_agent_denied_classified_data(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Include classified annex in FOIA disclosure package",
            "agent_role": "foia_response_agent",
            "user_id": "u_test",
            "source_id": "classified_data",
            "sensitivity_level": "restricted",
        })
        assert r.status_code == 200
        assert r.json()["decision"] == "DENY"

    # ── Cross-industry default deny ───────────────────────────────────────────

    def test_unknown_role_denied_regardless_of_source(self, real_client):
        for source in ("ehr_summaries", "alarm_feeds", "gps_telemetry", "credit_scores"):
            r = real_client.post("/v1/context/evaluate", json={
                "query": "test",
                "agent_role": "unknown_agent",
                "user_id": "u_test",
                "source_id": source,
                "sensitivity_level": "low",
            })
            assert r.json()["decision"] == "DENY", f"Expected DENY for source={source}"

    def test_healthcare_agent_denied_banking_source(self, real_client):
        """Healthcare role cannot access banking-specific sources."""
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Accessing credit scores from clinical agent",
            "agent_role": "clinical_summary_agent",
            "user_id": "u_test",
            "source_id": "credit_scores",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        # credit_scores not in allowed list for clinical_summary_agent → DENY
        assert r.json()["decision"] == "DENY"

    def test_banking_agent_denied_healthcare_source(self, real_client):
        """Banking role cannot access healthcare-specific sources."""
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Accessing EHR from loan underwriter",
            "agent_role": "loan_underwriter",
            "user_id": "u_test",
            "source_id": "ehr_summaries",
            "sensitivity_level": "high",
        })
        assert r.status_code == 200
        # ehr_summaries not in allowed_sources for loan_underwriter → DENY
        assert r.json()["decision"] == "DENY"


# ═══════════════════════════════════════════════════════════════════════════════
# 6. Task-level scoping for new multi-industry roles
# ═══════════════════════════════════════════════════════════════════════════════

class TestMultiIndustryTaskScoping:
    def test_fraud_analyst_allowed_task(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Flag suspicious transaction",
            "agent_role": "fraud_analyst",
            "user_id": "u_test",
            "source_id": "transaction_history",
            "sensitivity_level": "high",
            "task_type": "fraud_flag",
        })
        assert r.json()["decision"] == "ALLOW"

    def test_fraud_analyst_denied_task_credit_decision(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Make credit decision",
            "agent_role": "fraud_analyst",
            "user_id": "u_test",
            "source_id": "transaction_history",
            "sensitivity_level": "high",
            "task_type": "credit_decision",
        })
        assert r.json()["decision"] == "DENY"

    def test_triage_agent_allowed_task(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "ESI score assessment",
            "agent_role": "triage_agent",
            "user_id": "u_test",
            "source_id": "symptom_intake",
            "sensitivity_level": "medium",
            "task_type": "triage_assessment",
        })
        assert r.json()["decision"] == "ALLOW"

    def test_triage_agent_denied_task_diagnosis_entry(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Enter diagnosis code",
            "agent_role": "triage_agent",
            "user_id": "u_test",
            "source_id": "symptom_intake",
            "sensitivity_level": "medium",
            "task_type": "diagnosis_entry",
        })
        assert r.json()["decision"] == "DENY"

    def test_collections_agent_denied_task_fraud_flag(self, real_client):
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Flag account for fraud",
            "agent_role": "collections_agent",
            "user_id": "u_test",
            "source_id": "delinquency_records",
            "sensitivity_level": "medium",
            "task_type": "fraud_flag",
        })
        assert r.json()["decision"] == "DENY"

    def test_route_optimization_agent_no_task_type_allowed(self, real_client):
        """No task_type provided — task constraints not applied."""
        r = real_client.post("/v1/context/evaluate", json={
            "query": "Route calculation",
            "agent_role": "route_optimization_agent",
            "user_id": "u_test",
            "source_id": "gps_telemetry",
            "sensitivity_level": "low",
        })
        assert r.json()["decision"] == "ALLOW"
