const express = require('express');
const puppeteer = require('puppeteer');
const { v4: uuidv4 } = require('uuid');
const fs = require('fs');
const path = require('path');
const swaggerJsDoc = require('swagger-jsdoc');
const swaggerUi = require('swagger-ui-express');
const cors = require('cors');
const archiver = require('archiver');



const app = express();
const PORT = process.env.PORT || 3000;

app.use(cors());
app.use(cors({
  origin: '*',  // Permite qualquer origem
}));
app.use(express.json());
app.use(express.static(path.join(__dirname, 'public')));

// Swagger documentation configuration
const swaggerOptions = {
  swaggerDefinition: {
    openapi: '3.0.0',
    info: {
      title: 'HTML TO PDF API CONVERTER',
      version: '1.0.0',
      description: 'The PDF Generation API allows users to create high-quality PDFs from any provided URL, ensuring fidelity to the original HTML layout. Users can customize their PDF outputs with various options, including page size, margins, headers, footers, and more. This API is perfect for developers looking to integrate robust PDF generation capabilities into their applications. Additionally, users can easily <a href="/doc/API-PDF.postman_collection">download</a> a comprehensive Postman collection for testing and integration.'
    },
  },
  apis: ['./docs/generate-pdf.js', './docs/generate-pdf-multiples.js', './docs/delete-pdfs.js'], // path to the files containing API annotations
};

const swaggerDocs = swaggerJsDoc(swaggerOptions);
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDocs));

// Helper function to generate a GUID
function generateGuid() {
  return uuidv4();
}

// Function to create the PDF
async function generatePDF(url, options) {
  const browser = await puppeteer.launch({
    args: ['--no-sandbox', '--disable-setuid-sandbox']
  });
  const page = await browser.newPage();

  const startLoading = performance.now(); // Start time for page loading
  await page.goto(url, { waitUntil: 'networkidle0' });
  const endLoading = performance.now(); // Time after page loading

  const loadTime = (endLoading - startLoading).toFixed(2); // Load time in milliseconds

  const pdfOptions = {
    path: options.path || null,
    format: options.format || 'A4',
    width: options.width || undefined,
    height: options.height || undefined,
    scale: options.scale || 1,
    displayHeaderFooter: options.displayHeaderFooter || false,
    headerTemplate: options.headerTemplate || '',
    footerTemplate: options.footerTemplate || '',
    printBackground: options.printBackground || true,
    landscape: options.landscape || false,
    pageRanges: options.pageRanges || '',
    margin: options.margin || {
      top: '20mm',
      bottom: '20mm',
      left: '10mm',
      right: '10mm',
    },
    preferCSSPageSize: options.preferCSSPageSize || false,
    omitBackground: options.omitBackground || false,
    timeout: options.timeout || 30000,
    scaleFactor: options.scaleFactor || 1,
  };

  const pdfBuffer = await page.pdf(pdfOptions);
  await browser.close();

  return { pdfBuffer, loadTime }; // Returns the PDF buffer and load time
}

// Route to generate PDF
app.post('/generate-pdf', async (req, res) => {
  try {
    const { url, returnType = 'stream', options = {}, password = '' } = req.body;

    if (!url) {
      return res.status(400).json({ error: 'URL is required' });
    }

    const start = performance.now(); // Start time for PDF generation
    const { pdfBuffer, loadTime } = await generatePDF(url, options);
    const end = performance.now(); // End time after PDF generation

    const generationTime = (end - start).toFixed(2); // Generation time in milliseconds
    const pdfSize = pdfBuffer.length; // Size of the PDF in bytes

    // If a password is requested, add it to the PDF
    if (password) {
      pdfBuffer = await addPasswordToPDF(pdfBuffer, password);
    }

    // If 'file' is requested, save to server and return download link
    if (returnType === 'file') {
      const guid = generateGuid();
      const pdfDir = path.join(__dirname, 'pdfs');

      if (!fs.existsSync(pdfDir)) {
        fs.mkdirSync(pdfDir); // Create directory if it does not exist
      }

      const filePath = path.join(pdfDir, `${guid}.pdf`);
      fs.writeFileSync(filePath, pdfBuffer);

      const protocol = req.headers['x-forwarded-proto'] || req.protocol;
      const host = req.headers['x-forwarded-host'] || req.get('host');
      const fileUrl = `${protocol}://${host}/pdfs/${guid}.pdf`;

      return res.json({
        message: 'PDF generated successfully',
        fileUrl: fileUrl,
        filename: `${guid}.pdf`,
        pdfSizeInBytes: `${pdfSize}`, // Size of the PDF
        loadTimeInMs: `${loadTime}`, // Page load time
        generationTimeInMs: `${generationTime - loadTime}`, // Generation time excluding load time
        totalTimeInMs: `${generationTime}`, // Total generation time
      });
    }

    // If 'stream' is requested, return the PDF as a stream
    res.set({
      'Content-Type': 'application/pdf',
      'Content-Disposition': `inline; filename="${guid}.pdf"`,
      'Content-Length': pdfBuffer.length,
    });

    return res.send(pdfBuffer);

  } catch (error) {
    console.error('Error generating PDF:', error);
    return res.status(500).json({ error: 'An error occurred while generating the PDF' });
  }
});

app.post('/generate-pdf-multiples', async (req, res) => {
  try {
    const { urls, returnType = 'file', options = {} } = req.body;

    if (!urls || !Array.isArray(urls) || urls.length === 0) {
      return res.status(400).json({ error: 'A valid array of URLs is required' });
    }

    const start = performance.now(); // Start time for overall process
    const guid = generateGuid();
    const basePdfDir = path.join(__dirname, 'pdfs');
    const zipDir = path.join(basePdfDir, 'zip');
    
    if (!fs.existsSync(basePdfDir)) {
      fs.mkdirSync(basePdfDir);
    }
    if (!fs.existsSync(zipDir)) {
      fs.mkdirSync(zipDir);
    }

    // A temporary folder for this specific request's PDFs
    const tempFolder = path.join(zipDir, guid);
    fs.mkdirSync(tempFolder);

    const pdfResults = [];
    let totalLoadTime = 0;
    let totalGenerationTime = 0;

    // Generate PDF for each URL
    for (let i = 0; i < urls.length; i++) {
      const item = urls[i];
      const url = typeof item === 'string' ? item : item.url;
      const customName = typeof item === 'string' ? null : item.name;
      
      if (!url) continue;

      const pdfStart = performance.now();
      const { pdfBuffer, loadTime } = await generatePDF(url, options);
      const pdfEnd = performance.now();
      
      const generationTime = (pdfEnd - pdfStart).toFixed(2);
      totalLoadTime += parseFloat(loadTime);
      totalGenerationTime += parseFloat(generationTime);

      const baseName = customName ? customName : generateGuid();
      const fileName = `${baseName}.pdf`;
      const filePath = path.join(tempFolder, fileName);
      fs.writeFileSync(filePath, pdfBuffer);
      
      pdfResults.push({ url, fileName, size: pdfBuffer.length });
    }

    // Now zip the folder
    const zipFileName = `${guid}.zip`;
    const zipFilePath = path.join(zipDir, zipFileName);
    const output = fs.createWriteStream(zipFilePath);
    const archive = archiver('zip', {
      zlib: { level: 9 } // Sets the compression level.
    });

    output.on('close', function() {
      // Remove temporary folder after zipping
      fs.rmSync(tempFolder, { recursive: true, force: true });
      
      const end = performance.now();
      const totalTime = (end - start).toFixed(2);

      const protocol = req.headers['x-forwarded-proto'] || req.protocol;
      const host = req.headers['x-forwarded-host'] || req.get('host');
      const fileUrl = `${protocol}://${host}/pdfs/zip/${zipFileName}`;

      return res.json({
        message: 'PDFs generated and zipped successfully',
        fileUrl: fileUrl,
        filename: zipFileName,
        zipSizeInBytes: archive.pointer(),
        totalLoadTimeInMs: totalLoadTime.toFixed(2),
        totalGenerationTimeInMs: totalGenerationTime.toFixed(2),
        totalTimeInMs: totalTime,
        details: pdfResults
      });
    });

    archive.on('error', function(err) {
      throw err;
    });

    archive.pipe(output);
    archive.directory(tempFolder, false); // false means don't include the folder name itself, just contents
    archive.finalize();

  } catch (error) {
    console.error('Error generating multiple PDFs:', error);
    return res.status(500).json({ error: 'An error occurred while generating the multiple PDFs' });
  }
});


// New route to delete all PDFs
app.delete('/delete-pdfs', (req, res) => {
  const pdfDir = path.join(__dirname, 'pdfs');

  // Check if the directory exists
  if (!fs.existsSync(pdfDir)) {
    return res.status(404).json({ error: 'PDF directory not found' });
  }

  // Read files from the directory and remove them
  fs.readdir(pdfDir, (err, files) => {
    if (err) {
      return res.status(500).json({ error: 'Error reading PDF directory' });
    }

    // Delete each file and directory
    files.forEach(file => {
      const filePath = path.join(pdfDir, file);
      fs.rmSync(filePath, { recursive: true, force: true });
    });

    return res.json({ message: 'All PDFs deleted successfully' });
  });
});

// Middleware to serve static files (the generated PDFs)
app.use('/pdfs', express.static(path.join(__dirname, 'pdfs')));

app.get('/', (req, res) => {
  res.send(`
    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>API Overview</title>
        <style>
            body { font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif; background-color: #f4f4f4; color: #333; margin: 0; padding: 20px; }
            h1 { color: #007BFF; font-size: 2em; }
            h2 { color: #555; }
            p { line-height: 1.6; }
            a { color: #007BFF; text-decoration: none; font-weight: bold; }
            a:hover { text-decoration: underline; }
            .container { max-width: 800px; margin: auto; padding: 20px; background: white; border-radius: 5px; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1); }
            footer { margin-top: 20px; text-align: center; }
            .tab { display: none; }
            .tab.active { display: block; }
            .tabs { margin-bottom: 20px; }
            .tabs button { margin-right: 10px; padding: 10px; border: none; background: #007BFF; color: white; border-radius: 5px; cursor: pointer; }
            .tabs button:hover { background: #0056b3; }
        </style>
        <script>
            function showTab(lang) {
                const tabs = document.querySelectorAll('.tab');
                tabs.forEach(tab => tab.classList.remove('active'));
                document.getElementById(lang).classList.add('active');
            }
        </script>
    </head>
    <body>
        <div class="container">
            <h1>Welcome to the PDF Generation API</h1>
            <h2>Version: <strong>1.0.0</strong></h2>
            <div class="tabs">
                <button onclick="showTab('english')">English</button>
                <button onclick="showTab('portuguese')">Português</button>
                <button onclick="showTab('spanish')">Español</button>
            </div>

            <div id="english" class="tab active">
                <p>This API is designed to provide a simple and efficient way to generate PDFs from URLs. Built using Node.js and Express, it follows RESTful principles to ensure a clean and consistent interface.</p>
                <p>With various options, you can customize your PDF generation, including features such as password protection and page formatting.</p>
                <p>For detailed information, usage examples, and API endpoints, please refer to our <a href="/api-docs">documentation</a>.</p>
            </div>

            <div id="portuguese" class="tab">
                <p>Esta API foi projetada para fornecer uma maneira simples e eficiente de gerar PDFs a partir de URLs. Construída usando Node.js e Express, segue princípios RESTful para garantir uma interface limpa e consistente.</p>
                <p>Com várias opções, você pode personalizar a geração do seu PDF, incluindo recursos como proteção por senha e formatação de páginas.</p>
                <p>Para informações detalhadas, exemplos de uso e endpoints da API, consulte nossa <a href="/api-docs">documentação</a>.</p>
            </div>

            <div id="spanish" class="tab">
                <p>Esta API está diseñada para proporcionar una manera simple y eficiente de generar PDFs a partir de URLs. Construida usando Node.js y Express, sigue principios RESTful para garantizar una interfaz limpia y consistente.</p>
                <p>Con varias opciones, puedes personalizar tu generación de PDF, incluyendo características como protección por contraseña y formato de página.</p>
                <p>Para información detallada, ejemplos de uso y endpoints de la API, consulta nuestra <a href="/api-docs">documentación</a>.</p>
            </div>

            <footer>
                <p>&copy; ${new Date().getFullYear()} PDF Generation API. All rights reserved.</p>
            </footer>
        </div>
    </body>
    </html>
  `);
});

app.listen(PORT, () => {
  console.log(`Server is running on http://localhost:${PORT}`);
});
