using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Logging; using PuppeteerSharp; using PuppeteerSharp.Media; using RazorLight; using System; using System.Threading.Tasks; namespace GestionIntegral.Api.Services.Pdf { public class PuppeteerPdfGenerator : IPdfGeneratorService { private readonly IRazorLightEngine _razorEngine; private readonly ILogger _logger; public PuppeteerPdfGenerator(IHostEnvironment hostEnvironment, ILogger logger) { _logger = logger; var rootPath = hostEnvironment.ContentRootPath; _razorEngine = new RazorLightEngineBuilder() .UseFileSystemProject(rootPath) .UseMemoryCachingProvider() .Build(); _logger.LogInformation("Verificando caché de Chromium…"); new BrowserFetcher().DownloadAsync().Wait(); _logger.LogInformation("Chromium listo en caché."); } public async Task GeneratePdfFromRazorTemplateAsync(string templatePath, T model, PdfGenerationOptions? options = null) { if (string.IsNullOrEmpty(templatePath)) throw new ArgumentNullException(nameof(templatePath), "La ruta de la plantilla no puede ser nula o vacía."); if (model == null) throw new ArgumentNullException(nameof(model), "El modelo de datos no puede ser nulo."); options ??= new PdfGenerationOptions(); string htmlContent; try { htmlContent = await _razorEngine.CompileRenderAsync(templatePath, model); } catch (Exception ex) { _logger.LogError(ex, "Error al compilar la plantilla Razor: {TemplatePath}", templatePath); throw new InvalidOperationException($"No se pudo renderizar la plantilla Razor '{templatePath}'.", ex); } IBrowser? browser = null; try { var launchOptions = new LaunchOptions { Headless = true, Args = new[] { "--no-sandbox", "--disable-setuid-sandbox", "--disable-dev-shm-usage" } }; _logger.LogInformation("Lanzando Chromium headless…"); browser = await Puppeteer.LaunchAsync(launchOptions); await using var page = await browser.NewPageAsync(); _logger.LogInformation("Estableciendo contenido HTML en la página."); await page.SetContentAsync(htmlContent, new NavigationOptions { WaitUntil = new[] { WaitUntilNavigation.Networkidle0 } }); _logger.LogInformation("Generando PDF…"); var pdfOptions = new PdfOptions { Format = options.Format, HeaderTemplate = options.HeaderTemplate, FooterTemplate = options.FooterTemplate, PrintBackground = options.PrintBackground, Landscape = options.Landscape, MarginOptions = options.Margin ?? new MarginOptions() }; var pdfBytes = await page.PdfDataAsync(pdfOptions); _logger.LogInformation("PDF generado exitosamente ({Length} bytes).", pdfBytes.Length); return pdfBytes; } catch (Exception ex) { _logger.LogError(ex, "Error durante la generación del PDF con PuppeteerSharp."); throw; } finally { if (browser is not null && !browser.IsClosed) { await browser.CloseAsync(); _logger.LogInformation("Navegador cerrado."); } } } } }