#!/usr/bin/env node

/**
 * SWAI Generator CLI
 * Generates .swai files from website manifest.json files
 */

const { program } = require('commander');
const cheerio = require('cheerio');
const yaml = require('js-yaml');
const fs = require('fs');
const path = require('path');

// Use native fetch (Node 18+)
const fetchUrl = globalThis.fetch;

program
    .name('swai-generator')
    .description('Generate .swai files from website manifests')
    .version('1.0.0');

program
    .argument('<url>', 'URL of the website to generate .swai from')
    .option('-o, --output <path>', 'Output file path (default: ./<app_id>.swai)')
    .option('-n, --name <name>', 'Override app name from manifest')
    .option('-i, --id <id>', 'Override app ID (default: generated from URL)')
    .option('--no-dynamic-titlebar', 'Disable dynamic titlebar colors')
    .option('--use-manifest', 'Enable use_manifest flag in generated file')
    .option('-v, --verbose', 'Verbose output')
    .action(async (url, options) => {
        try {
            await generateSwaiFile(url, options);
        } catch (error) {
            console.error('Error:', error.message);
            if (options.verbose) {
                console.error(error.stack);
            }
            process.exit(1);
        }
    });

program.parse();

/**
 * Main function to generate .swai file
 */
async function generateSwaiFile(url, options) {
    const verbose = options.verbose;
    
    // Normalize URL
    if (!url.startsWith('http://') && !url.startsWith('https://')) {
        url = 'https://' + url;
    }
    
    const parsedUrl = new URL(url);
    const origin = parsedUrl.origin;
    
    if (verbose) console.log('Fetching page:', url);
    
    // Fetch the HTML page
    const pageResponse = await fetchUrl(url, {
        headers: {
            'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
        }
    });
    
    if (!pageResponse.ok) {
        throw new Error(`Failed to fetch page: ${pageResponse.status} ${pageResponse.statusText}`);
    }
    
    const html = await pageResponse.text();
    const $ = cheerio.load(html);
    
    // Find manifest link
    const manifestLink = $('link[rel="manifest"]').attr('href');
    
    let manifest = null;
    let manifestUrl = null;
    
    if (manifestLink) {
        manifestUrl = resolveUrl(manifestLink, origin, url);
        if (verbose) console.log('Found manifest:', manifestUrl);
        
        try {
            const manifestResponse = await fetchUrl(manifestUrl, {
                headers: {
                    'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36'
                }
            });
            
            if (manifestResponse.ok) {
                manifest = await manifestResponse.json();
                if (verbose) console.log('Manifest loaded successfully');
            }
        } catch (e) {
            console.warn('Warning: Could not fetch manifest:', e.message);
        }
    } else {
        console.warn('Warning: No manifest found on page');
    }
    
    // Extract page title as fallback
    const pageTitle = $('title').text().trim() || parsedUrl.hostname;
    
    // Get theme color from meta tag
    const themeColorLight = $('meta[name="theme-color"][media*="light"]').attr('content') 
        || $('meta[name="theme-color"]:not([media])').attr('content');
    const themeColorDark = $('meta[name="theme-color"][media*="dark"]').attr('content');
    
    // Build the .swai configuration
    const swaiConfig = buildSwaiConfig({
        url,
        origin,
        parsedUrl,
        manifest,
        manifestUrl,
        pageTitle,
        themeColorLight,
        themeColorDark,
        options
    });
    
    // Generate output path
    let outputPath = options.output;
    if (!outputPath) {
        const filename = swaiConfig.app_id.replace(/\./g, '-') + '.swai';
        outputPath = path.join(process.cwd(), filename);
    }
    
    // Write YAML file
    const yamlContent = yaml.dump(swaiConfig, {
        lineWidth: -1,
        quotingType: '"',
        forceQuotes: false
    });
    
    fs.writeFileSync(outputPath, yamlContent);
    
    console.log('Generated:', outputPath);
    console.log('');
    console.log('App Name:', swaiConfig.app_name);
    console.log('App ID:', swaiConfig.app_id);
    console.log('Main URL:', swaiConfig.main_url);
    console.log('Allowed URLs:', swaiConfig.allowed_urls.length, 'patterns');
    
    if (verbose) {
        console.log('');
        console.log('Full configuration:');
        console.log(yamlContent);
    }
}

/**
 * Build the .swai configuration object
 */
function buildSwaiConfig({ url, origin, parsedUrl, manifest, manifestUrl, pageTitle, themeColorLight, themeColorDark, options }) {
    // Determine app name
    let appName = options.name;
    if (!appName && manifest) {
        appName = manifest.name || manifest.short_name;
    }
    if (!appName) {
        appName = pageTitle;
    }
    
    // Determine app ID
    let appId = options.id;
    if (!appId) {
        // Generate from hostname (e.g., "app.example.com" -> "com.example.app")
        const hostParts = parsedUrl.hostname.split('.').reverse();
        appId = hostParts.join('.');
    }
    
    // Determine main URL
    let mainUrl = url;
    if (manifest && manifest.start_url) {
        mainUrl = resolveUrl(manifest.start_url, origin, manifestUrl || url);
    }
    
    // Build allowed URLs list
    const allowedUrls = new Set();
    
    // Add main URL pattern
    allowedUrls.add(origin + '/*');
    
    // Add scope from manifest
    if (manifest && manifest.scope) {
        const scope = resolveUrl(manifest.scope, origin, manifestUrl || url);
        const scopePattern = scope.endsWith('/') ? scope + '*' : scope + '/*';
        allowedUrls.add(scopePattern);
    }
    
    // Add scope_extensions from manifest
    if (manifest && manifest.scope_extensions && Array.isArray(manifest.scope_extensions)) {
        for (const ext of manifest.scope_extensions) {
            if (ext.origin) {
                let originPattern = ext.origin;
                if (originPattern.startsWith('http')) {
                    allowedUrls.add(originPattern + '/*');
                } else if (originPattern.startsWith('*.')) {
                    allowedUrls.add('https://' + originPattern + '/*');
                } else {
                    allowedUrls.add('https://' + originPattern + '/*');
                }
            }
        }
    }
    
    // Build the config object
    const config = {
        app_name: appName,
        app_id: appId,
        main_url: mainUrl,
        allowed_urls: Array.from(allowedUrls)
    };
    
    // Add optional fields
    if (manifest && manifest.description) {
        config.comment = manifest.description;
    }
    
    // Add categories from manifest
    if (manifest && manifest.categories && manifest.categories.length > 0) {
        config.categories = manifest.categories.map(cat => {
            // Capitalize first letter for FreeDesktop categories
            return cat.charAt(0).toUpperCase() + cat.slice(1);
        });
    }
    
    // Add icon path placeholder if manifest has icons
    if (manifest && manifest.icons && manifest.icons.length > 0) {
        // Find the best icon (prefer larger, PNG or SVG)
        const icons = manifest.icons.sort((a, b) => {
            const sizeA = parseInt(a.sizes?.split('x')[0] || '0');
            const sizeB = parseInt(b.sizes?.split('x')[0] || '0');
            return sizeB - sizeA;
        });
        
        const bestIcon = icons[0];
        const iconUrl = resolveUrl(bestIcon.src, origin, manifestUrl || url);
        config.icon_url = iconUrl;
    }
    
    // Dynamic titlebar option
    if (options.dynamicTitlebar === false) {
        config.use_dynamic_titlebar_colors = false;
    }
    
    // Use manifest option
    if (options.useManifest) {
        config.use_manifest = true;
    }
    
    return config;
}

/**
 * Resolve a URL that may be relative
 */
function resolveUrl(url, origin, baseUrl) {
    if (!url) return null;
    
    try {
        if (url.startsWith('http://') || url.startsWith('https://')) {
            return url;
        }
        
        if (url.startsWith('/')) {
            return origin + url;
        }
        
        // Relative to base URL
        const base = baseUrl.substring(0, baseUrl.lastIndexOf('/') + 1);
        return base + url;
    } catch (e) {
        return null;
    }
}

