"""
DSS MCP Audit Module

Tracks all operations for compliance, debugging, and audit trails.
Maintains immutable logs of all state-changing operations with before/after snapshots.
"""

import json
import uuid
from typing import Optional, Dict, Any
from datetime import datetime
from enum import Enum

from storage.json_store import ActivityLog, append_jsonl, read_jsonl, SYSTEM_DIR  # JSON storage


class AuditEventType(Enum):
    """Types of auditable events"""
    TOOL_CALL = "tool_call"
    CREDENTIAL_ACCESS = "credential_access"
    CREDENTIAL_CREATE = "credential_create"
    CREDENTIAL_DELETE = "credential_delete"
    PROJECT_CREATE = "project_create"
    PROJECT_UPDATE = "project_update"
    PROJECT_DELETE = "project_delete"
    COMPONENT_SYNC = "component_sync"
    TOKEN_SYNC = "token_sync"
    STATE_TRANSITION = "state_transition"
    ERROR = "error"
    SECURITY_EVENT = "security_event"


class AuditLog:
    """
    Persistent operation audit trail.

    All operations are logged with:
    - Full operation details
    - User who performed it
    - Timestamp
    - Before/after state snapshots
    - Result status
    """

    @staticmethod
    def log_operation(
        event_type: AuditEventType,
        operation_name: str,
        operation_id: str,
        user_id: Optional[str],
        project_id: Optional[str],
        args: Dict[str, Any],
        result: Optional[Dict[str, Any]] = None,
        error: Optional[str] = None,
        before_state: Optional[Dict[str, Any]] = None,
        after_state: Optional[Dict[str, Any]] = None
    ) -> str:
        """
        Log an operation to the audit trail.

        Args:
            event_type: Type of event
            operation_name: Human-readable operation name
            operation_id: Unique operation ID
            user_id: User who performed the operation
            project_id: Associated project ID
            args: Operation arguments (will be scrubbed of sensitive data)
            result: Operation result
            error: Error message if operation failed
            before_state: State before operation
            after_state: State after operation

        Returns:
            Audit log entry ID
        """
        audit_id = str(uuid.uuid4())

        # Scrub sensitive data from args
        scrubbed_args = AuditLog._scrub_sensitive_data(args)

        with get_connection() as conn:
            conn.execute("""
                INSERT INTO audit_log (
                    id, event_type, operation_name, operation_id, user_id,
                    project_id, args, result, error, before_state, after_state,
                    created_at
                ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
            """, (
                audit_id,
                event_type.value,
                operation_name,
                operation_id,
                user_id,
                project_id,
                json.dumps(scrubbed_args),
                json.dumps(result) if result else None,
                error,
                json.dumps(before_state) if before_state else None,
                json.dumps(after_state) if after_state else None,
                datetime.utcnow().isoformat()
            ))

        return audit_id

    @staticmethod
    def get_operation_history(
        project_id: Optional[str] = None,
        user_id: Optional[str] = None,
        operation_name: Optional[str] = None,
        limit: int = 100,
        offset: int = 0
    ) -> list:
        """
        Get operation history with optional filtering.

        Args:
            project_id: Filter by project
            user_id: Filter by user
            operation_name: Filter by operation
            limit: Number of records to return
            offset: Pagination offset

        Returns:
            List of audit log entries
        """
        with get_connection() as conn:
            cursor = conn.cursor()

            query = "SELECT * FROM audit_log WHERE 1=1"
            params = []

            if project_id:
                query += " AND project_id = ?"
                params.append(project_id)

            if user_id:
                query += " AND user_id = ?"
                params.append(user_id)

            if operation_name:
                query += " AND operation_name = ?"
                params.append(operation_name)

            query += " ORDER BY created_at DESC LIMIT ? OFFSET ?"
            params.extend([limit, offset])

            cursor.execute(query, params)
            return [dict(row) for row in cursor.fetchall()]

    @staticmethod
    def get_audit_trail(
        start_date: datetime,
        end_date: datetime,
        event_type: Optional[str] = None
    ) -> list:
        """
        Get audit trail for a date range.

        Useful for compliance reports and security audits.

        Args:
            start_date: Start of date range
            end_date: End of date range
            event_type: Optional event type filter

        Returns:
            List of audit log entries
        """
        with get_connection() as conn:
            cursor = conn.cursor()

            query = """
                SELECT * FROM audit_log
                WHERE created_at >= ? AND created_at <= ?
            """
            params = [start_date.isoformat(), end_date.isoformat()]

            if event_type:
                query += " AND event_type = ?"
                params.append(event_type)

            query += " ORDER BY created_at DESC"

            cursor.execute(query, params)
            return [dict(row) for row in cursor.fetchall()]

    @staticmethod
    def get_user_activity(
        user_id: str,
        days: int = 30
    ) -> Dict[str, Any]:
        """
        Get user activity summary for the past N days.

        Args:
            user_id: User to analyze
            days: Number of past days to include

        Returns:
            Activity summary including operation counts and patterns
        """
        from datetime import timedelta

        start_date = datetime.utcnow() - timedelta(days=days)

        with get_connection() as conn:
            cursor = conn.cursor()

            # Get total operations
            cursor.execute("""
                SELECT COUNT(*) FROM audit_log
                WHERE user_id = ? AND created_at >= ?
            """, (user_id, start_date.isoformat()))
            total_ops = cursor.fetchone()[0]

            # Get operations by type
            cursor.execute("""
                SELECT event_type, COUNT(*) as count
                FROM audit_log
                WHERE user_id = ? AND created_at >= ?
                GROUP BY event_type
                ORDER BY count DESC
            """, (user_id, start_date.isoformat()))
            ops_by_type = {row[0]: row[1] for row in cursor.fetchall()}

            # Get error count
            cursor.execute("""
                SELECT COUNT(*) FROM audit_log
                WHERE user_id = ? AND created_at >= ? AND error IS NOT NULL
            """, (user_id, start_date.isoformat()))
            errors = cursor.fetchone()[0]

            # Get unique projects
            cursor.execute("""
                SELECT COUNT(DISTINCT project_id) FROM audit_log
                WHERE user_id = ? AND created_at >= ?
            """, (user_id, start_date.isoformat()))
            projects = cursor.fetchone()[0]

        return {
            "user_id": user_id,
            "days": days,
            "total_operations": total_ops,
            "operations_by_type": ops_by_type,
            "errors": errors,
            "projects_touched": projects,
            "average_ops_per_day": round(total_ops / days, 2) if days > 0 else 0
        }

    @staticmethod
    def search_audit_log(
        search_term: str,
        limit: int = 50
    ) -> list:
        """
        Search audit log by operation name or error message.

        Args:
            search_term: Term to search for
            limit: Maximum results

        Returns:
            List of matching audit entries
        """
        with get_connection() as conn:
            cursor = conn.cursor()

            cursor.execute("""
                SELECT * FROM audit_log
                WHERE operation_name LIKE ? OR error LIKE ?
                ORDER BY created_at DESC
                LIMIT ?
            """, (f"%{search_term}%", f"%{search_term}%", limit))

            return [dict(row) for row in cursor.fetchall()]

    @staticmethod
    def _scrub_sensitive_data(data: Dict[str, Any]) -> Dict[str, Any]:
        """
        Remove sensitive data from arguments for safe logging.

        Removes API tokens, passwords, and other secrets.
        """
        sensitive_keys = {
            'token', 'api_key', 'secret', 'password',
            'credential', 'auth', 'figma_token', 'encrypted_data'
        }

        scrubbed = {}
        for key, value in data.items():
            if any(sensitive in key.lower() for sensitive in sensitive_keys):
                scrubbed[key] = "***REDACTED***"
            elif isinstance(value, dict):
                scrubbed[key] = AuditLog._scrub_sensitive_data(value)
            elif isinstance(value, list):
                scrubbed[key] = [
                    AuditLog._scrub_sensitive_data(item)
                    if isinstance(item, dict) else item
                    for item in value
                ]
            else:
                scrubbed[key] = value

        return scrubbed

    @staticmethod
    def ensure_audit_log_table():
        """Ensure audit_log table exists"""
        with get_connection() as conn:
            conn.execute("""
                CREATE TABLE IF NOT EXISTS audit_log (
                    id TEXT PRIMARY KEY,
                    event_type TEXT NOT NULL,
                    operation_name TEXT NOT NULL,
                    operation_id TEXT,
                    user_id TEXT,
                    project_id TEXT,
                    args TEXT,
                    result TEXT,
                    error TEXT,
                    before_state TEXT,
                    after_state TEXT,
                    created_at TEXT DEFAULT CURRENT_TIMESTAMP
                )
            """)
            conn.execute(
                "CREATE INDEX IF NOT EXISTS idx_audit_user ON audit_log(user_id)"
            )
            conn.execute(
                "CREATE INDEX IF NOT EXISTS idx_audit_project ON audit_log(project_id)"
            )
            conn.execute(
                "CREATE INDEX IF NOT EXISTS idx_audit_type ON audit_log(event_type)"
            )
            conn.execute(
                "CREATE INDEX IF NOT EXISTS idx_audit_date ON audit_log(created_at)"
            )


# Initialize table on import
AuditLog.ensure_audit_log_table()
