#!/usr/bin/env node

/**
 * Enhanced Build Script with Minification for Keyboard Vagabond
 * 
 * This script:
 * 1. Optimizes CSS with PurgeCSS
 * 2. Minifies CSS with cssnano
 * 3. Bundles and minifies JavaScript with Terser
 * 4. Minifies HTML with html-minifier-terser
 * 5. Creates a production-ready dist/ folder
 */

const { PurgeCSS } = require('purgecss');
const cssnano = require('cssnano');
const postcss = require('postcss');
const { minify: htmlMinify } = require('html-minifier-terser');
const { minify: jsMinify } = require('terser');
const fs = require('fs').promises;
const path = require('path');

async function createOptimizedBuild() {
    console.log('🚀 Starting enhanced build with minification...');
    
    // Clean and create dist directory
    await fs.rm('dist', { recursive: true, force: true });
    await fs.mkdir('dist', { recursive: true });
    await fs.mkdir('dist/css', { recursive: true });
    await fs.mkdir('dist/site-styles', { recursive: true });
    await fs.mkdir('dist/assets', { recursive: true });

    try {
        // Step 1: CSS Optimization with PurgeCSS
        console.log('📝 Step 1: Optimizing CSS with PurgeCSS...');
        const purgeResults = await new PurgeCSS().purge({
            content: [
                'public/index.html',
                'public/about.html',
                'public/getting-started.html',
            ],
            css: [
                'public/css/pico.jade.css',
                // 'public/css/pico.colors.min.css'
            ],
            safelist: {
                standard: [
                    'container',
                    'banner-container',
                    'banner',
                    'banner-title', 
                    'banner-subtitle',
                    'video-container',
                    'photo-gallery',
                    'photo-item',
                    'dark',
                    // Responsive navigation classes
                    'desktop-nav',
                    'mobile-nav',
                    'dropdown',
                    // Button classes
                    'secondary',
                    'outline'
                ],
                deep: [
                    /^--pico-/,
                    /\[data-theme/,
                    /data-theme/,
                    /:hover/,
                    /:focus/,
                    /:active/,
                    /:visited/,
                    // Dropdown and interactive states
                    /\[open\]/,
                    /\[role=/,
                    /details/,
                    /summary/
                ],
                // Keep base typography selectors that are essential for proper font rendering
                greedy: [
                    /^:where\(:root\)$/,
                    /^:where\(:host\)$/,
                    /^:root$/,
                    /^:host$/,
                    /^body$/,
                    /^html$/,
                    /^h[1-6]$/,
                    /^p$/,
                    /^ul$/,
                    /^li$/,
                    /^strong$/,
                    /^a$/,
                    /^a:/,  // Add anchor pseudo-classes like a:hover, a:focus
                    /^a\./,  // Add anchor with classes like a.secondary
                    /^a\[/,  // Add anchor with attributes like a[role=button]
                    // Dropdown and button elements
                    /^details$/,
                    /^summary$/,
                    /^button$/,
                    /^button:/,
                    /^button\./,
                    /^button\[/
                ]
            },
            variables: true,
            keyframes: true
        });

        // Step 1.5: Add essential rules that PurgeCSS removes too aggressively
        const fontRule = `:where(:host),:where(:root){background-color:var(--pico-background-color);color:var(--pico-color);font-weight:var(--pico-font-weight);font-size:var(--pico-font-size);line-height:var(--pico-line-height);font-family:var(--pico-font-family);}`;
        
        // Add missing anchor tag primary color rule
        const anchorRule = `a:not([role=button]){--pico-color:var(--pico-primary);--pico-underline:var(--pico-primary-underline);background-color:transparent;color:var(--pico-color);text-decoration:var(--pico-text-decoration);text-decoration-color:var(--pico-underline);text-underline-offset:.125em;transition:background-color var(--pico-transition),color var(--pico-transition),text-decoration var(--pico-transition),box-shadow var(--pico-transition);}a:not([role=button]):is(:hover,:active,:focus){--pico-color:var(--pico-primary-hover);--pico-underline:var(--pico-primary-hover-underline);text-decoration:underline;}`;

        // Step 2: CSS Minification with cssnano
        console.log('🗜️  Step 2: Minifying CSS with cssnano...');
        const combinedCSS = purgeResults.map(result => result.css).join('\n\n') + '\n' + fontRule + '\n' + anchorRule;
        
        const minifiedCSS = await postcss([
            cssnano({
                preset: ['default', {
                    discardComments: { removeAll: true },
                    normalizeWhitespace: true,
                    colormin: true,
                    minifySelectors: true,
                    minifyParams: true,
                    mergeLonghand: true,
                    mergeRules: true
                }]
            })
        ]).process(combinedCSS, { from: undefined });

        // Combine with custom site styles
        const customCSS = await fs.readFile('public/site-styles/style.css', 'utf8');
        const combinedWithCustomCSS = minifiedCSS.css + '\n' + customCSS;
        
        // Minify the combined CSS bundle
        const finalMinifiedCSS = await postcss([
            cssnano({
                preset: ['default', {
                    discardComments: { removeAll: true },
                    normalizeWhitespace: true
                }]
            })
        ]).process(combinedWithCustomCSS, { from: undefined });
        
        await fs.writeFile('dist/css/bundle.css', finalMinifiedCSS.css);

        // Step 3: JavaScript Bundling and Minification
        console.log('📦 Step 3: Bundling and minifying JavaScript...');
        await bundleAndMinifyJS();

        // Step 4: HTML Minification
        console.log('📄 Step 4: Minifying HTML...');
        await minifyHTMLFile('public/index.html', 'dist/index.html');
        await minifyHTMLFile('public/about.html', 'dist/about.html');
        await minifyHTMLFile('public/getting-started.html', 'dist/getting-started.html');

        // Step 5: Copy assets and SEO files
        console.log('📁 Step 5: Copying assets and SEO files...');
        try {
            await copyDirectoryRecursive('public/assets', 'dist/assets');
            console.log('   ✅ Assets directory copied recursively');
        } catch (err) {
            console.log('   No assets directory found, skipping...');
        }

        // Copy SEO files
        try {
            await fs.copyFile('public/structured-data.json', 'dist/structured-data.json');
            await fs.copyFile('public/getting-started-structured-data.json', 'dist/getting-started-structured-data.json');
            await fs.copyFile('public/sitemap.xml', 'dist/sitemap.xml');
            await fs.copyFile('public/robots.txt', 'dist/robots.txt');
            console.log('   ✅ SEO files copied (structured-data.json, getting-started-structured-data.json, sitemap.xml, robots.txt)');
        } catch (err) {
            console.log('   ⚠️  Some SEO files not found, skipping...');
        }

        // Calculate compression results
        const originalCSSSize = (await fs.stat('public/css/pico.jade.min.css')).size + 
                               (await fs.stat('public/css/pico.min.css')).size;
        const optimizedCSSSize = (await fs.stat('dist/css/bundle.css')).size;
        const cssReduction = ((originalCSSSize - optimizedCSSSize) / originalCSSSize * 100).toFixed(1);

        const originalHTMLSize = (await fs.stat('public/index.html')).size + 
                                (await fs.stat('public/about.html')).size;
        const minifiedHTMLSize = (await fs.stat('dist/index.html')).size + 
                                (await fs.stat('dist/about.html')).size;
        const htmlReduction = ((originalHTMLSize - minifiedHTMLSize) / originalHTMLSize * 100).toFixed(1);

        // Calculate JavaScript size if bundled
        let jsInfo = '';
        try {
            const bundledJSSize = (await fs.stat('dist/scripts/bundle.js')).size;
            jsInfo = `\n   JavaScript: Bundled into ${(bundledJSSize / 1024).toFixed(1)}KB`;
        } catch (err) {
            // No JavaScript files were bundled
        }

        console.log('\n✨ Build complete with minification!');
        console.log('\n📊 Optimization Results:');
        console.log(`   CSS: ${(originalCSSSize / 1024).toFixed(1)}KB → ${(optimizedCSSSize / 1024).toFixed(1)}KB (${cssReduction}% reduction)`);
        console.log(`   HTML: ${(originalHTMLSize / 1024).toFixed(1)}KB → ${(minifiedHTMLSize / 1024).toFixed(1)}KB (${htmlReduction}% reduction)`);
        if (jsInfo) console.log(jsInfo);
        console.log(`   Total CSS+HTML savings: ${((originalCSSSize + originalHTMLSize - optimizedCSSSize - minifiedHTMLSize) / 1024).toFixed(1)}KB`);
        
        console.log('\n📂 Production files created in dist/');
        console.log('🚀 Ready for deployment!');

    } catch (error) {
        console.error('❌ Build failed:', error);
        process.exit(1);
    }
}

async function bundleAndMinifyJS() {
    // Create scripts directory
    await fs.mkdir('dist/scripts', { recursive: true });
    
    // Find all JavaScript files in site-scripts directory
    const jsFiles = [];
    try {
        const siteScriptsDir = 'public/site-scripts';
        const files = await fs.readdir(siteScriptsDir);
        
        for (const file of files) {
            if (file.endsWith('.js')) {
                jsFiles.push(path.join(siteScriptsDir, file));
            }
        }
    } catch (err) {
        console.log('   No site-scripts directory found, skipping...');
        return;
    }
    
    if (jsFiles.length === 0) {
        console.log('   No JavaScript files found to bundle');
        return;
    }
    
    console.log(`   Found ${jsFiles.length} JavaScript file(s) to bundle:`);
    jsFiles.forEach(file => console.log(`     - ${path.basename(file)}`));
    
    // Read and concatenate all JavaScript files
    let combinedJS = '';
    for (const jsFile of jsFiles) {
        const content = await fs.readFile(jsFile, 'utf8');
        combinedJS += `\n// File: ${path.basename(jsFile)}\n`;
        combinedJS += content;
        combinedJS += '\n';
    }
    
    // Minify the combined JavaScript
    try {
        const minified = await jsMinify(combinedJS, {
            compress: {
                drop_console: false, // Keep console.log for debugging if needed
                drop_debugger: true,
                dead_code: true,
                conditionals: true,
                evaluate: true,
                booleans: true,
                loops: true,
                unused: true,
                hoist_funs: true,
                keep_fargs: false,
                hoist_vars: false,
                if_return: true,
                join_vars: true,

                side_effects: true
            },
            mangle: {
                reserved: ['theme', 'toggle', 'icon'] // Preserve important function names
            },
            format: {
                comments: false
            }
        });
        
        await fs.writeFile('dist/scripts/bundle.js', minified.code);
        
        // Calculate compression ratio
        const originalSize = Buffer.byteLength(combinedJS, 'utf8');
        const minifiedSize = Buffer.byteLength(minified.code, 'utf8');
        const reduction = ((originalSize - minifiedSize) / originalSize * 100).toFixed(1);
        
        console.log(`   ✅ JavaScript bundled: ${(originalSize / 1024).toFixed(1)}KB → ${(minifiedSize / 1024).toFixed(1)}KB (${reduction}% reduction)`);
        
    } catch (error) {
        console.error('   ❌ JavaScript minification failed:', error);
        // Fallback: write unminified version
        await fs.writeFile('dist/scripts/bundle.js', combinedJS);
        console.log('   ✅ JavaScript bundled (unminified fallback)');
    }
}

async function copyDirectoryRecursive(src, dest) {
    // Create destination directory if it doesn't exist
    await fs.mkdir(dest, { recursive: true });
    
    // Read all items in source directory
    const entries = await fs.readdir(src, { withFileTypes: true });
    
    for (const entry of entries) {
        const srcPath = path.join(src, entry.name);
        const destPath = path.join(dest, entry.name);
        
        if (entry.isDirectory()) {
            // Recursively copy subdirectories
            await copyDirectoryRecursive(srcPath, destPath);
        } else {
            // Copy files
            await fs.copyFile(srcPath, destPath);
        }
    }
}

async function injectStructuredData(html, jsonPath) {
    try {
        const json = await fs.readFile(jsonPath, 'utf8');
        const minified = JSON.stringify(JSON.parse(json));
        return html.replace(
            /<script type="application\/ld\+json">[\s\S]*?<\/script>/,
            `<script type="application/ld+json">${minified}</script>`
        );
    } catch (err) {
        console.log(`   ⚠️  ${jsonPath} not found, skipping injection`);
        return html;
    }
}

async function minifyHTMLFile(inputPath, outputPath) {
    const html = await fs.readFile(inputPath, 'utf8');
    
    // Update CSS references for production and update script reference
    let updatedHTML = html
        .replace(/<link rel="stylesheet" href="css\/pico\.jade\.min\.css">/g, '')
        .replace(/<link rel="stylesheet" href="site-styles\/style\.css">/g, 
                '<link rel="stylesheet" href="css/bundle.css">')
        .replace(/<script src="site-scripts\/theme-toggle\.js"><\/script>/g, ''); // Remove individual script references

    const structuredDataByPage = {
        'index.html': 'public/structured-data.json',
        'getting-started.html': 'public/getting-started-structured-data.json',
    };
    const structuredDataPath = structuredDataByPage[path.basename(inputPath)];
    if (structuredDataPath) {
        updatedHTML = await injectStructuredData(updatedHTML, structuredDataPath);
    }

    const minified = await htmlMinify(updatedHTML, {
        collapseWhitespace: true,
        removeComments: true,
        removeRedundantAttributes: true,
        removeScriptTypeAttributes: true,
        removeStyleLinkTypeAttributes: true,
        minifyCSS: true,
        minifyJS: true,
        useShortDoctype: true,
        removeAttributeQuotes: false, // Keep for readability
        removeEmptyAttributes: true,
        sortAttributes: true,
        sortClassName: true
    });

    await fs.writeFile(outputPath, minified);
    console.log(`   ✅ ${path.basename(inputPath)} → ${path.basename(outputPath)}`);
}

// Run if called directly
if (require.main === module) {
    createOptimizedBuild();
}

module.exports = { createOptimizedBuild };
