#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
Utilitários de Paginação Inteligente para Relatórios PDF
Sistema para evitar espaços longos em branco nos relatórios
"""

from reportlab.platypus import PageBreak, Spacer, KeepTogether
from reportlab.lib.units import inch

class SmartPagination:
    """Classe para gerenciar paginação inteligente em relatórios PDF"""
    
    def __init__(self, page_height=11*inch, margin_top=1*inch, margin_bottom=1*inch):
        """
        Inicializa o gerenciador de paginação
        
        Args:
            page_height: Altura total da página em pontos
            margin_top: Margem superior em pontos
            margin_bottom: Margem inferior em pontos
        """
        self.page_height = page_height
        self.margin_top = margin_top
        self.margin_bottom = margin_bottom
        self.usable_height = page_height - margin_top - margin_bottom
        self.current_height = 0
        self.elements_buffer = []
        
    def add_element(self, element, estimated_height=None):
        """
        Adiciona um elemento ao buffer com estimativa de altura
        
        Args:
            element: Elemento ReportLab (Paragraph, Table, Image, etc.)
            estimated_height: Altura estimada em pontos (opcional)
        """
        if estimated_height is None:
            estimated_height = self._estimate_element_height(element)
        
        # Se o elemento não cabe na página atual, força quebra
        if self.current_height + estimated_height > self.usable_height:
            self._force_page_break()
        
        self.elements_buffer.append(element)
        self.current_height += estimated_height
    
    def add_with_break_if_needed(self, element, estimated_height=None, force_break=False):
        """
        Adiciona elemento com quebra de página inteligente se necessário
        
        Args:
            element: Elemento ReportLab
            estimated_height: Altura estimada em pontos
            force_break: Força quebra de página antes do elemento
        """
        if estimated_height is None:
            estimated_height = self._estimate_element_height(element)
        
        # Força quebra se solicitado ou se não cabe na página
        if force_break or (self.current_height + estimated_height > self.usable_height):
            if self.elements_buffer:  # Só adiciona quebra se há elementos no buffer
                self.elements_buffer.append(PageBreak())
            self.current_height = 0
        
        self.elements_buffer.append(element)
        self.current_height += estimated_height
    
    def add_group_with_break(self, elements, group_estimated_height=None):
        """
        Adiciona um grupo de elementos que devem ficar juntos
        
        Args:
            elements: Lista de elementos ReportLab
            group_estimated_height: Altura total estimada do grupo
        """
        if group_estimated_height is None:
            group_estimated_height = sum(self._estimate_element_height(elem) for elem in elements)
        
        # Se o grupo não cabe na página atual, força quebra
        if self.current_height + group_estimated_height > self.usable_height:
            if self.elements_buffer:  # Só adiciona quebra se há elementos no buffer
                self.elements_buffer.append(PageBreak())
            self.current_height = 0
        
        # Adiciona o grupo como KeepTogether se tiver múltiplos elementos
        if len(elements) > 1:
            self.elements_buffer.append(KeepTogether(elements))
        else:
            self.elements_buffer.extend(elements)
        
        self.current_height += group_estimated_height
    
    def add_spacer(self, height=0.2*inch):
        """
        Adiciona espaçamento inteligente
        
        Args:
            height: Altura do espaçamento
        """
        if self.current_height + height > self.usable_height:
            # Se não cabe, pula para próxima página
            self._force_page_break()
        else:
            self.elements_buffer.append(Spacer(1, height))
            self.current_height += height
    
    def _force_page_break(self):
        """Força quebra de página"""
        if self.elements_buffer:
            self.elements_buffer.append(PageBreak())
        self.current_height = 0
    
    def _estimate_element_height(self, element):
        """
        Estima a altura de um elemento ReportLab
        
        Args:
            element: Elemento ReportLab
            
        Returns:
            Altura estimada em pontos
        """
        # Estimativas baseadas no tipo de elemento
        if hasattr(element, '__class__'):
            class_name = element.__class__.__name__
            
            if class_name == 'Paragraph':
                # Estima baseado no texto e fonte
                text = str(element.text) if hasattr(element, 'text') else ''
                lines = max(1, len(text) // 80)  # Aproximadamente 80 chars por linha
                return lines * 14  # 14 pontos por linha
            
            elif class_name == 'Table':
                # Estima baseado no número de linhas
                rows = len(element._cellvalues) if hasattr(element, '_cellvalues') else 1
                return rows * 20 + 20  # 20 pontos por linha + cabeçalho
            
            elif class_name == 'Image':
                # Usa a altura real da imagem se disponível
                return getattr(element, 'drawHeight', 100)
            
            elif class_name == 'Spacer':
                # Usa a altura real do espaçador
                return getattr(element, 'height', 20)
            
            elif class_name == 'PageBreak':
                return 0  # Quebra de página não ocupa espaço
            
            elif class_name == 'KeepTogether':
                # Estima baseado nos elementos internos
                return sum(self._estimate_element_height(elem) for elem in element._content)
        
        # Fallback para elementos desconhecidos
        return 50
    
    def get_story(self):
        """
        Retorna a lista de elementos formatada para o PDF
        
        Returns:
            Lista de elementos ReportLab prontos para doc.build()
        """
        return self.elements_buffer.copy()
    
    def reset(self):
        """Reinicia o gerenciador de paginação"""
        self.current_height = 0
        self.elements_buffer = []

def create_smart_pagination(page_height=11*inch, margin_top=1*inch, margin_bottom=1*inch):
    """
    Função utilitária para criar um gerenciador de paginação inteligente
    
    Args:
        page_height: Altura total da página em pontos
        margin_top: Margem superior em pontos
        margin_bottom: Margem inferior em pontos
        
    Returns:
        Instância de SmartPagination
    """
    return SmartPagination(page_height, margin_top, margin_bottom)

# Constantes para estimativas de altura
HEIGHT_ESTIMATES = {
    'title': 50,           # Títulos grandes
    'heading': 30,         # Subtítulos
    'paragraph': 20,       # Parágrafos normais
    'table_header': 25,    # Cabeçalho de tabela
    'table_row': 18,       # Linha de tabela
    'spacer_small': 10,    # Espaçamento pequeno
    'spacer_medium': 20,   # Espaçamento médio
    'spacer_large': 40,    # Espaçamento grande
    'chart_small': 150,    # Gráfico pequeno
    'chart_medium': 250,   # Gráfico médio
    'chart_large': 350,    # Gráfico grande
    'logo': 100,           # Logo
}

def get_height_estimate(element_type, custom_height=None):
    """
    Obtém estimativa de altura para tipos de elementos
    
    Args:
        element_type: Tipo do elemento (chave em HEIGHT_ESTIMATES)
        custom_height: Altura personalizada (sobrescreve estimativa)
        
    Returns:
        Altura estimada em pontos
    """
    if custom_height is not None:
        return custom_height
    return HEIGHT_ESTIMATES.get(element_type, 30)


# --- CondPageBreak: só nova página se o espaço útil restante for insuficiente ---


def _cond_page_break(min_height_pt: float):
    from reportlab.platypus import CondPageBreak

    return CondPageBreak(min_height_pt)


def cond_page_break_before_chart_section():
    """Após texto/tabelas: início da secção de gráficos (título + 1.º bloco)."""
    return _cond_page_break(4.25 * inch)


def cond_page_break_before_charts_title_block():
    """Antes do título da secção de gráficos (ex.: Antenna Check)."""
    return _cond_page_break(3.35 * inch)


def cond_page_break_min_remaining_height(inches: float):
    """Altura mínima (em polegadas) no frame atual antes de continuar o fluxo."""
    return _cond_page_break(float(inches) * inch)


def cond_page_break_before_large_figure(image_draw_height_inch: float = 4.0):
    """Antes de título + imagem larga (ex. 7×4\")."""
    extra_inch = 0.55
    return _cond_page_break((extra_inch + image_draw_height_inch + 0.2) * inch)


def cond_page_break_before_medium_figure(image_draw_height_inch: float = 2.5):
    """Antes de título + parágrafo + imagem média (ex. 7×2.5\")."""
    extra_inch = 0.85
    return _cond_page_break((extra_inch + image_draw_height_inch + 0.25) * inch)


def cond_page_break_before_footer_block():
    """Antes do bloco final (informações adicionais / rodapé)."""
    return _cond_page_break(2.0 * inch)


def cond_page_break_before_subsection_with_figure(figure_height_inch: float):
    """Antes de subtítulo + figura (Quick Threshold, etc.)."""
    return _cond_page_break((0.4 + figure_height_inch + 0.35) * inch)

