"""
Audit Service for logging admin actions.
All admin operations should call this service to create audit trails.
"""

import json
from typing import Optional, Any
from datetime import datetime
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func

from app.models.admin_audit_log import AdminAuditLog
from app.models import User


class AuditService:
    """Service for creating and querying audit logs."""

    @staticmethod
    async def log(
        db: AsyncSession,
        admin: User,
        action: str,
        description: str,
        target_type: Optional[str] = None,
        target_id: Optional[int] = None,
        details: Optional[dict[str, Any]] = None,
        ip_address: Optional[str] = None,
    ) -> AdminAuditLog:
        """
        Create an audit log entry.

        Args:
            db: Database session
            admin: The admin user performing the action
            action: Action code (e.g., DATA_WIPE, USER_UPDATE)
            description: Human-readable description
            target_type: Type of target (user, event, bet, etc.)
            target_id: ID of the target entity
            details: Additional details as a dictionary
            ip_address: IP address of the admin

        Returns:
            The created audit log entry
        """
        log_entry = AdminAuditLog(
            admin_id=admin.id,
            admin_username=admin.username,
            action=action,
            target_type=target_type,
            target_id=target_id,
            description=description,
            details=json.dumps(details) if details else None,
            ip_address=ip_address,
        )
        db.add(log_entry)
        await db.flush()
        return log_entry

    @staticmethod
    async def get_logs(
        db: AsyncSession,
        page: int = 1,
        page_size: int = 50,
        action_filter: Optional[str] = None,
        admin_id_filter: Optional[int] = None,
        target_type_filter: Optional[str] = None,
        start_date: Optional[datetime] = None,
        end_date: Optional[datetime] = None,
    ) -> tuple[list[AdminAuditLog], int]:
        """
        Get paginated audit logs with optional filters.

        Returns:
            Tuple of (logs, total_count)
        """
        query = select(AdminAuditLog)

        # Apply filters
        if action_filter:
            query = query.where(AdminAuditLog.action == action_filter)
        if admin_id_filter:
            query = query.where(AdminAuditLog.admin_id == admin_id_filter)
        if target_type_filter:
            query = query.where(AdminAuditLog.target_type == target_type_filter)
        if start_date:
            query = query.where(AdminAuditLog.created_at >= start_date)
        if end_date:
            query = query.where(AdminAuditLog.created_at <= end_date)

        # Get total count
        count_query = select(func.count()).select_from(query.subquery())
        total_result = await db.execute(count_query)
        total = total_result.scalar() or 0

        # Apply pagination and ordering
        query = query.order_by(AdminAuditLog.created_at.desc())
        query = query.offset((page - 1) * page_size).limit(page_size)

        result = await db.execute(query)
        logs = list(result.scalars().all())

        return logs, total

    @staticmethod
    async def get_latest_action(
        db: AsyncSession,
        action: str,
    ) -> Optional[AdminAuditLog]:
        """Get the most recent log entry for a specific action."""
        query = (
            select(AdminAuditLog)
            .where(AdminAuditLog.action == action)
            .order_by(AdminAuditLog.created_at.desc())
            .limit(1)
        )
        result = await db.execute(query)
        return result.scalar_one_or_none()


# Convenience functions for common audit actions

async def log_data_wipe(
    db: AsyncSession,
    admin: User,
    deleted_counts: dict[str, int],
    preserved_counts: dict[str, int],
    ip_address: Optional[str] = None,
) -> AdminAuditLog:
    """Log a data wipe operation."""
    return await AuditService.log(
        db=db,
        admin=admin,
        action="DATA_WIPE",
        description=f"Database wiped by {admin.username}",
        details={
            "deleted": deleted_counts,
            "preserved": preserved_counts,
        },
        ip_address=ip_address,
    )


async def log_data_seed(
    db: AsyncSession,
    admin: User,
    created_counts: dict[str, int],
    ip_address: Optional[str] = None,
) -> AdminAuditLog:
    """Log a data seed operation."""
    return await AuditService.log(
        db=db,
        admin=admin,
        action="DATA_SEED",
        description=f"Database seeded by {admin.username}",
        details={"created": created_counts},
        ip_address=ip_address,
    )


async def log_simulation_start(
    db: AsyncSession,
    admin: User,
    config: dict,
    ip_address: Optional[str] = None,
) -> AdminAuditLog:
    """Log simulation start."""
    return await AuditService.log(
        db=db,
        admin=admin,
        action="SIMULATION_START",
        description=f"Simulation started by {admin.username}",
        details={"config": config},
        ip_address=ip_address,
    )


async def log_simulation_stop(
    db: AsyncSession,
    admin: User,
    iterations: int,
    duration_seconds: float,
    ip_address: Optional[str] = None,
) -> AdminAuditLog:
    """Log simulation stop."""
    return await AuditService.log(
        db=db,
        admin=admin,
        action="SIMULATION_STOP",
        description=f"Simulation stopped by {admin.username} after {iterations} iterations",
        details={
            "iterations": iterations,
            "duration_seconds": duration_seconds,
        },
        ip_address=ip_address,
    )


async def log_user_status_change(
    db: AsyncSession,
    admin: User,
    target_user: User,
    old_status: str,
    new_status: str,
    reason: Optional[str] = None,
    ip_address: Optional[str] = None,
) -> AdminAuditLog:
    """Log user status change."""
    return await AuditService.log(
        db=db,
        admin=admin,
        action="USER_STATUS_CHANGE",
        description=f"{admin.username} changed {target_user.username}'s status from {old_status} to {new_status}",
        target_type="user",
        target_id=target_user.id,
        details={
            "old_status": old_status,
            "new_status": new_status,
            "reason": reason,
        },
        ip_address=ip_address,
    )


async def log_user_balance_adjust(
    db: AsyncSession,
    admin: User,
    target_user: User,
    old_balance: float,
    new_balance: float,
    amount: float,
    reason: str,
    transaction_id: int,
    ip_address: Optional[str] = None,
) -> AdminAuditLog:
    """Log user balance adjustment."""
    return await AuditService.log(
        db=db,
        admin=admin,
        action="USER_BALANCE_ADJUST",
        description=f"{admin.username} adjusted {target_user.username}'s balance by ${amount:+.2f}",
        target_type="user",
        target_id=target_user.id,
        details={
            "old_balance": old_balance,
            "new_balance": new_balance,
            "amount": amount,
            "reason": reason,
            "transaction_id": transaction_id,
        },
        ip_address=ip_address,
    )


async def log_user_admin_change(
    db: AsyncSession,
    admin: User,
    target_user: User,
    granted: bool,
    ip_address: Optional[str] = None,
) -> AdminAuditLog:
    """Log admin privilege grant/revoke."""
    action = "USER_ADMIN_GRANT" if granted else "USER_ADMIN_REVOKE"
    verb = "granted admin privileges to" if granted else "revoked admin privileges from"
    return await AuditService.log(
        db=db,
        admin=admin,
        action=action,
        description=f"{admin.username} {verb} {target_user.username}",
        target_type="user",
        target_id=target_user.id,
        details={"granted": granted},
        ip_address=ip_address,
    )


async def log_user_update(
    db: AsyncSession,
    admin: User,
    target_user: User,
    changes: dict[str, Any],
    ip_address: Optional[str] = None,
) -> AdminAuditLog:
    """Log user details update."""
    return await AuditService.log(
        db=db,
        admin=admin,
        action="USER_UPDATE",
        description=f"{admin.username} updated {target_user.username}'s profile",
        target_type="user",
        target_id=target_user.id,
        details={"changes": changes},
        ip_address=ip_address,
    )


async def log_settings_update(
    db: AsyncSession,
    admin: User,
    changes: dict[str, Any],
    ip_address: Optional[str] = None,
) -> AdminAuditLog:
    """Log platform settings update."""
    return await AuditService.log(
        db=db,
        admin=admin,
        action="SETTINGS_UPDATE",
        description=f"{admin.username} updated platform settings",
        details={"changes": changes},
        ip_address=ip_address,
    )


async def log_event_create(
    db: AsyncSession,
    admin: User,
    event_id: int,
    event_title: str,
    ip_address: Optional[str] = None,
) -> AdminAuditLog:
    """Log sport event creation."""
    return await AuditService.log(
        db=db,
        admin=admin,
        action="EVENT_CREATE",
        description=f"{admin.username} created event: {event_title}",
        target_type="event",
        target_id=event_id,
        ip_address=ip_address,
    )


async def log_event_update(
    db: AsyncSession,
    admin: User,
    event_id: int,
    event_title: str,
    changes: dict[str, Any],
    ip_address: Optional[str] = None,
) -> AdminAuditLog:
    """Log sport event update."""
    return await AuditService.log(
        db=db,
        admin=admin,
        action="EVENT_UPDATE",
        description=f"{admin.username} updated event: {event_title}",
        target_type="event",
        target_id=event_id,
        details={"changes": changes},
        ip_address=ip_address,
    )


async def log_event_delete(
    db: AsyncSession,
    admin: User,
    event_id: int,
    event_title: str,
    ip_address: Optional[str] = None,
) -> AdminAuditLog:
    """Log sport event deletion."""
    return await AuditService.log(
        db=db,
        admin=admin,
        action="EVENT_DELETE",
        description=f"{admin.username} deleted event: {event_title}",
        target_type="event",
        target_id=event_id,
        ip_address=ip_address,
    )
