"""Tests for policy loader and policy enforcer."""

import json
from pathlib import Path

from apiposture_pro.enterprise.policy.policy_enforcer import PolicyResult, enforce_policy
from apiposture_pro.enterprise.policy.policy_loader import PolicyConfig, load_policy

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


def write_policy(tmp_path: Path, policy: dict) -> Path:
    """Write a policy JSON file to tmp_path and return the directory."""
    p = tmp_path / ".apiposture-policy.json"
    p.write_text(json.dumps(policy), encoding="utf-8")
    return tmp_path


def make_finding(rule_id: str, severity: str) -> dict:
    return {"rule_id": rule_id, "severity": severity}


# ── load_policy tests ─────────────────────────────────────────────────────────


def test_load_policy_not_found(tmp_path):
    result = load_policy(tmp_path)
    assert result is None


def test_load_policy_valid(tmp_path):
    write_policy(tmp_path, {
        "zero_tolerance": ["AP101", "AP201"],
        "warn": ["AP001"],
        "fail_on": "high",
    })
    cfg = load_policy(tmp_path)
    assert cfg is not None
    assert isinstance(cfg, PolicyConfig)
    assert "AP101" in cfg.zero_tolerance
    assert "AP201" in cfg.zero_tolerance
    assert cfg.fail_on == "high"


def test_load_policy_defaults_for_missing_keys(tmp_path):
    write_policy(tmp_path, {})
    cfg = load_policy(tmp_path)
    assert cfg is not None
    assert cfg.zero_tolerance == []
    assert cfg.warn == []
    assert cfg.fail_on == "high"


def test_load_policy_invalid_fail_on_defaults_to_high(tmp_path):
    write_policy(tmp_path, {"fail_on": "nonsense"})
    cfg = load_policy(tmp_path)
    assert cfg.fail_on == "high"


def test_load_policy_warn_list(tmp_path):
    write_policy(tmp_path, {"warn": ["AP001", "AP002"]})
    cfg = load_policy(tmp_path)
    assert "AP001" in cfg.warn
    assert "AP002" in cfg.warn


# ── enforce_policy tests ──────────────────────────────────────────────────────


def test_enforce_no_findings_no_fail(tmp_path):
    write_policy(tmp_path, {"zero_tolerance": ["AP101"], "warn": [], "fail_on": "high"})
    cfg = load_policy(tmp_path)
    result = enforce_policy(cfg, [])
    assert result.should_fail is False
    assert isinstance(result, PolicyResult)


def test_enforce_zero_tolerance_fires(tmp_path):
    write_policy(tmp_path, {"zero_tolerance": ["AP101"], "warn": [], "fail_on": "critical"})
    cfg = load_policy(tmp_path)
    findings = [make_finding("AP101", "high")]
    result = enforce_policy(cfg, findings)
    assert result.should_fail is True
    assert "AP101" in result.zero_tolerance_violations


def test_enforce_fail_on_threshold_high(tmp_path):
    write_policy(tmp_path, {"zero_tolerance": [], "warn": [], "fail_on": "high"})
    cfg = load_policy(tmp_path)
    findings = [make_finding("AP002", "high")]
    result = enforce_policy(cfg, findings)
    assert result.should_fail is True
    assert len(result.severity_threshold_violations) == 1


def test_enforce_fail_on_threshold_critical_included(tmp_path):
    write_policy(tmp_path, {"zero_tolerance": [], "warn": [], "fail_on": "high"})
    cfg = load_policy(tmp_path)
    findings = [make_finding("AP101", "critical")]
    result = enforce_policy(cfg, findings)
    assert result.should_fail is True


def test_enforce_below_threshold_no_fail(tmp_path):
    write_policy(tmp_path, {"zero_tolerance": [], "warn": [], "fail_on": "critical"})
    cfg = load_policy(tmp_path)
    findings = [make_finding("AP001", "high")]
    result = enforce_policy(cfg, findings)
    assert result.should_fail is False


def test_enforce_warn_only_no_fail(tmp_path):
    write_policy(tmp_path, {"zero_tolerance": [], "warn": ["AP001"], "fail_on": "critical"})
    cfg = load_policy(tmp_path)
    findings = [make_finding("AP001", "high")]
    result = enforce_policy(cfg, findings)
    assert result.should_fail is False
    assert "AP001" in result.warnings


def test_enforce_zero_tolerance_deduplicates(tmp_path):
    write_policy(tmp_path, {"zero_tolerance": ["AP101"], "warn": [], "fail_on": "critical"})
    cfg = load_policy(tmp_path)
    findings = [make_finding("AP101", "high"), make_finding("AP101", "medium")]
    result = enforce_policy(cfg, findings)
    # Rule appears twice but should only appear once in violations list
    assert result.zero_tolerance_violations.count("AP101") == 1


def test_enforce_medium_threshold(tmp_path):
    write_policy(tmp_path, {"zero_tolerance": [], "warn": [], "fail_on": "medium"})
    cfg = load_policy(tmp_path)
    findings = [make_finding("AP007", "medium")]
    result = enforce_policy(cfg, findings)
    assert result.should_fail is True


def test_enforce_low_does_not_fail_on_medium_threshold(tmp_path):
    write_policy(tmp_path, {"zero_tolerance": [], "warn": [], "fail_on": "medium"})
    cfg = load_policy(tmp_path)
    findings = [make_finding("AP001", "low")]
    result = enforce_policy(cfg, findings)
    assert result.should_fail is False
