import cv2
import numpy as np
from PIL import Image
from pathlib import Path


class ImageIO:
    """
    Utilitários para carregar e salvar imagens.
    """
    
    SUPPORTED_FORMATS = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.tif', '.webp'}
    
    def __init__(self):
        pass
    
    def load(self, path):
        """
        Carrega uma imagem de arquivo.
        
        Args:
            path: Caminho do arquivo
            
        Returns:
            Imagem BGR ou None se falhar
        """
        path = Path(path)
        
        if not path.exists():
            raise FileNotFoundError(f"Arquivo não encontrado: {path}")
        
        if path.suffix.lower() not in self.SUPPORTED_FORMATS:
            raise ValueError(f"Formato não suportado: {path.suffix}")
        
        # Tentar carregar com OpenCV primeiro
        img = cv2.imread(str(path), cv2.IMREAD_COLOR)
        
        if img is None:
            # Fallback para PIL
            try:
                pil_img = Image.open(path)
                img = np.array(pil_img)
                
                # Converter RGB para BGR se necessário
                if len(img.shape) == 3 and img.shape[2] == 3:
                    img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
                elif len(img.shape) == 3 and img.shape[2] == 4:
                    # RGBA para BGR
                    img = cv2.cvtColor(img, cv2.COLOR_RGBA2BGR)
                    
            except Exception as e:
                raise IOError(f"Erro ao carregar imagem: {e}")
        
        return img
    
    def save(self, img, path, quality=95):
        """
        Salva uma imagem em arquivo.
        
        Args:
            img: Imagem BGR
            path: Caminho de destino
            quality: Qualidade JPEG (1-100)
        """
        path = Path(path)
        
        # Criar diretório se não existir
        path.parent.mkdir(parents=True, exist_ok=True)
        
        # Parâmetros de salvamento
        params = []
        
        if path.suffix.lower() in {'.jpg', '.jpeg'}:
            # JPEG
            params = [cv2.IMWRITE_JPEG_QUALITY, quality]
        elif path.suffix.lower() == '.png':
            # PNG - comprimir mas manter qualidade
            compression = int((100 - quality) / 10)
            params = [cv2.IMWRITE_PNG_COMPRESSION, min(compression, 9)]
        elif path.suffix.lower() == '.webp':
            # WebP
            params = [cv2.IMWRITE_WEBP_QUALITY, quality]
        
        # Salvar
        success = cv2.imwrite(str(path), img, params)
        
        if not success:
            raise IOError(f"Erro ao salvar imagem: {path}")
    
    def load_batch(self, directory, pattern='*'):
        """
        Carrega múltiplas imagens de um diretório.
        
        Args:
            directory: Diretório de origem
            pattern: Padrão de busca (ex: '*.jpg')
            
        Returns:
            Lista de tuplas (path, imagem)
        """
        directory = Path(directory)
        
        if not directory.is_dir():
            raise NotADirectoryError(f"Não é um diretório: {directory}")
        
        images = []
        
        for file_path in directory.glob(pattern):
            if file_path.suffix.lower() in self.SUPPORTED_FORMATS:
                try:
                    img = self.load(file_path)
                    if img is not None:
                        images.append((str(file_path), img))
                except Exception as e:
                    print(f"Erro ao carregar {file_path}: {e}")
        
        return images
    
    def get_image_info(self, path):
        """
        Obtém informações sobre uma imagem sem carregar completamente.
        
        Args:
            path: Caminho da imagem
            
        Returns:
            Dicionário com informações
        """
        path = Path(path)
        
        if not path.exists():
            raise FileNotFoundError(f"Arquivo não encontrado: {path}")
        
        try:
            pil_img = Image.open(path)
            
            info = {
                'format': pil_img.format,
                'mode': pil_img.mode,
                'width': pil_img.width,
                'height': pil_img.height,
                'size_mb': path.stat().st_size / (1024 * 1024),
                'path': str(path),
            }
            
            # Calcular megapixels
            info['megapixels'] = (pil_img.width * pil_img.height) / 1_000_000
            
            pil_img.close()
            
            return info
            
        except Exception as e:
            raise IOError(f"Erro ao ler informações: {e}")
    
    def convert_format(self, input_path, output_path, quality=95):
        """
        Converte formato de imagem.
        
        Args:
            input_path: Caminho de entrada
            output_path: Caminho de saída
            quality: Qualidade (para formatos com perda)
        """
        img = self.load(input_path)
        self.save(img, output_path, quality=quality)
    
    def validate_image(self, path):
        """
        Valida se o arquivo é uma imagem válida.
        
        Args:
            path: Caminho do arquivo
            
        Returns:
            True se válido, False caso contrário
        """
        try:
            path = Path(path)
            
            if not path.exists():
                return False
            
            if path.suffix.lower() not in self.SUPPORTED_FORMATS:
                return False
            
            # Tentar abrir
            img = cv2.imread(str(path))
            
            if img is None:
                pil_img = Image.open(path)
                pil_img.verify()
                pil_img.close()
            
            return True
            
        except Exception:
            return False
    
    def resize_if_needed(self, img, max_size=4096):
        """
        Redimensiona imagem se exceder tamanho máximo.
        
        Args:
            img: Imagem BGR
            max_size: Dimensão máxima permitida
            
        Returns:
            Imagem redimensionada se necessário
        """
        height, width = img.shape[:2]
        
        if max(height, width) <= max_size:
            return img
        
        # Calcular nova dimensão
        if height > width:
            new_height = max_size
            new_width = int(width * (max_size / height))
        else:
            new_width = max_size
            new_height = int(height * (max_size / width))
        
        # Redimensionar
        resized = cv2.resize(
            img,
            (new_width, new_height),
            interpolation=cv2.INTER_AREA
        )
        
        return resized
