from typing import Optional, Dict, Any
from datetime import datetime
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, and_
from src.models.usuarios import Usuario
from src.models.computadores import Computer
from src.models.atividades import Activity
from src.models.times import Time
from src.core.api_responses import to_schema_dict
from src.schemas.atividades import ActivityResponse


class VisualizacaoService:

    @staticmethod
    async def obter_atividades_por_usuario(
        db: AsyncSession,
        usuario_id: int,
        data_inicio: Optional[datetime] = None,
        data_fim: Optional[datetime] = None,
        evento: Optional[str] = None,
        processo: Optional[str] = None,
    ) -> Optional[Dict[str, Any]]:
        """
        Busca atividades de um usuário específico.

        Args:
            db: Sessão assíncrona do banco
            usuario_id: ID do usuário
            data_inicio: Data/hora inicial (opcional)
            data_fim: Data/hora final (opcional)
            evento: Filtro de evento (opcional)
            processo: Filtro de processo (opcional)

        Returns:
            Dict com dados do usuário e suas atividades, ou None se
            não encontrado
        """
        stmt_usuario = select(Usuario).where(Usuario.id == usuario_id)
        result_usuario = await db.execute(stmt_usuario)
        usuario = result_usuario.scalar_one_or_none()

        if not usuario:
            return None

        stmt_computadores = select(Computer).where(
            Computer.usuario_id == usuario_id)
        result_computadores = await db.execute(stmt_computadores)
        computadores = result_computadores.scalars().all()

        if not computadores:
            return {
                "usuario": {
                    "id": usuario.id,
                    "nome": usuario.nome,
                    "ativo": usuario.ativo,
                    "data_criacao": usuario.data_criacao,
                    "ultimo_login": usuario.ultimo_login,
                },
                "atividades": [],
                "total_computadores": 0,
                "filtros": {
                    "data_inicio": data_inicio,
                    "data_fim": data_fim,
                    "evento": evento,
                    "processo": processo,
                }
            }

        computador_ids = [c.id for c in computadores]

        conditions = [Activity.computador_id.in_(computador_ids)]

        if data_inicio:
            conditions.append(Activity.data_hora >= data_inicio)

        if data_fim:
            conditions.append(Activity.data_hora <= data_fim)

        if evento:
            conditions.append(Activity.evento == evento)

        if processo:
            conditions.append(Activity.processo.like(f"%{processo}%"))

        stmt_atividades = select(Activity).where(
            and_(
                *
                conditions)).order_by(
                    Activity.data_hora.desc())
        result_atividades = await db.execute(stmt_atividades)
        atividades = result_atividades.scalars().all()

        return {
            "usuario": {
                "id": usuario.id,
                "nome": usuario.nome,
                "ativo": usuario.ativo,
                "data_criacao": usuario.data_criacao,
                "ultimo_login": usuario.ultimo_login,
            },
            "atividades": [
                to_schema_dict(
                    ActivityResponse,
                    a) for a in atividades],
            "total_computadores": len(computadores),
            "total_atividades": len(atividades),
            "filtros": {
                "data_inicio": data_inicio,
                "data_fim": data_fim,
                "evento": evento,
                "processo": processo,
            }}

    @staticmethod
    async def obter_atividades_por_time(
        db: AsyncSession,
        time_id: int,
        data_inicio: Optional[datetime] = None,
        data_fim: Optional[datetime] = None,
        evento: Optional[str] = None,
        processo: Optional[str] = None,
    ) -> Optional[Dict[str, Any]]:
        """
        Busca atividades de todos os usuários que pertencem a um time.

        Usa o relacionamento muitos-para-muitos da tabela usuarios_times
        para buscar apenas os usuários vinculados ao time.

        Args:
            db: Sessão assíncrona do banco
            time_id: ID do time
            data_inicio: Data/hora inicial (opcional)
            data_fim: Data/hora final (opcional)
            evento: Filtro de evento (opcional)
            processo: Filtro de processo (opcional)

        Returns:
            Dict com dados do time, usuários e suas atividades, ou None se
            não encontrado
        """
        stmt_time = select(Time).where(Time.id == time_id)
        result_time = await db.execute(stmt_time)
        time = result_time.scalar_one_or_none()

        if not time:
            return None

        usuarios_time = time.usuarios

        if not usuarios_time:
            return {
                "time": {
                    "id": time.id,
                    "nome": time.nome,
                    "descricao": time.descricao,
                    "empresa_id": time.empresa_id,
                },
                "usuarios": [],
                "total_computadores": 0,
                "filtros": {
                    "data_inicio": data_inicio,
                    "data_fim": data_fim,
                    "evento": evento,
                    "processo": processo,
                }
            }
        usuarios_com_atividades = []
        total_computadores = 0

        for usuario in usuarios_time:
            stmt_computadores = select(Computer).where(
                Computer.usuario_id == usuario.id)
            result_computadores = await db.execute(stmt_computadores)
            computadores = result_computadores.scalars().all()

            if not computadores:
                usuarios_com_atividades.append({
                    "usuario": {
                        "id": usuario.id,
                        "nome": usuario.nome,
                        "ativo": usuario.ativo,
                    },
                    "computadores": 0,
                    "atividades": [],
                    "total_atividades": 0
                })
                continue

            total_computadores += len(computadores)
            computador_ids = [c.id for c in computadores]

            conditions = [Activity.computador_id.in_(computador_ids)]

            if data_inicio:
                conditions.append(Activity.data_hora >= data_inicio)

            if data_fim:
                conditions.append(Activity.data_hora <= data_fim)

            if evento:
                conditions.append(Activity.evento == evento)

            if processo:
                conditions.append(Activity.processo.like(f"%{processo}%"))

            stmt_atividades = select(Activity).where(
                and_(*conditions)
            ).order_by(Activity.data_hora.desc())
            result_atividades = await db.execute(stmt_atividades)
            atividades = result_atividades.scalars().all()

            usuarios_com_atividades.append({
                "usuario": {
                    "id": usuario.id,
                    "nome": usuario.nome,
                    "ativo": usuario.ativo,
                },
                "computadores": len(computadores),
                "atividades": [
                    to_schema_dict(ActivityResponse, a) for a in atividades
                ],
                "total_atividades": len(atividades)
            })

        total_atividades = sum(
            u["total_atividades"] for u in usuarios_com_atividades
        )

        return {
            "time": {
                "id": time.id,
                "nome": time.nome,
                "descricao": time.descricao,
                "empresa_id": time.empresa_id,
            },
            "usuarios": usuarios_com_atividades,
            "total_usuarios": len(usuarios_com_atividades),
            "total_computadores": total_computadores,
            "total_atividades": total_atividades,
            "filtros": {
                "data_inicio": data_inicio,
                "data_fim": data_fim,
                "evento": evento,
                "processo": processo,
            }
        }

    @staticmethod
    async def diagnosticar_usuario(
        db: AsyncSession,
        usuario_id: int
    ) -> Optional[Dict[str, Any]]:
        """
        Faz um diagnóstico completo do usuário para entender por que não
        há atividades.

        Args:
            db: Sessão assíncrona do banco
            usuario_id: ID do usuário

        Returns:
            Dict com informações de diagnóstico detalhadas
        """
        stmt_usuario = select(Usuario).where(Usuario.id == usuario_id)
        result_usuario = await db.execute(stmt_usuario)
        usuario = result_usuario.scalar_one_or_none()

        if not usuario:
            return None

        stmt_computadores = select(Computer).where(
            Computer.usuario_id == usuario_id)
        result_computadores = await db.execute(stmt_computadores)
        computadores = result_computadores.scalars().all()

        stmt_nao_vinculados = select(Computer).where(
            and_(
                Computer.nome_usuario == usuario.nome,
                Computer.usuario_id is None
            )
        )
        result_nao_vinculados = await db.execute(stmt_nao_vinculados)
        computadores_nao_vinculados = result_nao_vinculados.scalars().all()

        total_atividades = 0
        atividades_recentes = []
        if computadores:
            computador_ids = [c.id for c in computadores]
            stmt_atividades = select(Activity).where(
                Activity.computador_id.in_(computador_ids)
            ).order_by(Activity.data_hora.desc()).limit(5)
            result_atividades = await db.execute(stmt_atividades)
            atividades = result_atividades.scalars().all()
            atividades_recentes = [
                to_schema_dict(
                    ActivityResponse,
                    a) for a in atividades]

            from sqlalchemy import func
            stmt_count = select(func.count(Activity.id)).where(
                Activity.computador_id.in_(computador_ids)
            )
            result_count = await db.execute(stmt_count)
            total_atividades = result_count.scalar()

        return {
            "usuario": {
                "id": usuario.id,
                "nome": usuario.nome,
                "ativo": usuario.ativo,
                "data_criacao": usuario.data_criacao,
                "ultimo_login": usuario.ultimo_login,
            },
            "computadores_vinculados": {
                "total": len(computadores),
                "computadores": [
                    {
                        "id": c.id,
                        "nome_host": c.nome_host,
                        "nome_usuario": c.nome_usuario,
                        "numero_serie": c.numero_serie,
                    } for c in computadores
                ]
            },
            "computadores_nao_vinculados": {
                "total": len(computadores_nao_vinculados),
                "computadores": [
                    {
                        "id": c.id,
                        "nome_host": c.nome_host,
                        "nome_usuario": c.nome_usuario,
                        "numero_serie": c.numero_serie,
                    } for c in computadores_nao_vinculados
                ],
                "aviso": (
                    "Estes computadores têm o mesmo nome de usuário mas não "
                    "estão vinculados. Execute o script de vinculação."
                )
            },
            "atividades": {
                "total": total_atividades,
                "recentes": atividades_recentes
            },
            "diagnostico": {
                "tem_computadores_vinculados": len(computadores) > 0,
                "tem_computadores_nao_vinculados": (
                    len(computadores_nao_vinculados) > 0
                ),
                "tem_atividades": total_atividades > 0,
                "recomendacao": (
                    "Tudo OK! Usuário tem computadores vinculados e "
                    "atividades."
                    if len(computadores) > 0 and total_atividades > 0
                    else (
                        "Execute o script "
                        "'scripts/vincular_usuarios_computadores.py' "
                        "para vincular computadores ao usuário."
                    )
                    if len(computadores_nao_vinculados) > 0
                    else (
                        "Usuário não possui computadores vinculados e não há "
                        "computadores para vincular."
                    )
                    if len(computadores) == 0
                    else (
                        "Usuário tem computadores vinculados mas não há "
                        "atividades registradas."
                    )
                )
            }
        }
