"""
Security and Compliance for Context7 Integration

Provides security audit, compliance verification, and API key management.
"""

import hashlib
import logging
import os
from dataclasses import asdict, dataclass
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

import yaml

try:
    from cryptography.fernet import Fernet

    CRYPTO_AVAILABLE = True
except ImportError:
    CRYPTO_AVAILABLE = False


logger = logging.getLogger(__name__)


@dataclass
class SecurityAuditResult:
    """Result of a security audit."""

    passed: bool
    issues: list[str]
    warnings: list[str]
    recommendations: list[str]
    timestamp: str
    compliance_status: dict[str, bool]

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return asdict(self)


@dataclass
class ComplianceStatus:
    """SOC 2 compliance status."""

    soc2_verified: bool = False
    data_retention_compliant: bool = False
    audit_logging_enabled: bool = False
    api_key_encrypted: bool = False
    privacy_mode_enabled: bool = False

    def to_dict(self) -> dict[str, Any]:
        """Convert to dictionary."""
        return asdict(self)


class APIKeyManager:
    """Manages API keys with encryption support."""

    def __init__(self, config_dir: Path | None = None):
        """
        Initialize API key manager.

        Args:
            config_dir: Configuration directory (default: .tapps-agents)
        """
        if config_dir is None:
            config_dir = Path.cwd() / ".tapps-agents"

        self.config_dir = Path(config_dir)
        self.config_dir.mkdir(parents=True, exist_ok=True)

        self.keys_file = self.config_dir / "api-keys.encrypted"
        self.master_key_file = self.config_dir / ".master-key"

        self._master_key: bytes | None = None
        self._cipher: Fernet | None = None

        if CRYPTO_AVAILABLE:
            self._load_or_create_master_key()

    def _load_or_create_master_key(self):
        """Load or create master encryption key."""
        if not CRYPTO_AVAILABLE:
            return

        try:
            if self.master_key_file.exists():
                # Load existing key
                self._master_key = self.master_key_file.read_bytes()
            else:
                # Generate new key
                self._master_key = Fernet.generate_key()
                # Store with restricted permissions (owner read/write only)
                self.master_key_file.write_bytes(self._master_key)
                try:
                    os.chmod(self.master_key_file, 0o600)
                except Exception:
                    # Windows may not support chmod; best-effort only.
                    logger.debug("chmod not supported for master key file", exc_info=True)

            if self._master_key:
                self._cipher = Fernet(self._master_key)
        except Exception as e:
            # If master key initialization fails, log and continue without encryption
            logger.warning(
                f"Failed to initialize encryption master key: {e}. "
                f"API keys will not be encrypted. This is non-fatal."
            )
            self._master_key = None
            self._cipher = None

    def encrypt_api_key(self, api_key: str) -> str:
        """
        Encrypt an API key.

        Args:
            api_key: Plain text API key

        Returns:
            Encrypted API key (base64)
        """
        if not CRYPTO_AVAILABLE:
            # Fallback: hash the key (one-way, not reversible)
            return hashlib.sha256(api_key.encode()).hexdigest()

        if not self._cipher:
            self._load_or_create_master_key()

        if self._cipher is None:
            raise ValueError("Encryption not available. Failed to initialize cipher.")

        encrypted = self._cipher.encrypt(api_key.encode())
        return encrypted.decode("utf-8")

    def decrypt_api_key(self, encrypted_key: str) -> str:
        """
        Decrypt an API key.

        Args:
            encrypted_key: Encrypted API key

        Returns:
            Plain text API key
        """
        if not CRYPTO_AVAILABLE:
            raise ValueError("Encryption not available. Install cryptography package.")

        if not self._cipher:
            self._load_or_create_master_key()

        if self._cipher is None:
            raise ValueError("Encryption not available. Failed to initialize cipher.")

        decrypted = self._cipher.decrypt(encrypted_key.encode())
        return decrypted.decode("utf-8")

    def store_api_key(self, key_name: str, api_key: str, encrypt: bool = True):
        """
        Store an API key.

        Args:
            key_name: Name of the key (e.g., "context7", "anthropic")
            api_key: API key value
            encrypt: Whether to encrypt the key
        """
        keys = self.load_all_keys()

        if encrypt:
            keys[key_name] = {
                "encrypted": True,
                "value": self.encrypt_api_key(api_key),
                "stored_at": datetime.now(UTC).isoformat(),
            }
        else:
            keys[key_name] = {
                "encrypted": False,
                "value": api_key,
                "stored_at": datetime.now(UTC).isoformat(),
            }

        # Save to file
        with open(self.keys_file, "w", encoding="utf-8") as f:
            yaml.safe_dump(keys, f, default_flow_style=False)

        # Restrict file permissions
        try:
            os.chmod(self.keys_file, 0o600)
        except Exception:
            # Best-effort only (Windows may not support chmod).
            logger.debug("chmod not supported for keys file", exc_info=True)

    def load_api_key(self, key_name: str) -> str | None:
        """
        Load an API key.

        Args:
            key_name: Name of the key

        Returns:
            API key value or None if not found
        """
        keys = self.load_all_keys()

        if key_name not in keys:
            return None

        key_data = keys[key_name]

        if key_data.get("encrypted"):
            try:
                return self.decrypt_api_key(key_data["value"])
            except Exception:
                return None
        else:
            return key_data.get("value")

    def load_all_keys(self) -> dict[str, Any]:
        """Load all stored API keys."""
        if not self.keys_file.exists():
            return {}

        try:
            with open(self.keys_file, encoding="utf-8") as f:
                return yaml.safe_load(f) or {}
        except Exception:
            return {}

    def delete_api_key(self, key_name: str):
        """Delete a stored API key."""
        keys = self.load_all_keys()
        if key_name in keys:
            del keys[key_name]
            with open(self.keys_file, "w", encoding="utf-8") as f:
                yaml.safe_dump(keys, f, default_flow_style=False)


class SecurityAuditor:
    """Performs security audits and compliance verification."""

    def __init__(self, config_dir: Path | None = None, cache_dir: Path | None = None):
        """
        Initialize security auditor.

        Args:
            config_dir: Configuration directory
            cache_dir: Cache directory
        """
        if config_dir is None:
            config_dir = Path.cwd() / ".tapps-agents"
        if cache_dir is None:
            cache_dir = config_dir / "kb" / "context7-cache"

        self.config_dir = Path(config_dir)
        self.cache_dir = Path(cache_dir)
        self.api_key_manager = APIKeyManager(config_dir)

    def audit(self) -> SecurityAuditResult:
        """
        Perform security audit.

        Returns:
            SecurityAuditResult with audit findings
        """
        issues = []
        warnings = []
        recommendations = []
        compliance = ComplianceStatus()

        # Check API key storage
        if not CRYPTO_AVAILABLE:
            warnings.append(
                "cryptography package not installed - API keys cannot be encrypted"
            )
            recommendations.append(
                "Install cryptography package: pip install cryptography"
            )
        else:
            compliance.api_key_encrypted = True

        # Check environment variable usage
        env_keys = ["CONTEXT7_API_KEY", "ANTHROPIC_API_KEY", "OPENAI_API_KEY"]

        env_keys_found = [key for key in env_keys if os.getenv(key)]
        if env_keys_found:
            warnings.append(
                f"API keys found in environment variables: {', '.join(env_keys_found)}"
            )
            recommendations.append(
                "Consider using encrypted storage instead of environment variables"
            )

        # Check file permissions
        keys_file = self.config_dir / "api-keys.encrypted"
        if keys_file.exists():
            try:
                stat = keys_file.stat()
                mode = stat.st_mode & 0o777
                if mode > 0o600:
                    issues.append(
                        f"API keys file has insecure permissions: {oct(mode)}"
                    )
                    recommendations.append(
                        "Set file permissions to 600 (owner read/write only)"
                    )
            except Exception:
                logger.debug("Failed to stat API keys file permissions", exc_info=True)

        # Check cache directory permissions
        if self.cache_dir.exists():
            try:
                stat = self.cache_dir.stat()
                mode = stat.st_mode & 0o777
                if mode > 0o755:
                    warnings.append(
                        f"Cache directory has open permissions: {oct(mode)}"
                    )
            except Exception:
                logger.debug(
                    "Failed to stat cache directory permissions", exc_info=True
                )

        # Check for sensitive data in cache
        # (This is a simplified check - in production, scan for patterns)

        # Compliance checks
        compliance.audit_logging_enabled = True  # Assume enabled if audit is running
        compliance.data_retention_compliant = (
            True  # Assume compliant if cache cleanup exists
        )
        compliance.privacy_mode_enabled = True  # Context7 queries stay local by design

        # SOC 2 verification (simplified - would need actual certification)
        compliance.soc2_verified = len(issues) == 0 and len(warnings) < 3

        passed = len(issues) == 0

        return SecurityAuditResult(
            passed=passed,
            issues=issues,
            warnings=warnings,
            recommendations=recommendations,
            timestamp=datetime.now(UTC).isoformat(),
            compliance_status=compliance.to_dict(),
        )

    def verify_compliance(self) -> ComplianceStatus:
        """
        Verify SOC 2 compliance.

        Returns:
            ComplianceStatus
        """
        audit = self.audit()
        return ComplianceStatus(**audit.compliance_status)


def create_security_auditor(
    config_dir: Path | None = None, cache_dir: Path | None = None
) -> SecurityAuditor:
    """
    Convenience function to create a security auditor.

    Args:
        config_dir: Configuration directory
        cache_dir: Cache directory

    Returns:
        SecurityAuditor instance
    """
    return SecurityAuditor(config_dir=config_dir, cache_dir=cache_dir)
