from typing import Optional, List
from datetime import date
from diarios.models import DiarioOficial, TipoDiarioOficial
from django.core.exceptions import ObjectDoesNotExist
from ninja.errors import HttpError


class DiarioOficialService:
    @staticmethod
    def criar_diario(
        data: date,
        numero: str,
        tipo_id: Optional[int] = None,
        arquivo=None,
        link: Optional[str] = None
    ) -> DiarioOficial:
        """
        Cria um novo diário oficial
        
        Args:
            data: Data do diário
            numero: Número do diário
            tipo_id: ID do tipo de diário
            arquivo: Arquivo PDF (opcional)
            link: URL do PDF (opcional)
            
        Returns:
            DiarioOficial: O diário criado
            
        Raises:
            HttpError: Se ocorrer algum erro na criação
        """
        try:
            tipo = TipoDiarioOficial.objects.get(pk=tipo_id) if tipo_id else None
            
            diario = DiarioOficial(
                data=data,
                numero=numero,
                tipo=tipo,
                link=link
            )
            
            if arquivo:
                diario.arquivo = arquivo
                
            diario.full_clean()
            diario.save()
            return diario
            
        except ObjectDoesNotExist:
            raise HttpError(404, "Tipo de diário não encontrado")
        except Exception as e:
            raise HttpError(400, f"Erro ao criar diário: {str(e)}")

    @staticmethod
    def obter_diario_por_id(id: int) -> DiarioOficial:
        """
        Obtém um diário pelo ID
        
        Args:
            id: ID do diário
            
        Returns:
            DiarioOficial: O diário encontrado
            
        Raises:
            HttpError: Se o diário não for encontrado
        """
        try:
            return DiarioOficial.objects.get(pk=id)
        except ObjectDoesNotExist:
            raise HttpError(404, "Diário não encontrado")

    @staticmethod
    def listar_diarios(
        tipo_id: Optional[int] = None,
        data_inicio: Optional[date] = None,
        data_fim: Optional[date] = None,
        numero: Optional[str] = None
    ) -> List[DiarioOficial]:
        """
        Lista diários com filtros opcionais
        
        Args:
            tipo_id: ID do tipo de diário para filtrar
            data_inicio: Data inicial para filtrar
            data_fim: Data final para filtrar
            numero: Número do diário para filtrar
            
        Returns:
            List[DiarioOficial]: Lista de diários filtrados
        """
        queryset = DiarioOficial.objects.all().order_by('-data')
        
        if tipo_id:
            queryset = queryset.filter(tipo_id=tipo_id)
            
        if data_inicio and data_fim:
            queryset = queryset.filter(data__range=[data_inicio, data_fim])
        elif data_inicio:
            queryset = queryset.filter(data__gte=data_inicio)
        elif data_fim:
            queryset = queryset.filter(data__lte=data_fim)
            
        if numero:
            queryset = queryset.filter(numero__icontains=numero)
            
        return list(queryset)

    @staticmethod
    def atualizar_diario(
        id: int,
        data: Optional[date] = None,
        numero: Optional[str] = None,
        tipo_id: Optional[int] = None,
        arquivo=None,
        link: Optional[str] = None
    ) -> DiarioOficial:
        """
        Atualiza um diário existente
        
        Args:
            id: ID do diário a ser atualizado
            data: Nova data (opcional)
            numero: Novo número (opcional)
            tipo_id: Novo tipo (opcional)
            arquivo: Novo arquivo (opcional)
            link: Novo link (opcional)
            
        Returns:
            DiarioOficial: O diário atualizado
            
        Raises:
            HttpError: Se ocorrer algum erro na atualização
        """
        try:
            diario = DiarioOficial.objects.get(pk=id)
            
            if data is not None:
                diario.data = data
                
            if numero is not None:
                diario.numero = numero
                
            if tipo_id is not None:
                tipo = TipoDiarioOficial.objects.get(pk=tipo_id) if tipo_id else None
                diario.tipo = tipo
                
            if arquivo is not None:
                diario.arquivo = arquivo
                
            if link is not None:
                diario.link = link
                
            diario.full_clean()
            diario.save()
            return diario
            
        except ObjectDoesNotExist:
            raise HttpError(404, "Diário ou tipo não encontrado")
        except Exception as e:
            raise HttpError(400, f"Erro ao atualizar diário: {str(e)}")

    @staticmethod
    def deletar_diario(id: int) -> None:
        """
        Remove um diário
        
        Args:
            id: ID do diário a ser removido
            
        Raises:
            HttpError: Se o diário não for encontrado
        """
        try:
            diario = DiarioOficial.objects.get(pk=id)
            diario.delete()
        except ObjectDoesNotExist:
            raise HttpError(404, "Diário não encontrado")