"""Plugin signature enforcement policy (ADR-013 Phase 2).

One PluginVerifier is built per app from config and shared by the loader
(verify-at-load) and the migration manager (verify-at-migrate). A plugin only
loads or migrates when it verifies against a trusted key - so signing stops
being advisory and becomes enforced everywhere plugin code executes.

Default is OFF (PLUGIN_REQUIRE_SIGNED unset), so existing deploys are
unchanged. A site opts into hardening by stamping its plugins
(`flask plugin stamp-bundled`), pinning keys (PLUGIN_TRUSTED_KEYS), and setting
PLUGIN_REQUIRE_SIGNED. Fail-closed: an unverifiable plugin does NOT run.
"""

import logging
from pathlib import Path

from . import signing, packaging

logger = logging.getLogger(__name__)


class PluginVerifier:
    """Decides whether a plugin may load/migrate under the site's trust policy."""

    def __init__(self, plugins_dir, require_signed=False, trusted_key_paths=None,
                 dev_trust_dirs=None, is_dev=False):
        self.plugins_dir = Path(plugins_dir)
        self.require_signed = bool(require_signed)
        self.is_dev = bool(is_dev)
        # dev trust dirs only mean anything in dev/test (ADR-013); prod ignores.
        self.dev_trust_dirs = [Path(p).resolve() for p in (dev_trust_dirs or [])]
        self._keys = signing.load_trusted_keys(trusted_key_paths or [])

    def _dev_exempt(self, plugin_dir: Path) -> bool:
        # unsigned dev/external-repo plugins pass, but ONLY under DEBUG/TESTING
        if not self.is_dev or not self.dev_trust_dirs:
            return False
        resolved = Path(plugin_dir).resolve()
        for trusted in self.dev_trust_dirs:
            try:
                resolved.relative_to(trusted)
                return True
            except ValueError:
                continue
        return False

    def check(self, plugin_name: str):
        """(ok, reason). ok True = plugin may load/migrate."""
        if not self.require_signed:
            return True, 'enforcement off'

        plugin_dir = self.plugins_dir / plugin_name
        if self._dev_exempt(plugin_dir):
            return True, 'dev-trusted'

        if not self._keys:
            # require-signed with nothing to verify against = refuse everything.
            return False, ('PLUGIN_REQUIRE_SIGNED is set but no trusted keys '
                           'are configured')

        _, errors = packaging.verify_dir(plugin_dir, self._keys)
        if errors:
            return False, '; '.join(errors)
        return True, 'verified'
