use crate::application::usecases::exportar_pdf::exportar_pdf; use crate::domain::{ entities::{chave_serie::ChaveSerie, resultado_analise::ResultadoAnalise}, services::{detector_sequencia::agrupar_contiguos, parser_monetario::formatar_valor_br}, }; use crate::infrastructure::pdf_generator::GenpdfGenerator; use crate::ui::app::{AcaoModal, App, EstadoApp}; use egui::{Context, Ui}; const OPCOES_PAGINA: &[usize] = &[50, 100, 200, 1000]; /// Renderiza a tela de resultados. pub fn renderizar(ui: &mut Ui, ctx: &Context, app: &mut App) { // Extrair resultado do estado (sem mover) let resultado = match &app.estado { EstadoApp::ExibindoResultado(r) => r.clone(), _ => return, }; ui.heading("Resultado da Análise"); ui.add_space(8.0); ui.horizontal(|ui| { if ui.button("< Nova Análise").clicked() { app.exibir_confirmacao( "Nova Análise", "Deseja iniciar uma nova análise? O resultado atual será descartado.", AcaoModal::ConfirmarNovaAnalise, ); } if ui.button("⚙ Reconfigurar Colunas").clicked() { app.estado = EstadoApp::ConfigurandoColunas; return; } let pode_reanalisar = app.caminho_arquivo.is_some(); if ui .add_enabled(pode_reanalisar, egui::Button::new("🔄 Reanalisar Arquivo")) .on_hover_text("Reimporta o arquivo do disco com o layout atual e reanalisa") .clicked() { app.reimportar_e_analisar(ctx); } if ui.button("📄 Exportar PDF").clicked() { exportar_para_pdf(app, &resultado); } }); ui.add_space(8.0); // Controle de itens por página ui.horizontal(|ui| { ui.label("Itens por página:"); for &opcao in OPCOES_PAGINA { if ui .selectable_label(app.itens_por_pagina == opcao, opcao.to_string()) .clicked() { app.itens_por_pagina = opcao; app.pagina_faltantes = 0; app.pagina_duplicatas = 0; } } }); ui.add_space(12.0); ui.separator(); egui::ScrollArea::vertical().show(ui, |ui| { // Faltantes renderizar_faltantes(ui, app, &resultado); ui.add_space(12.0); ui.separator(); // Duplicatas renderizar_duplicatas(ui, app, &resultado); ui.add_space(12.0); ui.separator(); // Totais renderizar_totais(ui, &resultado); }); } fn renderizar_totais(ui: &mut Ui, resultado: &ResultadoAnalise) { ui.label(egui::RichText::new("Totais").heading().strong()); ui.add_space(4.0); ui.label(format!( "Total Geral: R$ {}", formatar_valor_br(&resultado.soma_total) )); let mut chaves: Vec<&ChaveSerie> = resultado.soma_por_serie.keys().collect(); chaves.sort(); for chave in chaves { let soma = &resultado.soma_por_serie[chave]; let total_notas = resultado.total_por_serie.get(chave).copied().unwrap_or(0); ui.label(format!( " Série {}: {} nota(s) — R$ {}", chave.label(), total_notas, formatar_valor_br(soma) )); } } fn renderizar_faltantes(ui: &mut Ui, app: &mut App, resultado: &ResultadoAnalise) { let total_faltantes = resultado.total_faltantes(); ui.label( egui::RichText::new(format!("Notas Faltantes ({} total)", total_faltantes)) .heading() .strong(), ); ui.add_space(4.0); if total_faltantes == 0 { ui.label("✔ Nenhuma nota faltante."); return; } let mut chaves: Vec<&ChaveSerie> = resultado.faltantes_por_serie.keys().collect(); chaves.sort(); for chave in chaves { let faltantes = &resultado.faltantes_por_serie[chave]; if faltantes.is_empty() { continue; } // Estatística de completude por série let total_notas = resultado.total_por_serie.get(chave).copied().unwrap_or(0); let total_esperado = total_notas + faltantes.len(); let percentual = total_notas as f64 / total_esperado as f64 * 100.0; ui.horizontal(|ui| { ui.label(format!( "Série {} — {} faltante(s) — {}/{} notas ({:.1}% completo):", chave.label(), faltantes.len(), total_notas, total_esperado, percentual, )); if ui .button("📋 Copiar") .on_hover_text("Copiar todos os números faltantes") .clicked() { let texto = faltantes .iter() .map(|n| n.to_string()) .collect::>() .join(", "); ui.ctx().copy_text(texto); } }); // Paginação (por faltante individual) let total_paginas = (faltantes.len() + app.itens_por_pagina - 1) / app.itens_por_pagina; if app.pagina_faltantes >= total_paginas { app.pagina_faltantes = 0; } let inicio = app.pagina_faltantes * app.itens_por_pagina; let fim = (inicio + app.itens_por_pagina).min(faltantes.len()); // Exibir grupos contíguos da página atual for (a, b) in agrupar_contiguos(&faltantes[inicio..fim]) { if a == b { ui.label(format!(" • {}", a)); } else { ui.label(format!(" • {}–{} ({} notas)", a, b, b - a + 1)); } } if total_paginas > 1 { ui.horizontal(|ui| { if ui.button("◀").clicked() && app.pagina_faltantes > 0 { app.pagina_faltantes -= 1; } ui.label(format!( "Página {} / {}", app.pagina_faltantes + 1, total_paginas )); if ui.button("▶").clicked() && app.pagina_faltantes + 1 < total_paginas { app.pagina_faltantes += 1; } }); } } } fn renderizar_duplicatas(ui: &mut Ui, app: &mut App, resultado: &ResultadoAnalise) { let total_dup = resultado.total_duplicatas(); ui.label( egui::RichText::new(format!("Notas Duplicadas ({} grupo(s))", total_dup)) .heading() .strong(), ); ui.add_space(4.0); if total_dup == 0 { ui.label("✔ Nenhuma nota duplicada."); return; } let mut chaves: Vec<&ChaveSerie> = resultado.duplicadas_por_serie.keys().collect(); chaves.sort(); for chave in chaves { let duplicatas = &resultado.duplicadas_por_serie[chave]; if duplicatas.is_empty() { continue; } ui.horizontal(|ui| { ui.label(format!( "Série {} — {} grupo(s) duplicado(s):", chave.label(), duplicatas.len() )); if ui .button("📋 Copiar") .on_hover_text("Copiar números duplicados") .clicked() { let texto = duplicatas .iter() .map(|(n, c)| format!("{} ({}x)", n, c)) .collect::>() .join(", "); ui.ctx().copy_text(texto); } }); let total_paginas = (duplicatas.len() + app.itens_por_pagina - 1) / app.itens_por_pagina; if app.pagina_duplicatas >= total_paginas { app.pagina_duplicatas = 0; } let inicio = app.pagina_duplicatas * app.itens_por_pagina; let fim = (inicio + app.itens_por_pagina).min(duplicatas.len()); for (numero, count) in &duplicatas[inicio..fim] { ui.label(format!( " • NF {} / Série {} — {} ocorrências", numero, chave.label(), count )); } if total_paginas > 1 { ui.horizontal(|ui| { if ui.button("◀").clicked() && app.pagina_duplicatas > 0 { app.pagina_duplicatas -= 1; } ui.label(format!( "Página {} / {}", app.pagina_duplicatas + 1, total_paginas )); if ui.button("▶").clicked() && app.pagina_duplicatas + 1 < total_paginas { app.pagina_duplicatas += 1; } }); } } } fn exportar_para_pdf(app: &mut App, resultado: &ResultadoAnalise) { if let Some(caminho) = rfd::FileDialog::new() .set_file_name("relatorio.pdf") .add_filter("PDF", &["pdf"]) .save_file() { let gerador = GenpdfGenerator; let nome_layout = if app.nome_layout_atual.is_empty() { None } else { Some(app.nome_layout_atual.as_str()) }; match exportar_pdf( &gerador, resultado, &app.nome_arquivo, nome_layout, &caminho, ) { Ok(_) => { app.exibir_aviso( "Sucesso", format!("PDF exportado para: {}", caminho.display()), ); } Err(e) => { app.exibir_erro(format!("Erro ao exportar PDF: {}", e)); } } } }