"""
Central audit service for Loi 25 compliance.

Wraps AccessLog and AuthLog with request context helpers.
All logging is gated behind ENABLE_AUDIT_LOG env var.

NOTE: ENABLE_AUDIT_LOG is read once at import time. Changing the env var
requires an application restart to take effect.

NOTE: Audit helpers do NOT commit the session — the caller's transaction
will persist the log entry when it commits. This avoids interfering with
ongoing transactions (e.g. a delete operation that audits then deletes).
"""

import os
import logging
from datetime import datetime, timedelta
from flask import request, has_request_context
from flask_login import current_user

from src.database import db
from src.models.access_log import AccessLog
from src.models.auth_log import AuthLog

logger = logging.getLogger(__name__)

# Read once at import — requires restart to change.
ENABLE_AUDIT_LOG = os.environ.get('ENABLE_AUDIT_LOG', 'false').lower() == 'true'


def is_audit_enabled():
    """Check if audit logging is enabled."""
    return ENABLE_AUDIT_LOG


def _get_request_context():
    """Extract IP address and user agent from current request."""
    if not has_request_context():
        return None, None
    ip = request.remote_addr
    ua = request.headers.get('User-Agent', '')[:500]
    return ip, ua


def _get_current_user_id():
    """Get current user ID if authenticated."""
    if has_request_context() and current_user and current_user.is_authenticated:
        return current_user.id
    return None


# --- Access logging helpers ---

_DEDUP_WINDOW_SECONDS = 300  # 5 minutes


def _is_recent_duplicate(action, resource_type, resource_id, user_id):
    """Return True if the same user already logged this action on this resource in the last 5 min.

    Prevents unbounded log growth for high-frequency read operations (e.g. view on every GET).
    """
    if user_id is None or resource_id is None:
        return False
    cutoff = datetime.utcnow() - timedelta(seconds=_DEDUP_WINDOW_SECONDS)
    return AccessLog.query.filter(
        AccessLog.user_id == user_id,
        AccessLog.action == action,
        AccessLog.resource_type == resource_type,
        AccessLog.resource_id == resource_id,
        AccessLog.timestamp >= cutoff,
    ).first() is not None


def audit_access(action, resource_type, resource_id=None, user_id=None, status='success', details=None):
    """Log a data access event if audit is enabled.

    Does NOT commit — the log is added to the current session and will be
    persisted when the caller (or Flask teardown) commits.

    View events are deduplicated: the same user viewing the same resource within
    5 minutes is logged only once to avoid unbounded log growth.
    """
    if not ENABLE_AUDIT_LOG:
        return None
    try:
        ip, ua = _get_request_context()
        if user_id is None:
            user_id = _get_current_user_id()
        if action == 'view' and _is_recent_duplicate(action, resource_type, resource_id, user_id):
            return None
        log = AccessLog.log_access(
            action=action,
            resource_type=resource_type,
            resource_id=resource_id,
            user_id=user_id,
            status=status,
            details=details,
            ip_address=ip,
            user_agent=ua,
        )
        return log
    except Exception as e:
        logger.warning(f"Failed to write access audit log: {e}")
        return None


def audit_view(resource_type, resource_id, **kwargs):
    """Log a view access."""
    return audit_access('view', resource_type, resource_id, **kwargs)


def audit_download(resource_type, resource_id, **kwargs):
    """Log a download access."""
    return audit_access('download', resource_type, resource_id, **kwargs)


def audit_edit(resource_type, resource_id, **kwargs):
    """Log an edit."""
    return audit_access('edit', resource_type, resource_id, **kwargs)


def audit_delete(resource_type, resource_id, **kwargs):
    """Log a deletion."""
    return audit_access('delete', resource_type, resource_id, **kwargs)


def audit_export(resource_type, resource_id, **kwargs):
    """Log a data export."""
    return audit_access('export', resource_type, resource_id, **kwargs)


# --- Auth logging helpers ---
# Auth events (login/logout/failed) are standalone operations, so they
# commit their own transaction since the caller typically redirects after.

def _audit_auth(func, *args, **kwargs):
    """Wrapper for auth audit helpers that commits independently."""
    if not ENABLE_AUDIT_LOG:
        return None
    try:
        ip, ua = _get_request_context()
        log = func(*args, ip_address=ip, user_agent=ua, **kwargs)
        db.session.commit()
        return log
    except Exception as e:
        logger.warning(f"Failed to write auth audit log: {e}")
        db.session.rollback()
        return None


def audit_login(user_id, details=None):
    """Log a successful login."""
    return _audit_auth(AuthLog.log_login, user_id, details=details)


def audit_logout(user_id=None):
    """Log a logout."""
    if user_id is None:
        user_id = _get_current_user_id()
    return _audit_auth(AuthLog.log_logout, user_id)


def audit_failed_login(details=None):
    """Log a failed login."""
    return _audit_auth(AuthLog.log_failed_login, details=details)


def audit_register(user_id):
    """Log a registration."""
    return _audit_auth(AuthLog.log_register, user_id)


def audit_password_change(user_id, details=None):
    """Log a password change."""
    return _audit_auth(AuthLog.log_password_change, user_id, details=details)


def audit_password_reset(user_id):
    """Log a password reset."""
    return _audit_auth(AuthLog.log_password_reset, user_id)


def audit_sso_login(user_id, details=None):
    """Log an SSO login."""
    return _audit_auth(AuthLog.log_sso_login, user_id, details=details)


# --- Query helpers for admin ---

def get_access_logs(page=1, per_page=50, user_id=None, resource_type=None, resource_id=None, action=None):
    """Query access logs with pagination and filters."""
    query = AccessLog.query.order_by(AccessLog.timestamp.desc())
    if user_id is not None:
        query = query.filter_by(user_id=user_id)
    if resource_type is not None:
        query = query.filter_by(resource_type=resource_type)
    if resource_id is not None:
        query = query.filter_by(resource_id=resource_id)
    if action is not None:
        query = query.filter_by(action=action)
    return query.paginate(page=page, per_page=per_page, error_out=False)


def get_auth_logs(page=1, per_page=50, user_id=None, action=None):
    """Query auth logs with pagination and filters."""
    query = AuthLog.query.order_by(AuthLog.timestamp.desc())
    if user_id is not None:
        query = query.filter_by(user_id=user_id)
    if action is not None:
        query = query.filter_by(action=action)
    return query.paginate(page=page, per_page=per_page, error_out=False)
