"""
Immutable audit logging with HMAC signatures.
"""

import hashlib
import hmac
import json
import os
from collections import Counter
from datetime import datetime, timezone
from pathlib import Path
from typing import Any, Dict, Optional, Union

import structlog

logger = structlog.get_logger(__name__)


class ImmutableAuditLogger:
    """Immutable audit logger with HMAC signatures to prevent log tampering."""

    def __init__(self) -> None:
        """Initialize the immutable audit logger."""
        self.hmac_secret = os.getenv(
            "AUDIT_HMAC_SECRET", "default_audit_secret_change_me"
        )
        log_path_str = os.getenv(
            "AUDIT_LOG_PATH", "/var/log/remote_access_audit.log"
        )
        self.audit_log_path = Path(log_path_str)
        self.audit_log_path.parent.mkdir(parents=True, exist_ok=True)
        self.audit_logger = structlog.get_logger("audit")

        logger.info(
            "Immutable audit logger initialized",
            audit_log_path=str(self.audit_log_path),
        )

    def _generate_hmac_signature(self, data: str) -> str:
        """
        Generate HMAC signature for data.

        Args:
            data: Data to sign.

        Returns:
            HMAC signature in hex format.
        """
        return hmac.new(
            self.hmac_secret.encode("utf-8"),
            data.encode("utf-8"),
            hashlib.sha256,
        ).hexdigest()

    def _verify_hmac_signature(self, data: str, signature: str) -> bool:
        """
        Verify HMAC signature.

        Args:
            data: Data to verify.
            signature: Signature to verify.

        Returns:
            True if signature is valid.
        """
        expected_signature = self._generate_hmac_signature(data)
        return hmac.compare_digest(expected_signature, signature)

    def log_security_event(
        self,
        event_type: str,
        client_ip: str,
        user_agent: Optional[str] = None,
        details: Optional[Dict[str, Any]] = None,
        severity: str = "info",
        username: Optional[str] = None,
    ) -> bool:
        """
        Log security event with immutable record.

        Args:
            event_type: Event type.
            client_ip: Client IP address.
            user_agent: Client user agent.
            details: Additional details.
            severity: Severity level.
            username: Username if applicable.

        Returns:
            True if logging succeeded.
        """
        try:
            event_data = {
                "event_type": "security_event",
                "security_event_type": event_type,
                "timestamp": datetime.now(timezone.utc).isoformat(),
                "client_ip": client_ip,
                "user_agent": user_agent or "unknown",
                "severity": severity,
                "username": username,
                "details": details or {},
            }
            return self._write_immutable_log(event_data)
        except Exception as e:
            logger.error("Failed to log security event", error=str(e))
            return False

    def log_audit_event(
        self,
        action: str,
        resource: str,
        client_ip: str,
        user_agent: Optional[str] = None,
        result: str = "success",
        details: Optional[Dict[str, Any]] = None,
        username: Optional[str] = None,
    ) -> bool:
        """
        Log audit event with immutable record.

        Args:
            action: Action performed.
            resource: Resource affected.
            client_ip: Client IP address.
            user_agent: Client user agent.
            result: Action result.
            details: Additional details.
            username: Username.

        Returns:
            True if logging succeeded.
        """
        try:
            event_data = {
                "event_type": "audit_event",
                "action": action,
                "resource": resource,
                "timestamp": datetime.now(timezone.utc).isoformat(),
                "client_ip": client_ip,
                "user_agent": user_agent or "unknown",
                "result": result,
                "username": username,
                "details": details or {},
            }
            return self._write_immutable_log(event_data)
        except Exception as e:
            logger.error("Failed to log audit event", error=str(e))
            return False

    def log_authentication_event(
        self,
        event_type: str,
        username: str,
        client_ip: str,
        success: bool,
        details: Optional[Dict[str, Any]] = None,
    ) -> bool:
        """
        Log authentication event.

        Args:
            event_type: Event type (login, logout, failed_login, etc.).
            username: Username.
            client_ip: Client IP address.
            success: Operation success status.
            details: Additional details.

        Returns:
            True if logging succeeded.
        """
        try:
            event_data = {
                "event_type": "authentication_event",
                "auth_event_type": event_type,
                "timestamp": datetime.now(timezone.utc).isoformat(),
                "username": username,
                "client_ip": client_ip,
                "success": success,
                "details": details or {},
            }
            return self._write_immutable_log(event_data)
        except Exception as e:
            logger.error("Failed to log authentication event", error=str(e))
            return False

    def log_connection_event(
        self,
        event_type: str,
        connection_id: str,
        username: str,
        client_ip: str,
        details: Optional[Dict[str, Any]] = None,
    ) -> bool:
        """
        Log connection event.

        Args:
            event_type: Event type (created, deleted, expired, etc.).
            connection_id: Connection ID.
            username: Username.
            client_ip: Client IP address.
            details: Additional details.

        Returns:
            True if logging succeeded.
        """
        try:
            event_data = {
                "event_type": "connection_event",
                "connection_event_type": event_type,
                "timestamp": datetime.now(timezone.utc).isoformat(),
                "connection_id": connection_id,
                "username": username,
                "client_ip": client_ip,
                "details": details or {},
            }
            return self._write_immutable_log(event_data)
        except Exception as e:
            logger.error("Failed to log connection event", error=str(e))
            return False

    def _write_immutable_log(self, event_data: Dict[str, Any]) -> bool:
        """
        Write immutable log entry with HMAC signature.

        Args:
            event_data: Event data.

        Returns:
            True if write succeeded.
        """
        try:
            json_data = json.dumps(event_data, ensure_ascii=False, sort_keys=True)
            signature = self._generate_hmac_signature(json_data)
            log_entry = {
                "data": event_data,
                "signature": signature,
                "log_timestamp": datetime.now(timezone.utc).isoformat(),
            }

            with self.audit_log_path.open("a", encoding="utf-8") as f:
                f.write(json.dumps(log_entry, ensure_ascii=False) + "\n")
                f.flush()

            self.audit_logger.info(
                "Audit event logged",
                event_type=event_data.get("event_type"),
                signature=signature[:16] + "...",
            )
            return True
        except Exception as e:
            logger.error("Failed to write immutable log", error=str(e))
            return False

    def verify_log_integrity(
        self, log_file_path: Optional[Union[str, Path]] = None
    ) -> Dict[str, Any]:
        """
        Verify audit log integrity.

        Args:
            log_file_path: Path to log file (defaults to main log file).

        Returns:
            Integrity verification result.
        """
        try:
            file_path = (
                Path(log_file_path) if log_file_path else self.audit_log_path
            )

            if not file_path.exists():
                return {
                    "status": "error",
                    "message": "Log file does not exist",
                    "file_path": str(file_path),
                }

            valid_entries = 0
            invalid_entries = 0
            total_entries = 0

            with file_path.open("r", encoding="utf-8") as f:
                for line in f:
                    if not line.strip():
                        continue

                    total_entries += 1

                    try:
                        log_entry = json.loads(line)

                        if "data" not in log_entry or "signature" not in log_entry:
                            invalid_entries += 1
                            continue

                        json_data = json.dumps(
                            log_entry["data"], ensure_ascii=False, sort_keys=True
                        )

                        if self._verify_hmac_signature(
                            json_data, log_entry["signature"]
                        ):
                            valid_entries += 1
                        else:
                            invalid_entries += 1
                    except (json.JSONDecodeError, KeyError, ValueError):
                        invalid_entries += 1

            return {
                "status": "success",
                "file_path": str(file_path),
                "total_entries": total_entries,
                "valid_entries": valid_entries,
                "invalid_entries": invalid_entries,
                "integrity_percentage": (
                    (valid_entries / total_entries * 100) if total_entries > 0 else 0
                ),
            }
        except Exception as e:
            logger.error("Failed to verify log integrity", error=str(e))
            return {
                "status": "error",
                "message": str(e),
                "file_path": str(log_file_path or self.audit_log_path),
            }

    def get_audit_stats(self) -> Dict[str, Any]:
        """
        Get audit log statistics.

        Returns:
            Audit log statistics.
        """
        try:
            if not self.audit_log_path.exists():
                return {
                    "status": "no_log_file",
                    "file_path": str(self.audit_log_path),
                }

            file_size = self.audit_log_path.stat().st_size
            event_types: Counter[str] = Counter()
            total_entries = 0

            with self.audit_log_path.open("r", encoding="utf-8") as f:
                for line in f:
                    if not line.strip():
                        continue

                    try:
                        log_entry = json.loads(line)
                        if (
                            "data" in log_entry
                            and "event_type" in log_entry["data"]
                        ):
                            event_type = log_entry["data"]["event_type"]
                            event_types[event_type] += 1
                            total_entries += 1
                    except (json.JSONDecodeError, KeyError):
                        continue

            return {
                "status": "success",
                "file_path": str(self.audit_log_path),
                "file_size_bytes": file_size,
                "total_entries": total_entries,
                "event_types": dict(event_types),
                "hmac_secret_configured": bool(
                    self.hmac_secret
                    and self.hmac_secret != "default_audit_secret_change_me"
                ),
            }
        except Exception as e:
            logger.error("Failed to get audit stats", error=str(e))
            return {"status": "error", "message": str(e)}


# Global instance for use in API
immutable_audit_logger = ImmutableAuditLogger()
