import datetime
from typing import Optional, List
from sqlalchemy import select, and_, delete
from sqlalchemy.ext.asyncio import AsyncSession
from src.models.computadores import Computer, ComputerOnline
from src.schemas.computadores import ComputerCreate, ComputerUpdate


class ComputadorCRUD:
    """CRUD operations for Computer model"""

    async def get_by_id(
        self,
            db: AsyncSession,
            computador_id: str) -> Optional[Computer]:
        result = await db.execute(
            select(Computer).where(
                Computer.id == str(computador_id)
                )
            )
        return result.scalar_one_or_none()

    async def list(
        self,
        db: AsyncSession,
        *,
        empresa_id: Optional[str] = None,
        nome_host: Optional[str] = None,
        nome_usuario: Optional[str] = None,
        numero_serie: Optional[str] = None,
        modelo: Optional[str] = None,
        fabricante: Optional[str] = None,
        sistema_operacional: Optional[str] = None,
        dominio: Optional[str] = None,
        localizacao: Optional[str] = None,
    ) -> List[Computer]:
        stmt = select(Computer)
        conditions = []
        if empresa_id:
            conditions.append(Computer.empresa_id == str(empresa_id))
        if nome_host:
            conditions.append(Computer.nome_host.ilike(f"%{nome_host}%"))
        if nome_usuario:
            conditions.append(Computer.nome_usuario.ilike(f"%{nome_usuario}%"))
        if numero_serie:
            conditions.append(Computer.numero_serie.ilike(f"%{numero_serie}%"))
        if modelo:
            conditions.append(Computer.modelo.ilike(f"%{modelo}%"))
        if fabricante:
            conditions.append(Computer.fabricante.ilike(f"%{fabricante}%"))
        if sistema_operacional:
            conditions.append(
                Computer.sistema_operacional.ilike(
                    f"%{sistema_operacional}%"))
        if dominio:
            conditions.append(Computer.dominio.ilike(f"%{dominio}%"))
        if localizacao:
            conditions.append(Computer.localizacao.ilike(f"%{localizacao}%"))
        if conditions:
            stmt = stmt.where(and_(*conditions))
        result = await db.execute(stmt)
        return list(result.scalars().all())

    async def create(
        self,
            db: AsyncSession,
            payload: ComputerCreate) -> Computer:
        obj = Computer(**payload.model_dump(exclude_unset=True))
        db.add(obj)
        await db.commit()
        await db.refresh(obj)
        return obj

    async def update(
        self,
        db: AsyncSession,
        computador_id: str,
        payload: ComputerUpdate,
    ) -> Optional[Computer]:
        obj = await self.get_by_id(db, computador_id)
        if not obj:
            return None
        for field, value in payload.model_dump(exclude_unset=True).items():
            setattr(obj, field, value)
        await db.commit()
        await db.refresh(obj)
        return obj

    async def delete(self, db: AsyncSession, computador_id: str) -> bool:
        exists = await db.execute(
            select(Computer.id).where(
                Computer.id == str(computador_id)
                )
            )
        if exists.scalar_one_or_none() is None:
            return False
        await db.execute(
            delete(Computer).where(
                Computer.id == str(computador_id)
            )
        )
        await db.commit()
        return True


crud = ComputadorCRUD()


class ComputerOnlineCRUD:
    async def get_by_id(
        self,
            db: AsyncSession,
            computador_id: str) -> Optional[ComputerOnline]:
        result = await db.execute(
            select(ComputerOnline).where(
                ComputerOnline.computador_id == str(computador_id)
            )
        )
        return result.scalar_one_or_none()

    async def create_or_update(
        self,
            db: AsyncSession,
            computador_id: str,
            nome_usuario: str) -> ComputerOnline:
        obj = await self.get_by_id(db, computador_id)
        now_utc = datetime.datetime.now(datetime.timezone.utc)
        if obj:
            obj.usuario = nome_usuario
            obj.ultimo_visto = now_utc
            obj.online = 1
        else:
            obj = ComputerOnline(
                computador_id=computador_id,
                usuario=nome_usuario,
                ultimo_visto=now_utc,
                online=1
            )
            db.add(obj)
        await db.commit()
        await db.refresh(obj)
        return obj

    async def set_online(
        self,
            db: AsyncSession,
            computador_id: str,
            nome_usuario: str) -> Optional[ComputerOnline]:
        obj = await self.get_by_id(db, computador_id)
        if not obj:
            return None
        obj.usuario = nome_usuario
        obj.ultimo_visto = datetime.datetime.now(datetime.timezone.utc)
        obj.online = 1
        await db.commit()
        await db.refresh(obj)
        return obj

    async def set_offline(
        self,
            db: AsyncSession,
            computador_id: str) -> Optional[ComputerOnline]:
        obj = await self.get_by_id(db, computador_id)
        if not obj:
            return None
        obj.online = 0
        await db.commit()
        await db.refresh(obj)
        return obj

    async def delete(self, db: AsyncSession, computador_id: str) -> bool:
        exists = await db.execute(
            select(
                ComputerOnline.computador_id).where(
                    ComputerOnline.computador_id == str(computador_id)
                )
        )
        if exists.scalar_one_or_none() is None:
            return False
        await db.execute(
            delete(ComputerOnline).where(
                ComputerOnline.computador_id == str(computador_id)
            )
        )
        await db.commit()
        return True


crud_online = ComputerOnlineCRUD()
