const db = require('../models');
const { sendError } = require('../utils/response');
const asyncHandler = require('../middlewares/asyncHandler');
const PDFDocument = require('pdfkit');
const path = require('path');
const fs = require('fs');
const atpService = require('../services/atpService');

/**
 * Generate BOM Requirement List PDF
 * This is a dedicated controller for generating BOM requirement list PDFs
 */
exports.generateBOMRequirementListPDF = asyncHandler(async (req, res) => {
  const { customerPOId, companyId, plantId, itemId } = req.body;
  const companyIdFromUser = req.user?.companyId || companyId;
  const plantIdFromUser = req.user?.plantId || plantId;

  if (!customerPOId) {
    return sendError(res, 'Customer PO ID is required', 400);
  }

  try {
    // Get Customer PO with items
    const customerPO = await db.CustomerPO.findByPk(customerPOId, {
      include: [
        {
          model: db.CustomerPOItem,
          as: 'items',
          include: [
            {
              model: db.Material,
              as: 'material',
              attributes: ['id', 'material_number', 'description', 'drawing_number']
            }
          ]
        },
        {
          model: db.BusinessPartner,
          as: 'customer',
          attributes: ['id', 'name', 'bp_number', 'legal_name']
        },
        {
          model: db.BPAddress,
          as: 'customerAddress',
          attributes: ['id', 'address_line1', 'address_line2', 'pincode'],
          include: [
            {
              model: db.City,
              as: 'city',
              attributes: ['id', 'city_name']
            },
            {
              model: db.State,
              as: 'state',
              attributes: ['id', 'state_name']
            }
          ]
        }
      ]
    });

    if (!customerPO) {
      return sendError(res, 'Customer PO not found', 404);
    }

    const finalPlantId = customerPO.plant_id || plantIdFromUser;
    const finalCompanyId = customerPO.company_id || companyIdFromUser;

    // Get print header
    const printHeader = await db.PrintHeader.findOne({
      where: { company_id: finalCompanyId, status: 'ACTIVE' },
      order: [['id', 'DESC']]
    });

    // Get company data
    const company = await db.Company.findByPk(finalCompanyId, {
      include: [
        { model: db.State, as: 'state', attributes: ['id', 'state_code', 'state_name', 'gst_state_code'] }
      ]
    });

    // Map to store aggregated quantities by material
    const materialMap = new Map();

    // Recursive function to fetch components and calculate quantities
    const fetchComponentsRecursively = async (bomId, parentRequiredQty, processedMaterials = new Set(), level = 0) => {
      if (processedMaterials.has(bomId)) {
        return;
      }
      processedMaterials.add(bomId);

      const bomHeader = await db.BOMHeader.findByPk(bomId, {
        include: [
          {
            model: db.BOMComponent,
            as: 'components',
            where: { status: 'ACTIVE' },
            required: false,
            include: [
              {
                model: db.Material,
                as: 'material',
                attributes: ['id', 'material_number', 'description', 'drawing_number']
              }
            ],
            order: [['item_number', 'ASC']]
          }
        ]
      });

      if (bomHeader && bomHeader.components) {
        for (const comp of bomHeader.components) {
          const componentQuantity = parseFloat(comp.component_quantity || 0);
          const material = comp.material;

          if (material) {
            const requiredQty = componentQuantity * parentRequiredQty;
            const stockInfo = await atpService.getAvailableStock(material.id, finalPlantId);
            const stockQty = stockInfo.available || 0;
            const purchaseQty = Math.max(0, requiredQty - stockQty);

            const materialId = material.id;
            if (materialMap.has(materialId)) {
              const existing = materialMap.get(materialId);
              existing.required_qty += requiredQty;
              existing.stock_qty = stockQty;
              existing.purchase_qty = Math.max(0, existing.required_qty - existing.stock_qty);
            } else {
              materialMap.set(materialId, {
                material_number: material.material_number || '',
                description: material.description || '',
                drawing_number: material.drawing_number || '-',
                required_qty: requiredQty,
                stock_qty: stockQty,
                purchase_qty: purchaseQty
              });
            }

            // Check for sub-BOM
            const subBOM = await db.BOMHeader.findOne({
              where: {
                material_id: material.id,
                company_id: finalCompanyId,
                status: 'ACTIVE'
              },
              order: [['id', 'DESC']],
              limit: 1
            });

            if (subBOM) {
              await fetchComponentsRecursively(subBOM.id, requiredQty, processedMaterials, level + 1);
            }
          }
        }
      }
    };

    // Process each item in the Customer PO
    // If itemId is provided, only process that specific item
    const itemsToProcess = itemId 
      ? customerPO.items.filter(item => item.id === parseInt(itemId))
      : customerPO.items;

    if (itemsToProcess && itemsToProcess.length > 0) {
      for (const item of itemsToProcess) {
        if (!item.material_id) continue;
        const itemQuantity = parseFloat(item.quantity || 0);
        if (itemQuantity <= 0) continue;

        const mainBOMs = await db.BOMHeader.findAll({
          where: {
            material_id: item.material_id,
            company_id: finalCompanyId,
            status: 'ACTIVE'
          },
          order: [['id', 'DESC']],
          limit: 1
        });

        for (const bom of mainBOMs) {
          await fetchComponentsRecursively(bom.id, itemQuantity, new Set(), 0);
        }
      }
    }

    // Convert map to array and sort
    const bomList = Array.from(materialMap.values()).map(item => ({
      material_number: item.material_number,
      description: item.description,
      drawing_number: item.drawing_number,
      required_qty: Math.round(item.required_qty * 100) / 100,
      stock_qty: Math.round(item.stock_qty * 100) / 100,
      purchase_qty: Math.round(item.purchase_qty * 100) / 100
    }));

    bomList.sort((a, b) => {
      if (a.material_number < b.material_number) return -1;
      if (a.material_number > b.material_number) return 1;
      return 0;
    });

    // Create PDF document
    const doc = new PDFDocument({ margin: 50, size: 'A4' });
    
    // Set response headers
    res.setHeader('Content-Type', 'application/pdf');
    res.setHeader('Content-Disposition', `attachment; filename="BOM_Requirement_List_${customerPO.po_number || customerPOId}_${new Date().toISOString().split('T')[0]}.pdf"`);

    // Pipe PDF to response
    doc.pipe(res);

    // Constants
    const pageWidth = doc.page.width;
    const pageHeight = doc.page.height;
    const margin = 50;
    const contentWidth = pageWidth - (margin * 2);
    let pageNumber = 1;
    let yPosition = 50; // Start position

    // Helper function to add logo
    const addLogo = (logoPath, x, y, maxWidth = 100) => {
      if (logoPath && printHeader) {
        try {
          const logoFullPath = path.join(__dirname, '../uploads', logoPath);
          if (fs.existsSync(logoFullPath)) {
            doc.image(logoFullPath, x, y, { width: maxWidth, fit: [maxWidth, 100] });
            return true;
          }
        } catch (err) {
          console.error('Error loading logo:', err);
        }
      }
      return false;
    };

    // Function to add header on each page
    const addPageHeader = () => {
      let yPos = 50;
      
      // Logo (left side)
      addLogo(printHeader?.logo_path, margin, yPos, 80);

      // Company Name (centered)
      doc.fontSize(16).font('Helvetica-Bold');
      const companyName = printHeader?.company_name || company?.name || 'Company Name';
      doc.text(companyName, margin, yPos, { align: 'center', width: contentWidth });
      yPos += 20;

      if (printHeader?.former_name) {
        doc.fontSize(10).font('Helvetica');
        doc.text(`(Formerly Known as ${printHeader.former_name})`, margin, yPos, { align: 'center', width: contentWidth });
        yPos += 15;
      }

      // Address (centered)
      doc.fontSize(9).font('Helvetica');
      const address = printHeader?.address || company?.address || '';
      if (address) {
        const addressLines = address.split('\n').filter(line => line.trim());
        addressLines.forEach(line => {
          doc.text(line.trim(), margin, yPos, { align: 'center', width: contentWidth });
          yPos += 12;
        });
      }

      // Add GSTIN to address if available
      if (printHeader?.gstin) {
        doc.fontSize(9).font('Helvetica');
        doc.text(`GSTIN: ${printHeader.gstin}`, margin, yPos, { align: 'center', width: contentWidth });
        yPos += 12;
      }

      // Contact Information (centered)
      const contactInfo = [];
      if (printHeader?.phone) contactInfo.push(`Phone: ${printHeader.phone}`);
      if (printHeader?.fax) contactInfo.push(`Fax: ${printHeader.fax}`);
      if (printHeader?.email) contactInfo.push(`E-mail: ${printHeader.email}`);
      if (printHeader?.website) contactInfo.push(`Webpage: ${printHeader.website}`);

      if (contactInfo.length > 0) {
        yPos += 5;
        contactInfo.forEach(info => {
          doc.text(info, margin, yPos, { align: 'center', width: contentWidth });
          yPos += 12;
        });
      }

      // Draw line after header
      yPos += 10;
      doc.moveTo(margin, yPos).lineTo(pageWidth - margin, yPos).stroke();
      yPos += 20;

      // Title: BOM Requirement List
      doc.fontSize(14).font('Helvetica-Bold');
      doc.text('BOM REQUIREMENT LIST', margin, yPos, { align: 'center', width: contentWidth });
      yPos += 20;

      // Add Customer and Order Details on EVERY page
      yPos = addCustomerAndOrderDetailsToHeader(yPos);

      return yPos;
    };

    // Function to add customer and order details to page header (on every page)
    const addCustomerAndOrderDetailsToHeader = (yStart) => {
      let yPos = yStart;

      // Two-column layout: Customer details (left) and Order Items (right)
      const leftColumnX = margin;
      const rightColumnX = pageWidth / 2 + 20;
      const columnWidth = (pageWidth - margin * 2) / 2 - 20;
      let leftY = yPos;
      let rightY = yPos;
      
      // Left Column: Customer Information (compact)
      doc.fontSize(9).font('Helvetica');
      doc.text(`Customer: ${customerPO.customer?.name || '-'}`, leftColumnX, leftY);
      leftY += 12;
      
      if (customerPO.customer?.bp_number) {
        doc.text(`Code: ${customerPO.customer.bp_number}`, leftColumnX, leftY);
        leftY += 12;
      }
      
      if (customerPO.po_number) {
        doc.font('Helvetica-Bold');
        doc.text(`PO: ${customerPO.po_number}`, leftColumnX, leftY);
        doc.font('Helvetica');
        leftY += 12;
      }
      
      // Right Column: Order Items (compact - show only first item or summary)
      if (customerPO.items && customerPO.items.length > 0) {
        doc.fontSize(9).font('Helvetica-Bold');
        doc.text('Items:', rightColumnX, rightY);
        rightY += 12;
        
        doc.fontSize(9).font('Helvetica');
        
        const formatDate = (dateString) => {
          if (!dateString) return '-';
          try {
            const date = new Date(dateString);
            const day = String(date.getDate()).padStart(2, '0');
            const month = String(date.getMonth() + 1).padStart(2, '0');
            const year = date.getFullYear();
            return `${day}/${month}/${year}`;
          } catch (e) {
            return dateString;
          }
        };
        
        const firstItem = customerPO.items[0];
        if (firstItem && firstItem.material) {
          const code = firstItem.material.material_number || '-';
          const qty = Math.round(parseFloat(firstItem.quantity || 0)).toString();
          const orderDate = formatDate(customerPO.po_date);
          
          doc.text(`${code}`, rightColumnX, rightY);
          rightY += 12;
          doc.text(`Qty: ${qty} | Date: ${orderDate}`, rightColumnX, rightY);
          rightY += 12;
          
          if (customerPO.items.length > 1) {
            doc.text(`+ ${customerPO.items.length - 1} more item(s)`, rightColumnX, rightY);
            rightY += 12;
          }
        }
      }
      
      return Math.max(leftY, rightY) + 10;
    };

    // Function to add customer and order details (only on first page - DEPRECATED, now in header)
    // Keeping for backward compatibility but not using it
    const addCustomerAndOrderDetails = (yStart) => {
      // This function is now integrated into addPageHeader
      // Return yStart unchanged to maintain compatibility
      return yStart;
    };

    // Function to add table headers
    const addTableHeaders = (yStart) => {
      let yPos = yStart;
      doc.fontSize(9).font('Helvetica-Bold');
      
      // Calculate available width (from left margin to right margin)
      const availableWidth = pageWidth - (margin * 2);
      
      // Define column widths that fit exactly within the available space
      // Distribute space proportionally: S.No. (small), Part No., Description (largest), Drawing No., Qty columns (compact)
      const colWidths = {
        sno: Math.floor(availableWidth * 0.06),           // ~6% - S.No. column
        partNo: Math.floor(availableWidth * 0.20),         // ~20% - Part No. column
        description: Math.floor(availableWidth * 0.35),   // ~35% - Description column (largest)
        drawingNo: Math.floor(availableWidth * 0.18),     // ~18% - Drawing No. column
        requiredQty: Math.floor(availableWidth * 0.10),  // ~10% - Req.Qty column
        stockQty: Math.floor(availableWidth * 0.06),      // ~6% - Stock Qty column
        purchaseQty: 0  // Will be calculated to fill remaining space
      };
      
      // Calculate purchaseQty to fill remaining space exactly
      const usedWidth = colWidths.sno + colWidths.partNo + colWidths.description + 
                        colWidths.drawingNo + colWidths.requiredQty + colWidths.stockQty;
      colWidths.purchaseQty = availableWidth - usedWidth;
      
      console.log(`[PDF] Column widths calculated - Available: ${availableWidth}px, Used: ${usedWidth + colWidths.purchaseQty}px`);

      // Set font for headers - bold and clear for better visibility
      doc.fontSize(10).font('Helvetica-Bold'); // Increased from 9 to 10 for better clarity
      
      let xPos = margin;
      const headerStartY = yPos;
      
      // Add padding above headers for better spacing
      yPos += 5; // Add space before headers
      
      // Print column headers with proper spacing and clear alignment
      doc.text('S.No.', xPos, yPos);
      xPos += colWidths.sno;
      doc.text('Part No.', xPos, yPos, { width: colWidths.partNo });
      xPos += colWidths.partNo;
      doc.text('Description', xPos, yPos, { width: colWidths.description });
      xPos += colWidths.description;
      doc.text('Drawing No.', xPos, yPos, { width: colWidths.drawingNo });
      xPos += colWidths.drawingNo;
      doc.text('Req.Qty', xPos, yPos, { width: colWidths.requiredQty, align: 'right' });
      xPos += colWidths.requiredQty;
      doc.text('Stock Qty', xPos, yPos, { width: colWidths.stockQty, align: 'right' });
      xPos += colWidths.stockQty;
      doc.text('Pur.Qty', xPos, yPos, { width: colWidths.purchaseQty, align: 'right' });

      // Increase header height - add more space after headers before the line
      yPos += 20; // Increased to 20 for better header height and clarity
      
      // Draw header line - extend to right margin to match left margin
      const rightMargin = pageWidth - margin; // Right margin equals left margin
      doc.lineWidth(1.5); // Slightly thicker line for better visibility
      doc.moveTo(margin, yPos).lineTo(rightMargin, yPos).stroke();
      doc.lineWidth(1); // Reset line width
      
      // Add more space after the line for better separation from data
      yPos += 12; // Increased from 10 to 12 for better spacing
      
      // Reset font for data rows - smaller and regular weight
      doc.fontSize(8).font('Helvetica');

      return { yPos, colWidths };
    };

    // Function to add page number and footer on current page
    // CRITICAL: This adds footer AFTER last items WITHOUT creating new pages
    const addPageFooter = (lastItemYPosition, colWidths) => {
      try {
        // Store current position
        const savedY = doc.y;
        const savedX = doc.x;
        
        // Calculate footer position: after last item with some spacing
        const lineSpacing = 10; // Space after last item before line
        const lineY = (lastItemYPosition || savedY) + lineSpacing;
        const lineThickness = 1.5; // Line thickness/height
        const pageNumberSpacing = 5; // Space between line and page number
        const pageNumberY = lineY + lineThickness + pageNumberSpacing;
        
        // Calculate line to match header line - extend to right margin to match left margin
        // Right margin equals left margin for consistent margins on all pages
        const rightMargin = pageWidth - margin;
        const lineLength = rightMargin - margin;
        
        // Draw a horizontal line with thickness (little bit of height)
        // Line extends from left margin to right margin - matches header line exactly
        doc.lineWidth(lineThickness);
        doc.moveTo(margin, lineY).lineTo(rightMargin, lineY).stroke();
        doc.lineWidth(1); // Reset line width to default
        
        console.log(`[PDF] Footer line drawn: from ${margin} to ${rightMargin}, length: ${lineLength}px (matches header line and margins)`);
        
        // Add page number text BELOW the line, right-aligned
        doc.fontSize(8).font('Helvetica');
        doc.text(`Page ${pageNumber}`, margin, pageNumberY, {
          align: 'right',
          width: contentWidth
        });
        
        // Restore position
        doc.y = savedY;
        doc.x = savedX;
        
        console.log(`[PDF] ✓ Added footer line at Y=${lineY} (length: ${lastColumnEnd - margin}px) and 'Page ${pageNumber}' at Y=${pageNumberY} on page ${pageNumber}`);
      } catch (err) {
        console.error(`[PDF] ✗ Error adding footer to page ${pageNumber}:`, err);
      }
    };

    // ========== START PDF GENERATION ==========
    
    // Constants
    const rowHeight = 22; // Increased for better spacing
    const itemsPerPage = 20; // Limit items per page
    const bottomMargin = 80; // Reduced margin - footer at 30px from bottom, so 80px is safe
    const tableHeaderHeight = 20; // Text (12px) + line spacing (8px)
    const safeMargin = 30; // Reduced safety margin
    
    // First Page: Header Section (includes customer details now)
    yPosition = addPageHeader();

    // Add Table Headers - NO PAGE BREAK CHECKS, just add them
    const { yPos: tableYPos, colWidths } = addTableHeaders(yPosition);
    yPosition = tableYPos;
    
    console.log(`[PDF] After table headers - yPosition: ${yPosition}, Page: ${pageNumber}`);
    
    // Note: We'll add footer to first page after items are rendered or when we create a new page

    // ========== RENDER ITEMS IMMEDIATELY ==========
    // CRITICAL: DO NOT add footer before items - it might cause page break
    // Render items IMMEDIATELY after headers
    // Font size already set in addTableHeaders function (8px for data rows)
    doc.fontSize(8).font('Helvetica');
    
    const formatQty = (qty) => {
      return Math.round(parseFloat(qty || 0)).toString();
    };

    // Render items - FIRST ITEM MUST BE ON SAME PAGE AS HEADERS
    // Keep colWidths available for footer on all pages
    let currentColWidths = colWidths;
    
    if (bomList.length === 0) {
      doc.text('No BOM components found.', margin, yPosition);
      // Add footer to first page after the message
      addPageFooter(yPosition, currentColWidths);
    } else {
      // Track items per page
      let itemsOnCurrentPage = 0;
      let currentPageStartIndex = 0; // Track which item started the current page
      
      // CRITICAL: Render ALL items starting immediately
      // The first item (index 0) MUST render on same page as headers - NO CHECKS
      bomList.forEach((item, index) => {

        // CRITICAL: Render item row - NO CHECKS FOR FIRST ITEM
        // The first item (index 0) MUST render on same page as headers
        let xPos = margin;
        
        console.log(`[PDF] Rendering item ${index + 1} at yPosition: ${yPosition} on page ${pageNumber}`);
        console.log(`[PDF] Material: ${item.material_number}, Items on page: ${itemsOnCurrentPage + 1}/${itemsPerPage}`);
        
        // CRITICAL: Render all columns for this row IMMEDIATELY
        // No delays, no checks - just render
        try {
          // S.No. (global index + 1)
          doc.text((index + 1).toString(), xPos, yPosition);
          xPos += colWidths.sno;
          
          // Part No.
          doc.text(item.material_number || '-', xPos, yPosition, { width: colWidths.partNo });
          xPos += colWidths.partNo;
          
          // Description
          doc.text(item.description || '-', xPos, yPosition, { width: colWidths.description });
          xPos += colWidths.description;
          
          // Drawing No.
          doc.text(item.drawing_number || '-', xPos, yPosition, { width: colWidths.drawingNo });
          xPos += colWidths.drawingNo;
          
          // Required Qty
          doc.text(formatQty(item.required_qty), xPos, yPosition, { width: colWidths.requiredQty, align: 'right' });
          xPos += colWidths.requiredQty;
          
          // Stock Qty
          doc.text(formatQty(item.stock_qty), xPos, yPosition, { width: colWidths.stockQty, align: 'right' });
          xPos += colWidths.stockQty;
          
          // Purchase Qty (bold)
          doc.font('Helvetica-Bold');
          doc.text(formatQty(item.purchase_qty), xPos, yPosition, { width: colWidths.purchaseQty, align: 'right' });
          doc.font('Helvetica');
          
          // Move to next row with professional spacing
          yPosition += rowHeight;
          itemsOnCurrentPage++;
          
          console.log(`[PDF] Item ${index + 1} rendered successfully, new yPosition: ${yPosition}, Items on page: ${itemsOnCurrentPage}/${itemsPerPage}`);
          
          // Check if we need a new page AFTER rendering this item
          if (index < bomList.length - 1) { // Not the last item
            const pageHeightAfter = doc.page.height;
            const safeBottomAfter = pageHeightAfter - bottomMargin;
            const hasMoreItems = index < bomList.length - 1;
            const needsNewPage = hasMoreItems && (
              itemsOnCurrentPage >= itemsPerPage || 
              yPosition + rowHeight > safeBottomAfter
            );
            
            if (needsNewPage) {
              // Add footer to current page AFTER last item, BEFORE creating new page
              // Pass the current yPosition (which is after the last item) and colWidths
              addPageFooter(yPosition, currentColWidths);
              
              console.log(`[PDF] Creating new page after item ${index + 1}, Items on page: ${itemsOnCurrentPage}, yPosition: ${yPosition}`);
              
              // Create new page
              doc.addPage();
              pageNumber++;
              yPosition = addPageHeader();
              const { yPos: newTableYPos, colWidths: newColWidths } = addTableHeaders(yPosition);
              // Update colWidths for the new page
              currentColWidths = newColWidths;
              yPosition = newTableYPos;
              itemsOnCurrentPage = 0; // Reset counter for new page
              currentPageStartIndex = index + 1; // Track new page start
            }
          }
        } catch (err) {
          console.error(`[PDF] Error rendering item ${index + 1}:`, err);
        }
      });
      
      // CRITICAL: Add footer to the last page AFTER all items are rendered
      // This ensures the last page ALWAYS has a footer
      // ALWAYS add footer regardless of itemsOnCurrentPage value
      addPageFooter(yPosition, currentColWidths);
      console.log(`[PDF] Added footer to last page ${pageNumber} after ${itemsOnCurrentPage} items`);
    }

    console.log(`[PDF] Finished rendering. Final yPosition: ${yPosition}, Total pages: ${pageNumber}`);
    console.log(`[PDF] All footers should now be added to all pages`);

    // Finalize PDF
    doc.end();
    
  } catch (error) {
    console.error('Error generating BOM Requirement List PDF:', error);
    if (!res.headersSent) {
      return sendError(res, 'Failed to generate PDF', 500);
    }
  }
});
