"""Filesystem policy checker — enforce read-only, no-write, and allowed-write rules."""

from __future__ import annotations

from fnmatch import fnmatch

from nestdx.config.schema import FilesystemPolicy
from nestdx.session.models import FileChange, PolicyViolation


def _matches_any(path: str, patterns: list[str]) -> str | None:
    """Return the first matching glob pattern, or None."""
    for pattern in patterns:
        if fnmatch(path, pattern):
            return pattern
        # Also match against just the filename for patterns like "*.pem"
        parts = path.split("/")
        if any(fnmatch(part, pattern) for part in parts):
            return pattern
        # Match directory prefixes: "secrets/" should match "secrets/key.pem"
        if pattern.endswith("/") and path.startswith(pattern):
            return pattern
    return None


class FilesystemChecker:
    """Check file changes against filesystem policies."""

    def __init__(self, policy: FilesystemPolicy):
        self.policy = policy

    def check(self, file_changes: list[FileChange]) -> list[PolicyViolation]:
        """Check all file changes against filesystem policy rules."""
        violations: list[PolicyViolation] = []

        for change in file_changes:
            path = change.path

            # Skip deleted files — we only care about writes
            if change.change_type == "deleted":
                continue

            # Check read_only paths
            matched = _matches_any(path, self.policy.read_only)
            if matched:
                violations.append(
                    PolicyViolation(
                        rule="filesystem.read_only",
                        severity="error",
                        message=f"Write to read-only path: '{path}' matches '{matched}'",
                        file=path,
                    )
                )
                continue  # Don't double-report

            # Check no_write paths
            matched = _matches_any(path, self.policy.no_write)
            if matched:
                violations.append(
                    PolicyViolation(
                        rule="filesystem.no_write",
                        severity="error",
                        message=f"Write to restricted path: '{path}' matches '{matched}'",
                        file=path,
                    )
                )
                continue

            # Check allowed_write (if configured)
            if self.policy.allowed_write:
                matched = _matches_any(path, self.policy.allowed_write)
                if not matched:
                    violations.append(
                        PolicyViolation(
                            rule="filesystem.allowed_write",
                            severity="warning",
                            message=f"Write outside allowed paths: '{path}'",
                            file=path,
                        )
                    )

        return violations
