#!/usr/bin/env node

/**
 * Workflow Template Export/Import Script
 * 
 * This script exports and imports workflow templates (instance_template and print_template)
 * from MongoDB to files for Blaze-to-Liquid conversion.
 * 
 * Usage:
 *   node run.js export [flowId] [outputDir]
 *   node run.js import <flowId> [templateType] [inputDir]
 * 
 * Examples:
 *   node run.js export                          # Export all flows
 *   node run.js export abc123                   # Export specific flow
 *   node run.js export abc123 /custom/path      # Export to custom directory
 *   node run.js import abc123                   # Import both templates
 *   node run.js import abc123 instance_template # Import only instance_template
 */
require('dotenv-flow').config({});
const { MongoClient } = require('mongodb');
const path = require('path');
const fs = require('fs').promises;

// MongoDB connection
let client = null;
let db = null;

// Connect to MongoDB using native driver
async function connectToMongoDB() {
    if (db) {
        return db;
    }

    const mongoUrl = process.env.MONGO_URL || 'mongodb://127.0.0.1:27017/steedos-plugins';
    console.log('mongoUrl', mongoUrl)
    try {
        client = new MongoClient(mongoUrl, {
            useNewUrlParser: true,
            useUnifiedTopology: true,
            directConnection: true
        });
        await client.connect();
        
        // Extract database name from URL
        const dbName = mongoUrl.split('/').pop().split('?')[0];
        db = client.db(dbName);
        
        console.log(`Connected to MongoDB: ${dbName}`);
        return db;
    } catch (error) {
        console.error('Failed to connect to MongoDB:', error.message);
        throw error;
    }
}

// Close MongoDB connection
async function closeMongoDB() {
    if (client) {
        await client.close();
        client = null;
        db = null;
    }
}

// Get collection from MongoDB
async function getCollection(name) {
    const database = await connectToMongoDB();
    return database.collection(name);
}

// Export templates to .blaze files
async function exportTemplates(flowId, outputDir) {
    try {
        const baseDir = outputDir || path.join(process.cwd(), 'flows-template');
        
        // Create directory
        try {
            await fs.mkdir(baseDir, { recursive: true });
        } catch (mkdirError) {
            if (mkdirError.code !== 'EEXIST') {
                throw new Error(`Failed to create directory ${baseDir}: ${mkdirError.message}`);
            }
        }

        const flowsCollection = await getCollection('flows');
        const formsCollection = await getCollection('forms');

        // Build query
        const query = {};
        if (flowId) {
            // Export specific flow by ID
            query._id = flowId;
        } else {
            // Export all flows with templates (when flowId is not specified)
            query.$or = [
                { instance_template: { $exists: true, $ne: null, $ne: '' } },
                { print_template: { $exists: true, $ne: null, $ne: '' } }
            ];
        }

        const flows = await flowsCollection.find(query, {
            projection: {
                _id: 1,
                name: 1,
                instance_template: 1,
                print_template: 1,
                form: 1
            }
        }).toArray();

        if (flows.length === 0) {
            console.log('No flows found with templates.');
            return;
        }

        if (flowId) {
            console.log(`Found flow: ${flows[0]?.name || flowId}`);
        } else {
            console.log(`Found ${flows.length} flow(s) with templates to export.`);
        }

        const exportSummary = [];

        for (const flow of flows) {
            console.log(`\nExporting flow: ${flow.name} (${flow._id})`);
            
            let fileCount = 0;
            const flowSummary = {
                id: flow._id,
                name: flow.name,
                templates: []
            };

            // Export instance_template
            if (flow.instance_template) {
                try {
                    const fileName = `${flow._id}.instance_template.blaze`;
                    const filePath = path.join(baseDir, fileName);
                    await fs.writeFile(filePath, flow.instance_template, 'utf8');
                    console.log(`  ✓ ${fileName}`);
                    fileCount++;
                    flowSummary.templates.push('instance_template');
                } catch (error) {
                    console.error(`  ✗ Failed to write instance_template: ${error.message}`);
                }
            }

            // Export print_template
            if (flow.print_template) {
                try {
                    const fileName = `${flow._id}.print_template.blaze`;
                    const filePath = path.join(baseDir, fileName);
                    await fs.writeFile(filePath, flow.print_template, 'utf8');
                    console.log(`  ✓ ${fileName}`);
                    fileCount++;
                    flowSummary.templates.push('print_template');
                } catch (error) {
                    console.error(`  ✗ Failed to write print_template: ${error.message}`);
                }
            }

            // Export field definitions
            if (flow.form) {
                const form = await formsCollection.findOne(
                    { _id: flow.form },
                    { projection: { 'current.fields': 1 } }
                );

                if (form && form.current && form.current.fields) {
                    try {
                        const fileName = `${flow._id}.fields.json`;
                        const filePath = path.join(baseDir, fileName);
                        await fs.writeFile(
                            filePath,
                            JSON.stringify(form.current.fields, null, 2),
                            'utf8'
                        );
                        console.log(`  ✓ ${fileName}`);
                        fileCount++;
                        flowSummary.templates.push('fields');
                    } catch (error) {
                        console.error(`  ✗ Failed to write fields.json: ${error.message}`);
                    }
                }
            }

            if (fileCount > 0) {
                console.log(`  Exported ${fileCount} file(s)`);
                exportSummary.push(flowSummary);
            }
        }

        console.log(`\n✅ Export completed. Files saved to: ${baseDir}`);
        
        // Print summary
        console.log('\n📋 导出清单:');
        console.log('='.repeat(80));
        exportSummary.forEach((item, index) => {
            console.log(`${index + 1}. ${item.name} (${item.id})`);
            console.log(`   模板: ${item.templates.join(', ')}`);
        });
        console.log('='.repeat(80));
        console.log(`共导出 ${exportSummary.length} 个流程`);
        
        // Save summary to file
        const summaryFile = path.join(baseDir, 'export-summary.json');
        await fs.writeFile(
            summaryFile,
            JSON.stringify(exportSummary, null, 2),
            'utf8'
        );
        console.log(`\n清单已保存至: ${summaryFile}`);
    } catch (error) {
        console.error(`\n❌ Export failed: ${error.message}`);
        throw error;
    }
}

// Import templates from .liquid files
async function importTemplates(flowId, templateType, inputDir) {
    try {
        if (!flowId) {
            throw new Error('flowId is required for import');
        }

        const baseDir = inputDir || path.join(process.cwd(), 'flows-template');
        const flowsCollection = await getCollection('flows');

        // Verify flow exists
        const flow = await flowsCollection.findOne(
            { _id: flowId },
            { projection: { _id: 1, name: 1, instance_template: 1, print_template: 1 } }
        );

        if (!flow) {
            throw new Error(`Flow not found: ${flowId}`);
        }

        console.log(`\nImporting templates for flow: ${flow.name} (${flow._id})`);

        const updateObj = {
            modified: new Date()
        };
        const imported = [];

        // Determine which templates to import
        const templatesToImport = templateType 
            ? [templateType] 
            : ['instance_template', 'print_template'];

        for (const type of templatesToImport) {
            const liquidFile = path.join(baseDir, `${flowId}.${type}.liquid`);
            
            try {
                // Check if file exists and is readable
                await fs.access(liquidFile, fs.constants.R_OK);
                
                // Read the liquid file
                const liquidContent = await fs.readFile(liquidFile, 'utf8');

                // Backup old value if it exists
                const backupField = `${type}_backup`;
                if (flow[type]) {
                    updateObj[backupField] = flow[type];
                    console.log(`  ℹ Backing up old ${type} to ${backupField}`);
                }

                // Set new value
                updateObj[type] = liquidContent;
                
                imported.push({
                    type,
                    file: `${flowId}.${type}.liquid`,
                    backed_up: !!flow[type]
                });

                console.log(`  ✓ Imported ${type} from ${flowId}.${type}.liquid`);
            } catch (fileError) {
                if (fileError.code === 'ENOENT') {
                    // File doesn't exist, skip
                    console.log(`  ⊗ ${type} file not found, skipping`);
                    continue;
                }
                if (fileError.code === 'EACCES') {
                    throw new Error(`Permission denied reading file ${liquidFile}`);
                }
                throw new Error(`Failed to read file ${liquidFile}: ${fileError.message}`);
            }
        }

        if (imported.length === 0) {
            throw new Error(`No template files found for flow ${flowId}`);
        }

        // Update the flow
        await flowsCollection.updateOne(
            { _id: flowId },
            { $set: updateObj }
        );

        console.log(`\n✅ Import completed. Imported ${imported.length} template(s).`);
        imported.forEach(item => {
            console.log(`  - ${item.type}: ${item.backed_up ? 'updated (old value backed up)' : 'created'}`);
        });
    } catch (error) {
        console.error(`\n❌ Import failed: ${error.message}`);
        throw error;
    }
}

// Main function
async function main() {
    const args = process.argv.slice(2);
    const command = args[0];

    if (!command || !['export', 'import'].includes(command)) {
        console.log(`
Workflow Template Export/Import Script

Usage:
  node run.js export [flowId] [outputDir]
  node run.js import <flowId> [templateType] [inputDir]

Commands:
  export                Export templates to .blaze files
  import                Import templates from .liquid files

Export Examples:
  node run.js export                          # Export all flows
  node run.js export abc123                   # Export specific flow
  node run.js export abc123 /custom/path      # Export to custom directory

Import Examples:
  node run.js import abc123                   # Import both templates
  node run.js import abc123 instance_template # Import only instance_template
  node run.js import abc123 print_template    # Import only print_template

Directory Structure:
  flows-template/
  ├── {flowId}.instance_template.blaze    # Exported Blaze template
  ├── {flowId}.print_template.blaze       # Exported Blaze template
  ├── {flowId}.fields.json                # Field definitions
  ├── {flowId}.instance_template.liquid   # Converted Liquid template (for import)
  └── {flowId}.print_template.liquid      # Converted Liquid template (for import)

Workflow:
  1. Export: node run.js export [flowId]
  2. Convert: Convert .blaze files to .liquid format (manually or with Copilot)
  3. Import: node run.js import <flowId>
        `);
        process.exit(command ? 1 : 0);
    }

    try {
        if (command === 'export') {
            const flowId = args[1];
            const outputDir = args[2];
            await exportTemplates(flowId, outputDir);
        } else if (command === 'import') {
            const flowId = args[1];
            const templateType = args[2];
            const inputDir = args[3];
            
            // Validate templateType if provided
            if (templateType && !['instance_template', 'print_template'].includes(templateType)) {
                // Maybe it's the inputDir
                await importTemplates(flowId, null, templateType);
            } else {
                await importTemplates(flowId, templateType, inputDir);
            }
        }
        
        await closeMongoDB();
        process.exit(0);
    } catch (error) {
        console.error('\n❌ Error:', error.message);
        await closeMongoDB();
        process.exit(1);
    }
}

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

module.exports = { exportTemplates, importTemplates };
