
import { Request, Response } from 'express';
import { Report } from '../models/Report';
import { User } from '../models/User';
import { findProjectByIdOrCode } from '../utils/projectHelper';
import mongoose from 'mongoose';
import { ReportDataService } from '../services/reportDataService';
import { PDFGenerator } from '../services/pdfGenerator';

export const getReports = async (req: Request, res: Response) => {
    try {
        const { projectId } = req.query;
        let query: any = {};

        // Handle projectId filter if provided
        if (projectId) {
            let projectObjectId: any;

            if (mongoose.Types.ObjectId.isValid(projectId as string)) {
                projectObjectId = new mongoose.Types.ObjectId(projectId as string);
                query.projectId = projectObjectId;
            } else {
                const project = await findProjectByIdOrCode(projectId as string);
                if (project) {
                    query.projectId = project._id;
                } else {
                    // If project logic fails for string ID, we might try querying by string (legacy)
                    // or just return empty if strictly valid project is required.
                    // However, we'll try the native query approach if we can't resolve it, 
                    // OR if resolved, we use the resolved ID.
                    // The native query strictly matches what is in DB.
                    // If we want to support 's1' in DB directly (unlikely for ObjectId field),
                    // we stick to the resolved _id.

                    // But wait, the previous pattern was:
                    // If string AND not ObjectId -> use native collection find with string ID
                    // This assumes the DB *might* have string IDs in projectId field (demo data).

                    // Let's replicate the pattern from other controllers:
                    projectObjectId = projectId;
                }
            }

            // If projectObjectId is a string (and not a valid ObjectId), use native query
            if (typeof projectObjectId === 'string' && !mongoose.Types.ObjectId.isValid(projectObjectId)) {
                if (!mongoose.connection.db) {
                    throw new Error('Database connection not established');
                }
                const reportsCollection = mongoose.connection.db.collection('reports');
                const reports = await reportsCollection.find({ projectId: projectObjectId }).sort({ generatedDate: -1 }).toArray();
                return res.json({ success: true, data: reports });
            }

            // If we resolved it to an ObjectId (or it was one), update query
            // Also query for string version to handle legacy data
            if (projectObjectId instanceof mongoose.Types.ObjectId) {
                query.$or = [
                    { projectId: projectObjectId },
                    { projectId: projectObjectId.toString() }
                ];
            } else if (typeof projectObjectId === 'string') {
                // If it's a string ObjectId, query for both ObjectId and string
                if (mongoose.Types.ObjectId.isValid(projectObjectId)) {
                    query.$or = [
                        { projectId: new mongoose.Types.ObjectId(projectObjectId) },
                        { projectId: projectObjectId }
                    ];
                } else {
                    query.projectId = projectObjectId;
                }
            }
        }

        const reports = await Report.find(query).sort({ generatedDate: -1 }).lean();
        
        // Populate user information for createdBy field
        // Since createdBy is Mixed type, we need to manually fetch user data
        const reportsWithUsers = await Promise.all(
            reports.map(async (report: any) => {
                if (report.createdBy) {
                    try {
                        let user: any = null;
                        // Try to find user by ID (could be ObjectId or string)
                        if (mongoose.Types.ObjectId.isValid(report.createdBy)) {
                            user = await User.findById(report.createdBy).select('name email').lean();
                        } else if (typeof report.createdBy === 'string') {
                            // Try native collection query for string ID
                            if (mongoose.connection.db) {
                                const usersCollection = mongoose.connection.db.collection('users');
                                user = await usersCollection.findOne(
                                    { _id: report.createdBy },
                                    { projection: { name: 1, email: 1 } }
                                );
                            }
                        } else if (report.createdBy._id) {
                            // If createdBy is already an object with _id
                            const userId = report.createdBy._id;
                            if (mongoose.Types.ObjectId.isValid(userId)) {
                                user = await User.findById(userId).select('name email').lean();
                            }
                        }
                        
                        // Add user name/email to report
                        if (user) {
                            report.createdBy = user.name || user.email || report.createdBy;
                        }
                    } catch (err) {
                        // If user lookup fails, keep original createdBy value
                        console.error('Error fetching user for report:', err);
                    }
                }
                return report;
            })
        );
        
        res.json({ success: true, data: reportsWithUsers });
    } catch (error: any) {
        res.status(500).json({ success: false, message: error.message });
    }
};

export const generateReport = async (req: Request, res: Response) => {
    try {
        const { type, title, projectId, screenshot, financialData } = req.body;
        const userId = (req as any).user?._id;

        if (!type || !title) {
            return res.status(400).json({ success: false, message: 'Type and title are required' });
        }

        // Convert projectId to ObjectId if it's a valid ObjectId string
        // This ensures consistency with how getReports queries the data
        let projectIdToSave: any = projectId;
        if (projectId && mongoose.Types.ObjectId.isValid(projectId)) {
            projectIdToSave = new mongoose.Types.ObjectId(projectId);
        }

        const newReport = new Report({
            title,
            type,
            createdBy: userId, // Assuming user is populated in auth middleware
            projectId: projectIdToSave,
            status: 'Processing',
            generatedDate: new Date()
        });

        await newReport.save();

        // Generate report with actual data from MongoDB
        setTimeout(async () => {
            try {
                let reportData: any;
                const reportDate = new Date();
                
                // Get data based on report type
                switch (type) {
                    case 'Daily':
                        reportData = await ReportDataService.getDailyReportData(projectId, reportDate);
                        break;
                    case 'Monthly':
                        const monthStr = `${reportDate.getFullYear()}-${String(reportDate.getMonth() + 1).padStart(2, '0')}`;
                        reportData = await ReportDataService.getMonthlyReportData(projectId, monthStr);
                        break;
                    case 'Financial':
                        // Get last 12 months
                        const endMonth = `${reportDate.getFullYear()}-${String(reportDate.getMonth() + 1).padStart(2, '0')}`;
                        const startDate = new Date(reportDate);
                        startDate.setMonth(startDate.getMonth() - 11);
                        const startMonth = `${startDate.getFullYear()}-${String(startDate.getMonth() + 1).padStart(2, '0')}`;
                        reportData = await ReportDataService.getFinancialReportData(projectId, startMonth, endMonth);
                        
                        // If financialData is provided from frontend, merge it with backend data
                        // This allows frontend to send current view data for accurate PDF rendering
                        if (financialData && financialData.financialData && financialData.costBreakdown) {
                            // Merge frontend data (more up-to-date) with backend structure
                            reportData.financials = financialData.financialData.map((f: any) => ({
                                month: f.month,
                                revenue: f.revenue || 0,
                                opex: f.opex || 0,
                                netProfit: f.netProfit || 0,
                                cumulativeROI: f.cumulativeROI || 0
                            }));
                            reportData.costBreakdown = financialData.costBreakdown || [];
                            if (financialData.totals) {
                                reportData.summary.totalRevenue = financialData.totals.totalRevenue || reportData.summary.totalRevenue;
                                reportData.summary.totalOpex = financialData.totals.totalOpex || reportData.summary.totalOpex;
                                reportData.summary.totalNetProfit = financialData.totals.totalProfit || reportData.summary.totalNetProfit;
                                reportData.summary.currentROI = financialData.totals.currentROI || 0;
                                reportData.summary.profitMargin = financialData.totals.profitMargin || 0;
                            }
                        }
                        
                        // Deprecated: screenshot support (keeping for backward compatibility)
                        if (screenshot) {
                            reportData.screenshot = screenshot;
                        }
                        break;
                    case 'Audit':
                        const endDate = new Date(reportDate);
                        const startAuditDate = new Date(reportDate);
                        startAuditDate.setMonth(startAuditDate.getMonth() - 1); // Last month
                        reportData = await ReportDataService.getAuditReportData(projectId, startAuditDate, endDate);
                        break;
                    default:
                        reportData = { title: title || 'Báo cáo' };
                }
                
                // Generate PDF from report data
                const pdfBase64 = await PDFGenerator.generatePDF(type, reportData, title);
                
                // Validate PDF before saving
                try {
                    const pdfBuffer = Buffer.from(pdfBase64, 'base64');
                    if (pdfBuffer.length < 4 || pdfBuffer.toString('ascii', 0, 4) !== '%PDF') {
                        throw new Error('Generated PDF is invalid - missing PDF header');
                    }
                    
                    // Check for EOF marker
                    const pdfString = pdfBuffer.toString('ascii');
                    if (!pdfString.includes('%%EOF')) {
                        throw new Error('Generated PDF is invalid - missing EOF marker');
                    }
                    
                    newReport.status = 'Ready';
                    newReport.downloadUrl = `data:application/pdf;base64,${pdfBase64}`;
                    newReport.size = `${Math.round(pdfBuffer.length / 1024)} KB`;
                    await newReport.save();
                } catch (pdfError: any) {
                    console.error('PDF validation error:', pdfError);
                    throw pdfError;
                }
            } catch (err: any) {
                console.error('Error generating report:', err);
                try {
                    newReport.status = 'Failed';
                    await newReport.save();
                } catch (saveErr) {
                    console.error('Error saving failed status:', saveErr);
                }
            }
        }, 2000);

        res.json({ success: true, message: 'Report generation started', data: newReport });
    } catch (error: any) {
        res.status(500).json({ success: false, message: error.message });
    }
};

export const downloadReport = async (req: Request, res: Response) => {
    try {
        const { id } = req.params;
        const report = await Report.findById(id);

        if (!report) {
            return res.status(404).json({ success: false, message: 'Report not found' });
        }

        if (report.status !== 'Ready') {
            return res.status(400).json({ 
                success: false, 
                message: `Report is not ready. Current status: ${report.status}` 
            });
        }

        if (!report.downloadUrl || report.downloadUrl === '#') {
            return res.status(404).json({ 
                success: false, 
                message: 'Download URL not available for this report' 
            });
        }

        // If downloadUrl is a data URL, extract and send the PDF
        if (report.downloadUrl.startsWith('data:')) {
            const matches = report.downloadUrl.match(/^data:([^;]+);base64,(.+)$/);
            if (matches && matches.length >= 3) {
                const base64Data = matches[2];
                
                // Clean base64 string (remove whitespace, handle URL-safe)
                let cleanedBase64 = base64Data.replace(/[\s\n\r\t]/g, '');
                cleanedBase64 = cleanedBase64.replace(/-/g, '+').replace(/_/g, '/');
                
                // Add padding if needed
                const padding = cleanedBase64.length % 4;
                if (padding !== 0) {
                    cleanedBase64 += '='.repeat(4 - padding);
                }
                
                const pdfBuffer = Buffer.from(cleanedBase64, 'base64');
                
                // Validate PDF header
                if (pdfBuffer.length < 4 || pdfBuffer.toString('ascii', 0, 4) !== '%PDF') {
                    console.error('Invalid PDF header in download endpoint:', {
                        bufferLength: pdfBuffer.length,
                        firstBytes: pdfBuffer.toString('ascii', 0, 10)
                    });
                    return res.status(500).json({ 
                        success: false, 
                        message: 'Invalid PDF data - header check failed' 
                    });
                }

                // Validate PDF has EOF marker
                const pdfString = pdfBuffer.toString('ascii');
                if (!pdfString.includes('%%EOF')) {
                    console.warn('PDF missing EOF marker, but sending anyway');
                }

                // Set proper headers for PDF download
                // HTTP headers MUST be ASCII-only. Unicode characters must be encoded using RFC 5987
                
                // Convert Vietnamese/Unicode to ASCII-safe filename
                const transliterateToASCII = (text: string): string => {
                    if (!text || typeof text !== 'string') {
                        return 'report';
                    }
                    
                    // Vietnamese character mapping to ASCII
                    const vietnameseMap: { [key: string]: string } = {
                        'à': 'a', 'á': 'a', 'ạ': 'a', 'ả': 'a', 'ã': 'a',
                        'â': 'a', 'ầ': 'a', 'ấ': 'a', 'ậ': 'a', 'ẩ': 'a', 'ẫ': 'a',
                        'ă': 'a', 'ằ': 'a', 'ắ': 'a', 'ặ': 'a', 'ẳ': 'a', 'ẵ': 'a',
                        'è': 'e', 'é': 'e', 'ẹ': 'e', 'ẻ': 'e', 'ẽ': 'e',
                        'ê': 'e', 'ề': 'e', 'ế': 'e', 'ệ': 'e', 'ể': 'e', 'ễ': 'e',
                        'ì': 'i', 'í': 'i', 'ị': 'i', 'ỉ': 'i', 'ĩ': 'i',
                        'ò': 'o', 'ó': 'o', 'ọ': 'o', 'ỏ': 'o', 'õ': 'o',
                        'ô': 'o', 'ồ': 'o', 'ố': 'o', 'ộ': 'o', 'ổ': 'o', 'ỗ': 'o',
                        'ơ': 'o', 'ờ': 'o', 'ớ': 'o', 'ợ': 'o', 'ở': 'o', 'ỡ': 'o',
                        'ù': 'u', 'ú': 'u', 'ụ': 'u', 'ủ': 'u', 'ũ': 'u',
                        'ư': 'u', 'ừ': 'u', 'ứ': 'u', 'ự': 'u', 'ử': 'u', 'ữ': 'u',
                        'ỳ': 'y', 'ý': 'y', 'ỵ': 'y', 'ỷ': 'y', 'ỹ': 'y',
                        'đ': 'd',
                        'À': 'A', 'Á': 'A', 'Ạ': 'A', 'Ả': 'A', 'Ã': 'A',
                        'Â': 'A', 'Ầ': 'A', 'Ấ': 'A', 'Ậ': 'A', 'Ẩ': 'A', 'Ẫ': 'A',
                        'Ă': 'A', 'Ằ': 'A', 'Ắ': 'A', 'Ặ': 'A', 'Ẳ': 'A', 'Ẵ': 'A',
                        'È': 'E', 'É': 'E', 'Ẹ': 'E', 'Ẻ': 'E', 'Ẽ': 'E',
                        'Ê': 'E', 'Ề': 'E', 'Ế': 'E', 'Ệ': 'E', 'Ể': 'E', 'Ễ': 'E',
                        'Ì': 'I', 'Í': 'I', 'Ị': 'I', 'Ỉ': 'I', 'Ĩ': 'I',
                        'Ò': 'O', 'Ó': 'O', 'Ọ': 'O', 'Ỏ': 'O', 'Õ': 'O',
                        'Ô': 'O', 'Ồ': 'O', 'Ố': 'O', 'Ộ': 'O', 'Ổ': 'O', 'Ỗ': 'O',
                        'Ơ': 'O', 'Ờ': 'O', 'Ớ': 'O', 'Ợ': 'O', 'Ở': 'O', 'Ỡ': 'O',
                        'Ù': 'U', 'Ú': 'U', 'Ụ': 'U', 'Ủ': 'U', 'Ũ': 'U',
                        'Ư': 'U', 'Ừ': 'U', 'Ứ': 'U', 'Ự': 'U', 'Ử': 'U', 'Ữ': 'U',
                        'Ỳ': 'Y', 'Ý': 'Y', 'Ỵ': 'Y', 'Ỷ': 'Y', 'Ỹ': 'Y',
                        'Đ': 'D'
                    };
                    
                    let result = '';
                    for (let i = 0; i < text.length; i++) {
                        const char = text[i];
                        result += vietnameseMap[char] || char;
                    }
                    
                    return result;
                };
                
                // Sanitize filename: convert to ASCII and remove invalid chars
                const sanitizeFilename = (name: string): string => {
                    if (!name || typeof name !== 'string') {
                        return 'report';
                    }
                    
                    // First transliterate Vietnamese to ASCII
                    let sanitized = transliterateToASCII(name);
                    
                    // Remove all control characters and problematic chars
                    sanitized = sanitized
                        .replace(/[\x00-\x1F\x7F-\x9F]/g, '') // Remove all control characters
                        .replace(/[\r\n\t]/g, '') // Remove line breaks and tabs
                        .replace(/[<>:"/\\|?*]/g, '_') // Replace invalid filename chars
                        .replace(/["\\]/g, '_') // Replace quotes and backslashes
                        .replace(/;/g, '_') // Replace semicolons
                        .replace(/[^\x20-\x7E]/g, '_') // Replace any remaining non-ASCII
                        .replace(/\s+/g, '_') // Replace spaces with underscores
                        .replace(/_+/g, '_') // Replace multiple underscores with single
                        .replace(/^_+|_+$/g, '') // Remove leading/trailing underscores
                        .trim();
                    
                    // Limit length and ensure not empty
                    sanitized = sanitized.substring(0, 100) || 'report';
                    
                    // Final check: ensure only ASCII printable characters
                    if (!/^[\x20-\x7E]+$/.test(sanitized)) {
                        sanitized = 'report';
                    }
                    
                    return sanitized;
                };
                
                const safeFilename = sanitizeFilename(report.title);
                
                // Build Content-Disposition header with ASCII-only filename
                // Use quoted format for safety
                const contentDispositionValue = `attachment; filename="${safeFilename}.pdf"`;
                
                // Set headers - should be safe now as it's ASCII-only
                res.setHeader('Content-Type', 'application/pdf');
                res.setHeader('Content-Disposition', contentDispositionValue);
                
                res.setHeader('Content-Length', pdfBuffer.length.toString());
                res.setHeader('Cache-Control', 'no-cache');
                
                return res.send(pdfBuffer);
            }
        }

        // If it's a regular URL, redirect or fetch
        if (report.downloadUrl.startsWith('http://') || report.downloadUrl.startsWith('https://')) {
            return res.redirect(report.downloadUrl);
        }

        return res.status(400).json({ 
            success: false, 
            message: 'Invalid download URL format' 
        });
    } catch (error: any) {
        console.error('Error downloading report:', error);
        res.status(500).json({ success: false, message: error.message });
    }
};

export const deleteReport = async (req: Request, res: Response) => {
    try {
        const { id } = req.params;
        const deletedReport = await Report.findByIdAndDelete(id);

        if (!deletedReport) {
            return res.status(404).json({ success: false, message: 'Report not found' });
        }

        res.json({ success: true, message: 'Report deleted successfully' });
    } catch (error: any) {
        res.status(500).json({ success: false, message: error.message });
    }
};
