"""Tests for YAML policy loading."""

import os
import tempfile

import pytest

from pyauthz import ConfigError, PolicyVersionError, PyAuthz, SchemaError


def _write_yaml(content: str, suffix: str = ".yaml") -> str:
    """Write YAML content to a temp file and return the path."""
    fd, path = tempfile.mkstemp(suffix=suffix)
    with os.fdopen(fd, "w") as f:
        f.write(content)
    return path


def _write_yaml_dir(files: dict[str, str]) -> str:
    """Write multiple YAML files to a temp directory."""
    d = tempfile.mkdtemp()
    for name, content in files.items():
        with open(os.path.join(d, name), "w") as f:
            f.write(content)
    return d


VALID_YAML = """\
version: 1

roles:
  - name: admin
    permissions: ["*:*"]
  - name: editor
    permissions: [documents:write, documents:read]

policies:
  - name: admin-full-access
    description: Admins can do anything
    effect: allow
    roles: [admin]
    permissions: ["*:*"]

  - name: deny-pii
    effect: deny
    roles: [editor]
    permissions: ["pii:*"]
    priority: 100
"""


class TestYamlLoading:
    def test_load_single_file(self):
        path = _write_yaml(VALID_YAML)
        try:
            rp = PyAuthz()
            rp.load_policies(path)
            # Roles should be loaded
            from pyauthz.models.resource import Resource
            from pyauthz.models.subject import Subject

            admin = Subject(id="a", roles=["admin"])
            doc = Resource(id="d", type="documents")
            assert rp.is_allowed(admin, "read", doc) is True
        finally:
            os.unlink(path)

    def test_load_directory(self):
        d = _write_yaml_dir({"roles.yaml": VALID_YAML})
        try:
            rp = PyAuthz()
            rp.load_policies(d)
            from pyauthz.models.resource import Resource
            from pyauthz.models.subject import Subject

            admin = Subject(id="a", roles=["admin"])
            doc = Resource(id="d", type="documents")
            assert rp.is_allowed(admin, "read", doc) is True
        finally:
            import shutil

            shutil.rmtree(d)

    def test_missing_version_raises(self):
        path = _write_yaml("roles: []")
        try:
            rp = PyAuthz()
            with pytest.raises(PolicyVersionError):
                rp.load_policies(path)
        finally:
            os.unlink(path)

    def test_unsupported_version_raises(self):
        path = _write_yaml("version: 99\nroles: []")
        try:
            rp = PyAuthz()
            with pytest.raises(PolicyVersionError):
                rp.load_policies(path)
        finally:
            os.unlink(path)

    def test_non_mapping_yaml_raises(self):
        path = _write_yaml("- just a list")
        try:
            rp = PyAuthz()
            with pytest.raises(SchemaError, match="Expected a YAML mapping"):
                rp.load_policies(path)
        finally:
            os.unlink(path)

    def test_nonexistent_path_raises(self):
        rp = PyAuthz()
        with pytest.raises(ConfigError, match="does not exist"):
            rp.load_policies("/nonexistent/path/to/policies")

    def test_empty_directory_raises(self):
        d = tempfile.mkdtemp()
        try:
            rp = PyAuthz()
            with pytest.raises(ConfigError, match="No YAML"):
                rp.load_policies(d)
        finally:
            os.rmdir(d)

    def test_invalid_effect_raises(self):
        path = _write_yaml("""\
version: 1
policies:
  - name: bad
    effect: maybe
    permissions: ["*:*"]
""")
        try:
            rp = PyAuthz()
            with pytest.raises(SchemaError, match="invalid effect"):
                rp.load_policies(path)
        finally:
            os.unlink(path)

    def test_missing_policy_name_raises(self):
        path = _write_yaml("""\
version: 1
policies:
  - effect: allow
    permissions: ["*:*"]
""")
        try:
            rp = PyAuthz()
            with pytest.raises(SchemaError, match="missing required field 'name'"):
                rp.load_policies(path)
        finally:
            os.unlink(path)

    def test_missing_role_name_raises(self):
        path = _write_yaml("""\
version: 1
roles:
  - permissions: ["*:*"]
""")
        try:
            rp = PyAuthz()
            with pytest.raises(SchemaError, match="missing required field 'name'"):
                rp.load_policies(path)
        finally:
            os.unlink(path)

    def test_conditions_parsed(self):
        path = _write_yaml("""\
version: 1
policies:
  - name: owner-only
    effect: allow
    roles: [editor]
    permissions: [documents:write]
    conditions:
      - field: resource.owner_id
        op: "=="
        value: subject.id
""")
        try:
            rp = PyAuthz()
            rp.load_policies(path)
            # Verify the condition was parsed (engine stores it)
        finally:
            os.unlink(path)

    def test_yml_extension_supported(self):
        d = _write_yaml_dir({"test.yml": VALID_YAML})
        try:
            rp = PyAuthz()
            rp.load_policies(d)
        finally:
            import shutil

            shutil.rmtree(d)
