/**
 * Screenshot Manager for HVAC Testing Framework
 * 
 * Provides comprehensive screenshot functionality:
 * - Organized screenshot storage with naming conventions
 * - Failure capture with context information
 * - Full page and element-specific screenshots
 * - Screenshot comparison utilities
 * 
 * @package HVAC_Community_Events
 * @version 2.0.0
 * @created 2025-08-27
 */

const fs = require('fs').promises;
const path = require('path');
const ConfigManager = require('../core/ConfigManager');

class ScreenshotManager {
    constructor() {
        this.config = ConfigManager;
        this.baseDir = this.config.get('media.screenshotDir', './test-results/screenshots');
        this.quality = this.config.get('media.quality', 80);
        this.fullPage = this.config.get('media.fullPage', false);
        this.ensureDirectoryExists();
    }
    
    /**
     * Ensure screenshots directory exists
     */
    async ensureDirectoryExists() {
        try {
            await fs.mkdir(this.baseDir, { recursive: true });
        } catch (error) {
            console.warn('Could not create screenshots directory:', error.message);
        }
    }
    
    /**
     * Capture screenshot with context and naming
     */
    async capture(page, name, basePath = null, options = {}) {
        try {
            const screenshotPath = await this.generatePath(name, basePath, options);
            
            const screenshotOptions = {
                path: screenshotPath,
                quality: options.quality || this.quality,
                fullPage: options.fullPage !== undefined ? options.fullPage : this.fullPage,
                clip: options.clip,
                timeout: options.timeout || 5000,
                ...options
            };
            
            // Remove custom options that aren't part of Playwright's screenshot API
            delete screenshotOptions.category;
            delete screenshotOptions.testId;
            delete screenshotOptions.metadata;
            
            await page.screenshot(screenshotOptions);
            
            console.log(`📸 Screenshot captured: ${screenshotPath}`);
            
            // Save metadata if provided
            if (options.metadata) {
                await this.saveMetadata(screenshotPath, options.metadata);
            }
            
            return screenshotPath;
            
        } catch (error) {
            console.error('❌ Screenshot capture failed:', error.message);
            throw error;
        }
    }
    
    /**
     * Capture failure screenshot with additional context
     */
    async captureFailure(page, testInfo, error, options = {}) {
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        const testName = this.sanitizeFileName(testInfo.title);
        const name = `failure-${testName}-${timestamp}`;
        
        const metadata = {
            testTitle: testInfo.title,
            testFile: testInfo.file,
            error: error.message,
            url: page.url(),
            timestamp: new Date().toISOString(),
            viewport: await page.viewportSize(),
            userAgent: await page.evaluate(() => navigator.userAgent),
            ...options.metadata
        };
        
        try {
            const screenshotPath = await this.capture(page, name, null, {
                fullPage: true,
                metadata,
                category: 'failures',
                ...options
            });
            
            // Also capture console logs
            await this.captureConsoleLogs(page, screenshotPath, testInfo);
            
            // Capture page source
            await this.capturePageSource(page, screenshotPath);
            
            return screenshotPath;
            
        } catch (captureError) {
            console.error('Failed to capture failure screenshot:', captureError.message);
            return null;
        }
    }
    
    /**
     * Capture element-specific screenshot
     */
    async captureElement(page, selector, name, options = {}) {
        try {
            const element = await page.locator(selector);
            await element.waitFor({ state: 'visible', timeout: 5000 });
            
            const boundingBox = await element.boundingBox();
            if (!boundingBox) {
                throw new Error(`Element not visible or has no bounding box: ${selector}`);
            }
            
            return await this.capture(page, name, null, {
                clip: boundingBox,
                ...options
            });
            
        } catch (error) {
            console.error(`Failed to capture element screenshot (${selector}):`, error.message);
            throw error;
        }
    }
    
    /**
     * Capture comparison screenshots (before/after)
     */
    async captureComparison(page, name, action, options = {}) {
        const beforePath = await this.capture(page, `${name}-before`, null, options);
        
        // Perform the action
        if (typeof action === 'function') {
            await action();
        }
        
        const afterPath = await this.capture(page, `${name}-after`, null, options);
        
        return { before: beforePath, after: afterPath };
    }
    
    /**
     * Capture mobile/desktop responsive comparison
     */
    async captureResponsive(page, name, options = {}) {
        const originalViewport = page.viewportSize();
        
        // Desktop screenshot
        const desktopViewport = this.config.get('browser.viewport');
        await page.setViewportSize(desktopViewport);
        const desktopPath = await this.capture(page, `${name}-desktop`, null, options);
        
        // Mobile screenshot
        const mobileViewport = this.config.get('browser.mobileViewport');
        await page.setViewportSize(mobileViewport);
        const mobilePath = await this.capture(page, `${name}-mobile`, null, options);
        
        // Restore original viewport
        if (originalViewport) {
            await page.setViewportSize(originalViewport);
        }
        
        return { desktop: desktopPath, mobile: mobilePath };
    }
    
    /**
     * Generate screenshot file path
     */
    async generatePath(name, basePath = null, options = {}) {
        const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
        const sanitizedName = this.sanitizeFileName(name);
        
        let directory = basePath || this.baseDir;
        
        // Create category subdirectory if specified
        if (options.category) {
            directory = path.join(directory, options.category);
            await fs.mkdir(directory, { recursive: true });
        }
        
        // Create test-specific subdirectory if testId provided
        if (options.testId) {
            directory = path.join(directory, this.sanitizeFileName(options.testId));
            await fs.mkdir(directory, { recursive: true });
        }
        
        const filename = `${sanitizedName}-${timestamp}.png`;
        return path.join(directory, filename);
    }
    
    /**
     * Save screenshot metadata
     */
    async saveMetadata(screenshotPath, metadata) {
        try {
            const metadataPath = screenshotPath.replace('.png', '.json');
            await fs.writeFile(metadataPath, JSON.stringify(metadata, null, 2));
        } catch (error) {
            console.warn('Could not save screenshot metadata:', error.message);
        }
    }
    
    /**
     * Capture console logs alongside screenshot
     */
    async captureConsoleLogs(page, screenshotPath, testInfo) {
        try {
            const logs = [];
            
            // Get console messages from page context
            page.on('console', msg => {
                logs.push({
                    type: msg.type(),
                    text: msg.text(),
                    location: msg.location(),
                    timestamp: new Date().toISOString()
                });
            });
            
            // Save console logs
            const logsPath = screenshotPath.replace('.png', '-console.json');
            const logData = {
                testTitle: testInfo.title,
                url: page.url(),
                timestamp: new Date().toISOString(),
                logs
            };
            
            await fs.writeFile(logsPath, JSON.stringify(logData, null, 2));
            
        } catch (error) {
            console.warn('Could not capture console logs:', error.message);
        }
    }
    
    /**
     * Capture page source alongside screenshot
     */
    async capturePageSource(page, screenshotPath) {
        try {
            const pageSource = await page.content();
            const sourcePath = screenshotPath.replace('.png', '-source.html');
            await fs.writeFile(sourcePath, pageSource);
        } catch (error) {
            console.warn('Could not capture page source:', error.message);
        }
    }
    
    /**
     * Sanitize filename for cross-platform compatibility
     */
    sanitizeFileName(name) {
        return name
            .replace(/[^a-zA-Z0-9\-_.]/g, '-')
            .replace(/-+/g, '-')
            .replace(/^-|-$/g, '')
            .toLowerCase();
    }
    
    /**
     * Clean up old screenshots (keep last N days)
     */
    async cleanup(daysToKeep = 7) {
        try {
            const cutoffDate = new Date();
            cutoffDate.setDate(cutoffDate.getDate() - daysToKeep);
            
            const files = await this.getScreenshotFiles(this.baseDir);
            let deletedCount = 0;
            
            for (const file of files) {
                const stats = await fs.stat(file);
                if (stats.mtime < cutoffDate) {
                    await fs.unlink(file);
                    deletedCount++;
                }
            }
            
            console.log(`🧹 Cleaned up ${deletedCount} old screenshots`);
            
        } catch (error) {
            console.warn('Could not clean up screenshots:', error.message);
        }
    }
    
    /**
     * Get all screenshot files recursively
     */
    async getScreenshotFiles(directory) {
        const files = [];
        
        try {
            const entries = await fs.readdir(directory, { withFileTypes: true });
            
            for (const entry of entries) {
                const fullPath = path.join(directory, entry.name);
                
                if (entry.isDirectory()) {
                    const subFiles = await this.getScreenshotFiles(fullPath);
                    files.push(...subFiles);
                } else if (entry.name.endsWith('.png')) {
                    files.push(fullPath);
                }
            }
        } catch (error) {
            // Directory doesn't exist or can't be read
        }
        
        return files;
    }
    
    /**
     * Generate screenshot summary report
     */
    async generateReport() {
        try {
            const files = await this.getScreenshotFiles(this.baseDir);
            const report = {
                totalScreenshots: files.length,
                categories: {},
                generatedAt: new Date().toISOString()
            };
            
            // Group by category (subdirectory)
            for (const file of files) {
                const relativePath = path.relative(this.baseDir, file);
                const category = path.dirname(relativePath);
                
                if (!report.categories[category]) {
                    report.categories[category] = 0;
                }
                report.categories[category]++;
            }
            
            const reportPath = path.join(this.baseDir, 'screenshot-report.json');
            await fs.writeFile(reportPath, JSON.stringify(report, null, 2));
            
            console.log(`📊 Screenshot report generated: ${reportPath}`);
            return report;
            
        } catch (error) {
            console.warn('Could not generate screenshot report:', error.message);
            return null;
        }
    }
    
    /**
     * Create screenshot gallery HTML
     */
    async createGallery(title = 'Test Screenshots') {
        try {
            const files = await this.getScreenshotFiles(this.baseDir);
            const galleryData = [];
            
            for (const file of files) {
                const relativePath = path.relative(this.baseDir, file);
                const stats = await fs.stat(file);
                
                // Try to load metadata
                const metadataPath = file.replace('.png', '.json');
                let metadata = null;
                try {
                    const metadataContent = await fs.readFile(metadataPath, 'utf8');
                    metadata = JSON.parse(metadataContent);
                } catch (error) {
                    // No metadata file
                }
                
                galleryData.push({
                    path: relativePath,
                    filename: path.basename(file),
                    size: stats.size,
                    created: stats.birthtime,
                    modified: stats.mtime,
                    metadata
                });
            }
            
            // Sort by creation date (newest first)
            galleryData.sort((a, b) => b.created - a.created);
            
            // Generate HTML
            const html = this.generateGalleryHTML(title, galleryData);
            const galleryPath = path.join(this.baseDir, 'gallery.html');
            await fs.writeFile(galleryPath, html);
            
            console.log(`🖼️ Screenshot gallery created: ${galleryPath}`);
            return galleryPath;
            
        } catch (error) {
            console.warn('Could not create screenshot gallery:', error.message);
            return null;
        }
    }
    
    /**
     * Generate HTML for screenshot gallery
     */
    generateGalleryHTML(title, screenshots) {
        const screenshotCards = screenshots.map(screenshot => `
            <div class="screenshot-card">
                <img src="${screenshot.path}" alt="${screenshot.filename}" loading="lazy">
                <div class="screenshot-info">
                    <h4>${screenshot.filename}</h4>
                    <p><strong>Created:</strong> ${screenshot.created.toLocaleString()}</p>
                    <p><strong>Size:</strong> ${(screenshot.size / 1024).toFixed(1)} KB</p>
                    ${screenshot.metadata ? `
                        <details>
                            <summary>Metadata</summary>
                            <pre>${JSON.stringify(screenshot.metadata, null, 2)}</pre>
                        </details>
                    ` : ''}
                </div>
            </div>
        `).join('');
        
        return `
        <!DOCTYPE html>
        <html lang="en">
        <head>
            <meta charset="UTF-8">
            <meta name="viewport" content="width=device-width, initial-scale=1.0">
            <title>${title}</title>
            <style>
                body { font-family: system-ui, sans-serif; margin: 0; padding: 20px; background: #f5f5f5; }
                h1 { text-align: center; color: #333; }
                .gallery { display: grid; grid-template-columns: repeat(auto-fill, minmax(300px, 1fr)); gap: 20px; }
                .screenshot-card { background: white; border-radius: 8px; overflow: hidden; box-shadow: 0 2px 8px rgba(0,0,0,0.1); }
                .screenshot-card img { width: 100%; height: 200px; object-fit: cover; }
                .screenshot-info { padding: 15px; }
                .screenshot-info h4 { margin: 0 0 10px 0; color: #333; }
                .screenshot-info p { margin: 5px 0; color: #666; font-size: 14px; }
                details { margin-top: 10px; }
                pre { background: #f8f8f8; padding: 10px; border-radius: 4px; font-size: 12px; overflow-x: auto; }
                .stats { text-align: center; margin-bottom: 30px; padding: 20px; background: white; border-radius: 8px; }
            </style>
        </head>
        <body>
            <h1>${title}</h1>
            <div class="stats">
                <p><strong>Total Screenshots:</strong> ${screenshots.length}</p>
                <p><strong>Generated:</strong> ${new Date().toLocaleString()}</p>
            </div>
            <div class="gallery">
                ${screenshotCards}
            </div>
        </body>
        </html>
        `;
    }
}

module.exports = ScreenshotManager;