"""Tests for the PolicyWatcher (YAML hot-reload)."""

import time

import pytest

from pyauthz import PyAuthz, Resource, Role, Subject
from pyauthz.storage.watcher import PolicyWatcher


@pytest.fixture
def tmp_policy_dir(tmp_path):
    """Create a temp directory with a valid YAML policy file."""
    policy_file = tmp_path / "roles.yaml"
    policy_file.write_text("version: 1\nroles:\n  - name: admin\n    permissions: ['*:*']\n")
    return tmp_path


class TestPolicyWatcher:
    def test_start_and_stop(self, tmp_policy_dir):
        rp = PyAuthz()
        watcher = PolicyWatcher(rp, str(tmp_policy_dir), interval=0.1)
        watcher.start()
        assert watcher.is_running is True
        watcher.stop()
        assert watcher.is_running is False

    def test_double_start_is_safe(self, tmp_policy_dir):
        rp = PyAuthz()
        watcher = PolicyWatcher(rp, str(tmp_policy_dir), interval=0.1)
        watcher.start()
        watcher.start()  # Should not create a second thread
        assert watcher.is_running is True
        watcher.stop()

    def test_detects_file_change(self, tmp_policy_dir):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))

        watcher = PolicyWatcher(rp, str(tmp_policy_dir), interval=0.1)
        watcher.start()

        # Modify the policy file to add a viewer role
        policy_file = tmp_policy_dir / "roles.yaml"
        policy_file.write_text(
            "version: 1\n"
            "roles:\n"
            "  - name: admin\n"
            "    permissions: ['*:*']\n"
            "  - name: viewer\n"
            "    permissions: ['documents:read']\n"
        )

        # Wait for the watcher to pick up the change
        time.sleep(0.5)
        watcher.stop()

        # Verify the new role was loaded
        viewer = Subject(id="alice", roles=["viewer"])
        doc = Resource(id="d1", type="documents")
        assert rp.is_allowed(viewer, "read", doc) is True

    def test_invalid_yaml_keeps_previous(self, tmp_policy_dir):
        rp = PyAuthz()
        rp.add_role(Role(name="admin", permissions=["*:*"]))

        watcher = PolicyWatcher(rp, str(tmp_policy_dir), interval=0.1)
        watcher.start()

        # Write invalid YAML
        policy_file = tmp_policy_dir / "roles.yaml"
        policy_file.write_text("- not a valid policy file")

        time.sleep(0.5)
        watcher.stop()

        # Previous role should still work (admin was loaded via replace_roles from
        # the initial valid file, but the invalid reload should be skipped)
        # The admin role exists because it was added before the watcher started
        admin = Subject(id="alice", roles=["admin"])
        doc = Resource(id="d1", type="documents")
        assert rp.is_allowed(admin, "read", doc) is True

    def test_watcher_on_single_file(self, tmp_policy_dir):
        policy_file = tmp_policy_dir / "roles.yaml"
        rp = PyAuthz()
        watcher = PolicyWatcher(rp, str(policy_file), interval=0.1)
        watcher.start()
        assert watcher.is_running is True
        watcher.stop()
