"""Tests for policy YAML parser."""

import pytest
import tempfile
from pathlib import Path
from datetime import timedelta

from hieromem_core.policy.parser import (
    PolicyParser,
    PolicyParseError,
    PolicyValidationError,
    load_policy,
)
from hieromem_core.policy.schema import DecayCurve


VALID_POLICY_YAML = """
apiVersion: hieromem/v1
kind: MemoryPolicy
metadata:
  name: test-policy
  namespace: test-ns
  version: "1.0"
  description: "Test policy"
spec:
  enabled: true
  retention:
    horizon: 30
    decay_curve: exponential
    base_lambda: 0.95
    min_trust: 0.1
  summarization:
    max_episodes: 100
    depth: 2
  graph:
    edge_ttl: 60
    prune_orphans: true
    max_hops: 5
  governance:
    forbidden_relations:
      - kills
      - harms
    max_episodes: 10000
  priority:
    high_priority_patterns:
      - urgent
    critical_tags:
      - important
"""

MINIMAL_POLICY_YAML = """
apiVersion: hieromem/v1
kind: MemoryPolicy
metadata:
  name: minimal
spec:
  enabled: true
"""


class TestPolicyParser:
    """Tests for PolicyParser."""

    def test_parse_valid_yaml(self) -> None:
        parser = PolicyParser()
        policy = parser.parse_string(VALID_POLICY_YAML)

        assert policy.name == "test-policy"
        assert policy.namespace == "test-ns"
        assert policy.version == "1.0"
        assert policy.retention.horizon == timedelta(days=30)
        assert policy.retention.decay_curve == DecayCurve.EXPONENTIAL
        assert policy.retention.base_lambda == 0.95
        assert policy.summarization.max_episodes == 100
        assert policy.graph.max_hops == 5
        assert "kills" in policy.governance.forbidden_relations
        assert "urgent" in policy.priority.high_priority_patterns
        assert "important" in policy.priority.critical_tags

    def test_parse_minimal_yaml(self) -> None:
        parser = PolicyParser()
        policy = parser.parse_string(MINIMAL_POLICY_YAML)

        assert policy.name == "minimal"
        assert policy.namespace == "*"  # Default
        assert policy.enabled is True
        # All sub-policies have defaults
        assert policy.retention.base_lambda == 0.95
        assert policy.summarization.max_episodes == 100

    def test_parse_file(self) -> None:
        parser = PolicyParser()

        with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
            f.write(VALID_POLICY_YAML)
            f.flush()
            path = Path(f.name)

        try:
            policy = parser.parse_file(path)
            assert policy.name == "test-policy"
        finally:
            path.unlink()

    def test_file_not_found(self) -> None:
        parser = PolicyParser()
        with pytest.raises(PolicyParseError, match="File not found"):
            parser.parse_file("/nonexistent/path.yaml")

    def test_invalid_yaml(self) -> None:
        parser = PolicyParser()
        with pytest.raises(PolicyParseError, match="Invalid YAML"):
            parser.parse_string("{ invalid yaml [")

    def test_empty_yaml(self) -> None:
        parser = PolicyParser()
        with pytest.raises(PolicyParseError, match="Empty YAML"):
            parser.parse_string("")

    def test_missing_api_version(self) -> None:
        parser = PolicyParser()
        with pytest.raises(PolicyValidationError, match="apiVersion is required"):
            parser.parse_string("""
kind: MemoryPolicy
metadata:
  name: test
spec:
  enabled: true
""")

    def test_unsupported_api_version(self) -> None:
        parser = PolicyParser()
        with pytest.raises(PolicyValidationError, match="Unsupported apiVersion"):
            parser.parse_string("""
apiVersion: hieromem/v99
kind: MemoryPolicy
metadata:
  name: test
spec:
  enabled: true
""")

    def test_invalid_kind(self) -> None:
        parser = PolicyParser()
        with pytest.raises(PolicyValidationError, match="Invalid kind"):
            parser.parse_string("""
apiVersion: hieromem/v1
kind: SomethingElse
metadata:
  name: test
spec:
  enabled: true
""")

    def test_missing_name(self) -> None:
        parser = PolicyParser()
        with pytest.raises(PolicyValidationError, match="Policy name is required"):
            parser.parse_string("""
apiVersion: hieromem/v1
kind: MemoryPolicy
metadata:
  namespace: test
spec:
  enabled: true
""")

    def test_invalid_decay_curve(self) -> None:
        parser = PolicyParser()
        with pytest.raises(PolicyValidationError, match="Invalid decay_curve"):
            parser.parse_string("""
apiVersion: hieromem/v1
kind: MemoryPolicy
metadata:
  name: test
spec:
  retention:
    decay_curve: invalid_curve
""")

    def test_invalid_base_lambda_range(self) -> None:
        parser = PolicyParser()
        with pytest.raises(PolicyValidationError, match="base_lambda must be between"):
            parser.parse_string("""
apiVersion: hieromem/v1
kind: MemoryPolicy
metadata:
  name: test
spec:
  retention:
    base_lambda: 1.5
""")

    def test_invalid_max_hops(self) -> None:
        parser = PolicyParser()
        with pytest.raises(PolicyValidationError, match="max_hops must be at least 1"):
            parser.parse_string("""
apiVersion: hieromem/v1
kind: MemoryPolicy
metadata:
  name: test
spec:
  graph:
    max_hops: 0
""")

    def test_load_directory(self) -> None:
        parser = PolicyParser()

        with tempfile.TemporaryDirectory() as tmpdir:
            # Create test files
            (Path(tmpdir) / "policy1.yaml").write_text("""
apiVersion: hieromem/v1
kind: MemoryPolicy
metadata:
  name: policy1
spec:
  enabled: true
""")
            (Path(tmpdir) / "policy2.yml").write_text("""
apiVersion: hieromem/v1
kind: MemoryPolicy
metadata:
  name: policy2
spec:
  enabled: true
""")

            policies = parser.load_directory(tmpdir)
            assert len(policies) == 2
            names = {p.name for p in policies}
            assert "policy1" in names
            assert "policy2" in names


class TestLoadPolicy:
    """Tests for load_policy convenience function."""

    def test_load_from_string(self) -> None:
        policy = load_policy(MINIMAL_POLICY_YAML)
        assert policy.name == "minimal"

    def test_load_from_dict(self) -> None:
        policy = load_policy({
            "apiVersion": "hieromem/v1",
            "kind": "MemoryPolicy",
            "metadata": {"name": "dict-policy"},
            "spec": {"enabled": True},
        })
        assert policy.name == "dict-policy"

    def test_load_from_file(self) -> None:
        with tempfile.NamedTemporaryFile(mode="w", suffix=".yaml", delete=False) as f:
            f.write(MINIMAL_POLICY_YAML)
            f.flush()
            path = f.name

        try:
            policy = load_policy(path)
            assert policy.name == "minimal"
        finally:
            Path(path).unlink()
