import secrets

from typing import Optional, List
from sqlalchemy import select, delete
from src.models.empresas import Empresa
from sqlalchemy.exc import IntegrityError
from sqlalchemy.ext.asyncio import AsyncSession
from src.schemas.empresas import EnterpriseCreate, EnterpriseUpdate


class EmpresaService:
    @staticmethod
    async def criar(db: AsyncSession, payload: EnterpriseCreate) -> Empresa:
        existing_result = await db.execute(
            select(Empresa).where(
                (Empresa.cnpj == payload.cnpj) |
                (Empresa.nomeFantasia == payload.nomeFantasia)
            )
        )
        existing: Optional[Empresa] = existing_result.scalar_one_or_none()
        if existing:
            if not existing.token:
                existing.token = secrets.token_hex(32)
                await db.commit()
                await db.refresh(existing)
            return existing

        token = secrets.token_hex(32)
        obj = Empresa(
            cnpj=payload.cnpj,
            razaoSocial=payload.razaoSocial,
            nomeFantasia=payload.nomeFantasia,
            token=token
        )
        db.add(obj)
        try:
            await db.commit()
        except IntegrityError:
            await db.rollback()
            again = await db.execute(
                select(Empresa).where(
                    (Empresa.cnpj == payload.cnpj) |
                    (Empresa.nomeFantasia == payload.nomeFantasia)
                )
            )
            found = again.scalar_one_or_none()
            if found:
                if not found.token:
                    found.token = secrets.token_hex(32)
                    await db.commit()
                    await db.refresh(found)
                return found
            raise
        await db.refresh(obj)
        return obj

    @staticmethod
    async def listar(
        db: AsyncSession,
        *,
        enterprise_token: Optional[str] = None,
        nomeFantasia: Optional[str] = None,
    ) -> List[Empresa]:
        stmt = select(Empresa)
        if enterprise_token:
            stmt = stmt.where(Empresa.token == enterprise_token)
        if nomeFantasia:
            stmt = stmt.where(Empresa.nomeFantasia.like(f"%{nomeFantasia}%"))
        result = await db.execute(stmt)
        return list(result.scalars().all())

    @staticmethod
    async def atualizar(
        db: AsyncSession,
            empresa_id: str,
            payload: EnterpriseUpdate) -> Optional[Empresa]:
        result = await db.execute(
            select(Empresa).where(Empresa.id == str(empresa_id))
        )
        obj: Optional[Empresa] = result.scalar_one_or_none()
        if not obj:
            return None
        for field, value in payload.model_dump(exclude_unset=True).items():
            setattr(obj, field, value)
        try:
            await db.commit()
        except IntegrityError as e:
            await db.rollback()
            raise e
        await db.refresh(obj)
        return obj

    @staticmethod
    async def deletar(db: AsyncSession, empresa_id: str) -> bool:
        result = await db.execute(
            select(Empresa.id).where(Empresa.id == str(empresa_id))
        )
        exists = result.scalar_one_or_none()
        if not exists:
            return False
        await db.execute(delete(Empresa).where(Empresa.id == str(empresa_id)))
        await db.commit()
        return True
