"""
Audit logging for proxy security hardening.

This module provides structured audit logging for all proxy interactions,
including request/response hashing, secret detection events, and user actions.
"""

import hashlib
import json
import uuid
from datetime import UTC, datetime
from pathlib import Path
from typing import Any

from vibectl.logutil import logger

from .config import SecurityConfig
from .sanitizer import DetectedSecret


class AuditLogger:
    """Structured audit logger for proxy security events."""

    def __init__(self, config: SecurityConfig, proxy_profile: str | None = None):
        """Initialize audit logger.

        Args:
            config: Security configuration containing audit settings
            proxy_profile: Name of the proxy profile being used
        """
        self.config = config
        self.proxy_profile = proxy_profile
        self.enabled = config.audit_logging

        # Determine audit log path
        self.log_path = self._get_audit_log_path()

        # Create audit log directory if it doesn't exist
        if self.enabled and self.log_path:
            self.log_path.parent.mkdir(parents=True, exist_ok=True)

    def _get_audit_log_path(self) -> Path | None:
        """Get the audit log file path."""
        if not self.enabled:
            return None

        # Use configured path if available
        if self.config.audit_log_path:
            return Path(self.config.audit_log_path).expanduser()

        # Default path: ~/.config/vibectl/audit.log
        # Or profile-specific: ~/.config/vibectl/audit-<profile>.log
        config_dir = Path.home() / ".config" / "vibectl"
        if self.proxy_profile:
            return config_dir / f"audit-{self.proxy_profile}.log"
        else:
            return config_dir / "audit.log"

    def _hash_content(self, content: str | None) -> str:
        """Create SHA256 hash of content."""
        if not content:
            return "null"
        return hashlib.sha256(content.encode("utf-8")).hexdigest()

    def log_llm_request(
        self,
        request_id: str | None = None,
        request_content: str | None = None,
        response_content: str | None = None,
        secrets_detected: list[DetectedSecret] | None = None,
        command_generated: str | None = None,
        user_approved: bool | None = None,
        model_used: str | None = None,
        additional_metadata: dict[str, Any] | None = None,
    ) -> None:
        """Log an LLM request event.

        Args:
            request_id: Unique identifier for the request
            request_content: Content of the request (will be hashed)
            response_content: Content of the response (will be hashed)
            secrets_detected: List of secrets detected in the request
            command_generated: Command generated by the LLM
            user_approved: Whether the user approved the command
            model_used: Name of the LLM model used
            additional_metadata: Additional metadata to include
        """
        if not self.enabled:
            return

        # Generate request ID if not provided
        if not request_id:
            request_id = str(uuid.uuid4())

        # Prepare secrets information
        secrets_count = len(secrets_detected) if secrets_detected else 0
        secret_types = (
            [s.secret_type for s in secrets_detected] if secrets_detected else []
        )

        # Create audit entry
        audit_entry = {
            "timestamp": datetime.now(UTC).isoformat(),
            "proxy_profile": self.proxy_profile,
            "request_id": request_id,
            "event_type": "llm_request",
            "request_hash": self._hash_content(request_content),
            "response_hash": self._hash_content(response_content),
            "secrets_detected": secrets_count,
            "secrets_types": secret_types,
            "command_generated": command_generated,
            "user_approved": user_approved,
            "model_used": model_used,
        }

        # Add additional metadata if provided
        if additional_metadata:
            audit_entry.update(additional_metadata)

        self._write_audit_entry(audit_entry)

    def log_sanitization_event(
        self,
        request_id: str | None = None,
        secrets_detected: list[DetectedSecret] | None = None,
        original_content: str | None = None,
        sanitized_content: str | None = None,
    ) -> None:
        """Log a sanitization event.

        Args:
            request_id: Unique identifier for the request
            secrets_detected: List of secrets detected
            original_content: Original content (will be hashed)
            sanitized_content: Sanitized content (will be hashed)
        """
        if not self.enabled:
            return

        # Generate request ID if not provided
        if not request_id:
            request_id = str(uuid.uuid4())

        # Prepare secrets information
        secrets_count = len(secrets_detected) if secrets_detected else 0
        secret_types = (
            [s.secret_type for s in secrets_detected] if secrets_detected else []
        )

        # Create audit entry
        audit_entry = {
            "timestamp": datetime.now(UTC).isoformat(),
            "proxy_profile": self.proxy_profile,
            "request_id": request_id,
            "event_type": "sanitization",
            "original_hash": self._hash_content(original_content),
            "sanitized_hash": self._hash_content(sanitized_content),
            "secrets_detected": secrets_count,
            "secrets_types": secret_types,
        }

        self._write_audit_entry(audit_entry)

    def log_proxy_connection(
        self,
        server_url: str,
        success: bool,
        error_message: str | None = None,
        connection_time_ms: float | None = None,
    ) -> None:
        """Log a proxy connection event.

        Args:
            server_url: URL of the proxy server
            success: Whether the connection was successful
            error_message: Error message if connection failed
            connection_time_ms: Connection time in milliseconds
        """
        if not self.enabled:
            return

        audit_entry = {
            "timestamp": datetime.now(UTC).isoformat(),
            "proxy_profile": self.proxy_profile,
            "request_id": str(uuid.uuid4()),
            "event_type": "proxy_connection",
            "server_url": server_url,
            "success": success,
            "error_message": error_message,
            "connection_time_ms": connection_time_ms,
        }

        self._write_audit_entry(audit_entry)

    def _write_audit_entry(self, entry: dict[str, Any]) -> None:
        """Write an audit entry to the log file.

        Args:
            entry: Audit entry dictionary to write
        """
        if not self.log_path:
            logger.warning("Audit logging enabled but no log path configured")
            return

        try:
            with open(self.log_path, "a", encoding="utf-8") as f:
                json.dump(entry, f, separators=(",", ":"))
                f.write("\n")
        except Exception as e:
            logger.error(f"Failed to write audit log entry: {e}")

    def get_audit_entries(
        self,
        limit: int | None = None,
        since: datetime | None = None,
        event_type: str | None = None,
    ) -> list[dict[str, Any]]:
        """Read audit entries from the log file.

        Args:
            limit: Maximum number of entries to return
            since: Only return entries after this timestamp
            event_type: Only return entries of this event type

        Returns:
            List of audit entries
        """
        if not self.log_path or not self.log_path.exists():
            return []

        entries = []
        try:
            with open(self.log_path, encoding="utf-8") as f:
                for line in f:
                    line = line.strip()
                    if not line:
                        continue

                    try:
                        entry = json.loads(line)

                        # Filter by event type if specified
                        if event_type and entry.get("event_type") != event_type:
                            continue

                        # Filter by timestamp if specified
                        if since:
                            entry_time = datetime.fromisoformat(entry["timestamp"])
                            if entry_time < since:
                                continue

                        entries.append(entry)

                        # Apply limit if specified
                        if limit and len(entries) >= limit:
                            break

                    except json.JSONDecodeError as e:
                        logger.warning(f"Invalid JSON in audit log: {e}")
                        continue

        except Exception as e:
            logger.error(f"Failed to read audit log: {e}")

        return entries
