from datetime import datetime

from sqlalchemy import Select, func, select
from sqlalchemy.orm import Session

from app.models.audit import AuditLog


class AuditRepository:
    @staticmethod
    def create(
        db: Session,
        *,
        actor_user_id: int | None,
        actor_username: str,
        action: str,
        entity_type: str,
        entity_id: int | None,
        entity_label: str,
        ip_address: str,
        user_agent: str,
        before_data: dict | list | None = None,
        after_data: dict | list | None = None,
        metadata: dict | list | None = None,
    ) -> AuditLog:
        audit_log = AuditLog(
            actor_user_id=actor_user_id,
            actor_username=actor_username,
            action=action,
            entity_type=entity_type,
            entity_id=entity_id,
            entity_label=entity_label,
            ip_address=ip_address,
            user_agent=user_agent,
            before_data=before_data,
            after_data=after_data,
            metadata_data=metadata,
        )
        db.add(audit_log)
        db.commit()
        db.refresh(audit_log)
        return audit_log

    @staticmethod
    def query(
        *,
        entity_type: str | None = None,
        actor_user_id: int | None = None,
        action: str | None = None,
        date_from: datetime | None = None,
        date_to: datetime | None = None,
    ) -> Select[tuple[AuditLog]]:
        query = select(AuditLog)
        if entity_type:
            query = query.where(AuditLog.entity_type == entity_type)
        if actor_user_id is not None:
            query = query.where(AuditLog.actor_user_id == actor_user_id)
        if action:
            query = query.where(AuditLog.action == action)
        if date_from:
            query = query.where(AuditLog.created_at >= date_from)
        if date_to:
            query = query.where(AuditLog.created_at <= date_to)
        return query

    @staticmethod
    def list(
        db: Session,
        *,
        page: int,
        page_size: int,
        entity_type: str | None = None,
        actor_user_id: int | None = None,
        action: str | None = None,
        date_from: datetime | None = None,
        date_to: datetime | None = None,
    ) -> tuple[list[AuditLog], int]:
        base_query = AuditRepository.query(
            entity_type=entity_type,
            actor_user_id=actor_user_id,
            action=action,
            date_from=date_from,
            date_to=date_to,
        )
        total = db.scalar(select(func.count()).select_from(base_query.subquery())) or 0
        items = list(
            db.scalars(
                base_query
                .order_by(AuditLog.created_at.desc())
                .offset((page - 1) * page_size)
                .limit(page_size)
            )
        )
        return items, total

    @staticmethod
    def latest(db: Session, limit: int = 10) -> list[AuditLog]:
        return list(
            db.scalars(
                select(AuditLog)
                .order_by(AuditLog.created_at.desc())
                .limit(limit)
            )
        )
