/*
 * @Author: Copilot
 * @Date: 2026-01-28
 * @Description: Transform old approval comments field configurations to new format
 * 
 * This endpoint scans forms collection and transforms fields with approval comments formulas
 * from old format to new steedos-field format with config object.
 */

const { MongoClient } = require('mongodb');

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

async function connectToMongoDB() {
    if (db) {
        return db;
    }

    const mongoUrl = process.env.MONGO_URL || 'mongodb://127.0.0.1:27017/steedos';
    try {
        client = new MongoClient(mongoUrl);
        await client.connect();
        
        const dbName = mongoUrl.split('/').pop().split('?')[0];
        db = client.db(dbName);
        return db;
    } catch (error) {
        console.error('Failed to connect to MongoDB:', error.message);
        throw error;
    }
}

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

/**
 * Check if a field value matches approval comments patterns
 */
function matchesApprovalPattern(value) {
    if (!value || typeof value !== 'string') {
        return false;
    }
    
    const patterns = [
        /\{traces\./,
        /\$\{traces\./,
        /\{signature\.traces\./,
        /\$\{signature\.traces\./,
        /\{yijianlan:/
    ];
    
    return patterns.some(pattern => pattern.test(value));
}

/**
 * Parse yijianlan object string (non-standard JSON with unquoted keys)
 * Example: {yijianlan:{step:'部门经理审核',default:'已阅'}}
 */
function parseYijianlan(str) {
    try {
        // Extract yijianlan content
        const match = str.match(/\{yijianlan:\s*(\{[^}]+\})\s*\}/);
        if (!match) {
            return null;
        }
        
        const objStr = match[1];
        // Convert to valid JSON by adding quotes to unquoted keys and converting single quotes to double quotes
        // Note: This assumes the yijianlan content is in the expected format
        const jsonStr = objStr
            .replace(/(\w+):/g, '"$1":')  // Add quotes to keys
            .replace(/'/g, '"');           // Convert single quotes to double quotes
        return JSON.parse(jsonStr);
    } catch (error) {
        // Log more detailed error for troubleshooting
        console.error('Error parsing yijianlan:', error.message, 'Input:', str);
        return null;
    }
}

/**
 * Extract step name from formula/default_value
 */
function extractStepName(value) {
    // Handle yijianlan format
    if (value.includes('yijianlan:')) {
        const yijianlan = parseYijianlan(value);
        if (yijianlan && yijianlan.step) {
            return yijianlan.step;
        }
    }
    
    // Handle ${signature.traces.XXX} or {signature.traces.XXX}
    let match = value.match(/\$?\{signature\.traces\.([^}]+)\}/);
    if (match) {
        return match[1];
    }
    
    // Handle ${traces.XXX} or {traces.XXX}
    match = value.match(/\$?\{traces\.([^}]+)\}/);
    if (match) {
        return match[1];
    }
    
    return '';
}

/**
 * Parse special boolean logic for show_cc and show_handler
 */
function parseShowOptions(yijianlan) {
    if (!yijianlan) {
        return { show_cc: true, show_handler: true };
    }
    
    if (yijianlan.only_cc === true || yijianlan.only_cc === 'true') {
        return { show_cc: true, show_handler: false };
    }
    
    if (yijianlan.only_handler === true || yijianlan.only_handler === 'true') {
        return { show_cc: false, show_handler: true };
    }
    
    return { show_cc: true, show_handler: true };
}

/**
 * Transform a single field
 */
function transformField(field) {
    // Check formula and default_value only
    const formulaValue = field.formula || field.default_value;
    
    if (!formulaValue || !matchesApprovalPattern(formulaValue)) {
        return null;
    }
    
    // Already transformed
    if (field.__approval_comments_transformed) {
        return null;
    }
    
    const stepName = extractStepName(formulaValue);
    if (!stepName) {
        return null;
    }
    
    // Parse yijianlan for additional config
    const yijianlan = formulaValue.includes('yijianlan:') ? parseYijianlan(formulaValue) : null;
    const showOptions = parseShowOptions(yijianlan);
    
    // Check if it's signature.traces (image sign)
    const showImageSign = formulaValue.includes('signature.traces') || 
                          (yijianlan && yijianlan.image_sign === true) || 
                          false;
    
    // Get default value from yijianlan
    const defaultValue = yijianlan && yijianlan.default ? yijianlan.default : undefined;
    
    // Get label from field name or code only
    const label = field.name || field.code;
    
    // Build the step config
    const step = {
        name: stepName,
        show_cc: showOptions.show_cc,
        show_handler: showOptions.show_handler,
        show_image_sign: showImageSign
    };
    
    if (defaultValue !== undefined) {
        step.default = defaultValue;
    }
    
    // Build update object
    const update = {
        type: 'steedos-field',
        __approval_comments_transformed: true,
        __approval_comments_original_type: field.type || 'input',  // Store original type for zero-loss rollback
        config: {
            type: 'approval_comments',
            label: label,
            object: '',
            name: field.code || field.name,
            steps: [step]
        }
    };
    
    // Backup original formula if exists
    if (field.formula) {
        update.__approval_comments_original_formula = field.formula;
    }
    
    // Backup original default_value if exists
    if (field.default_value) {
        update.__approval_comments_original_default_value = field.default_value;
    }
    
    // Rename _amisField to __approval_comments_original__amisField if it exists
    if (field._amisField) {
        update.__approval_comments_original__amisField = field._amisField;
    }
    
    return update;
}

/**
 * Transform a single amis schema field node
 */
function transformAmisSchemaField(node) {
    // Check if node has a value property that matches approval patterns
    if (!node || !node.value || typeof node.value !== 'string') {
        return null;
    }
    
    if (!matchesApprovalPattern(node.value)) {
        return null;
    }
    
    const stepName = extractStepName(node.value);
    if (!stepName) {
        return null;
    }
    
    // Parse yijianlan for additional config
    const yijianlan = node.value.includes('yijianlan:') ? parseYijianlan(node.value) : null;
    const showOptions = parseShowOptions(yijianlan);
    
    // Check if it's signature.traces (image sign)
    const showImageSign = node.value.includes('signature.traces') || 
                          (yijianlan && yijianlan.image_sign === true) || 
                          false;
    
    // Get default value from yijianlan
    const defaultValue = yijianlan && yijianlan.default ? yijianlan.default : undefined;
    
    // Build the step config
    const step = {
        name: stepName,
        show_cc: showOptions.show_cc,
        show_handler: showOptions.show_handler,
        show_image_sign: showImageSign
    };
    
    if (defaultValue !== undefined) {
        step.default = defaultValue;
    }
    
    // Build transformed node - keep all original properties except value
    const transformed = {
        ...node,
        type: 'sfield-approvalcomments',
        __approval_comments_original_type: node.type,  // Store original type for zero-loss rollback
        config: {
            type: 'approval_comments',
            label: node.label || node.name,
            object: '',
            name: node.name,
            steps: [step]
        }
    };
    
    // Remove value property from transformed node
    delete transformed.value;
    
    return transformed;
}

/**
 * Process amis_schema string - parse, transform body nodes, and stringify
 */
function processAmisSchema(amisSchemaStr) {
    if (!amisSchemaStr || typeof amisSchemaStr !== 'string') {
        return null;
    }
    
    try {
        // Parse the JSON string
        const schema = JSON.parse(amisSchemaStr);
        
        // Only process if schema has a body array
        if (!schema.body || !Array.isArray(schema.body)) {
            return null;
        }
        
        let hasChanges = false;
        
        // Process only first-level body nodes
        for (let i = 0; i < schema.body.length; i++) {
            const node = schema.body[i];
            const transformed = transformAmisSchemaField(node);
            
            if (transformed) {
                schema.body[i] = transformed;
                hasChanges = true;
            }
        }
        
        // Return stringified schema if there were changes
        if (hasChanges) {
            return JSON.stringify(schema);
        }
        
        return null;
    } catch (error) {
        console.error('Error processing amis_schema:', error.message, error.stack);
        return null;
    }
}

/**
 * Process fields array and return bulk operations
 */
function processFieldsArray(fields, pathPrefix) {
    const updates = [];
    const unsets = [];
    
    if (!Array.isArray(fields)) {
        return { updates, unsets };
    }
    
    for (let i = 0; i < fields.length; i++) {
        const field = fields[i];
        const transformation = transformField(field);
        
        if (transformation) {
            updates.push({
                index: i,
                path: `${pathPrefix}.${i}`,
                update: transformation,
                fieldId: field._id || field.code || field.name,  // Track field for counting
                hasAmisField: !!field._amisField  // Track if we need to unset _amisField
            });
            
            // Unset original properties that are being backed up
            if (field._amisField) {
                unsets.push(`${pathPrefix}.${i}._amisField`);
            }
            if (field.formula) {
                unsets.push(`${pathPrefix}.${i}.formula`);
            }
            if (field.default_value) {
                unsets.push(`${pathPrefix}.${i}.default_value`);
            }
        }
    }
    
    return { updates, unsets };
}

module.exports = {
    rest: {
        method: 'GET',
        fullPath: '/api/workflow/migrateApprovalCommentsField'
    },
    params: {
        batchSize: { type: 'number', optional: true, convert: true },
        dryRun: { type: 'boolean', optional: true, convert: true },
        fid: { type: 'string', optional: true }  // Flow ID to process single flow
    },
    async handler(ctx) {
        const { user } = ctx.meta;

        // Check if user exists and is space admin
        if (!user || user.is_space_admin !== true) {
            throw new Error('只有管理员才能执行此操作');
        }

        const { batchSize = 500, dryRun = false, fid } = ctx.params;
        
        const formsCollection = await getCollection('forms');
        
        // Build query filter
        const query = { state: 'enabled' };  // Only process enabled forms
        
        // If fid is provided, query flows collection to get form ID
        if (fid) {
            const flowsCollection = await getCollection('flows');
            const flow = await flowsCollection.findOne({ _id: fid });
            
            if (!flow) {
                throw new Error(`Flow with ID ${fid} not found`);
            }
            
            if (!flow.form) {
                throw new Error(`Flow ${fid} does not have a form associated`);
            }
            
            // Add form ID filter
            query._id = flow.form;
        }

        // Use cursor for memory efficiency
        const cursor = formsCollection.find(query);
        
        const results = {
            totalForms: 0,
            totalFields: 0,
            errors: []
        };
        
        const bulkOperations = [];

        // Process forms one by one using cursor
        while (await cursor.hasNext()) {
            const form = await cursor.next();
            try {
                let formUpdated = false;
                const formUpdate = {};
                const formUnset = {};
                let formFieldCount = 0;  // Track actual fields transformed in this form
                
                // Process current.fields
                if (form.current && Array.isArray(form.current.fields)) {
                    const { updates, unsets } = processFieldsArray(form.current.fields, 'current.fields');
                    
                    if (updates.length > 0) {
                        formUpdated = true;
                        formFieldCount += updates.length;  // Count fields, not operations
                        
                        // Build $set updates for current.fields
                        for (const update of updates) {
                            for (const key in update.update) {
                                formUpdate[`current.fields.${update.index}.${key}`] = update.update[key];
                            }
                        }
                        
                        // Build $unset for _amisField
                        for (const path of unsets) {
                            formUnset[path] = '';
                        }
                    }
                }
                
                // Process historys[].fields
                if (Array.isArray(form.historys)) {
                    for (let h = 0; h < form.historys.length; h++) {
                        const history = form.historys[h];
                        if (Array.isArray(history.fields)) {
                            const { updates, unsets } = processFieldsArray(
                                history.fields, 
                                `historys.${h}.fields`
                            );
                            
                            if (updates.length > 0) {
                                formUpdated = true;
                                formFieldCount += updates.length;  // Count fields, not operations
                                
                                // Build $set updates for historys.fields
                                for (const update of updates) {
                                    for (const key in update.update) {
                                        formUpdate[`historys.${h}.fields.${update.index}.${key}`] = update.update[key];
                                    }
                                }
                                
                                // Build $unset for _amisField
                                for (const path of unsets) {
                                    formUnset[path] = '';
                                }
                            }
                        }
                    }
                }
                
                // Process root amis_schema (not current.amis_schema or historys[*].amis_schema)
                if (form.amis_schema && typeof form.amis_schema === 'string') {
                    const transformedSchema = processAmisSchema(form.amis_schema);
                    
                    if (transformedSchema) {
                        formUpdated = true;
                        
                        // Backup original amis_schema
                        formUpdate.__approval_comments_original__amis_schema = form.amis_schema;
                        
                        // Set transformed amis_schema
                        formUpdate.amis_schema = transformedSchema;
                    }
                }
                
                if (formUpdated) {
                    results.totalForms++;
                    results.totalFields += formFieldCount;  // Add actual field count
                    
                    if (!dryRun) {
                        const updateDoc = { $set: formUpdate };
                        if (Object.keys(formUnset).length > 0) {
                            updateDoc.$unset = formUnset;
                        }
                        
                        bulkOperations.push({
                            updateOne: {
                                filter: { _id: form._id },
                                update: updateDoc
                            }
                        });
                        
                        // Execute bulk write in batches
                        if (bulkOperations.length >= batchSize) {
                            await formsCollection.bulkWrite(bulkOperations, { ordered: false });
                            bulkOperations.length = 0; // Clear array
                        }
                    }
                }
            } catch (error) {
                console.error(`Error processing form ${form._id}:`, error);
                results.errors.push({
                    formId: form._id,
                    formName: form.name,
                    error: error.message
                });
            }
        }
        
        // Execute remaining bulk operations
        if (!dryRun && bulkOperations.length > 0) {
            try {
                await formsCollection.bulkWrite(bulkOperations, { ordered: false });
            } catch (error) {
                console.error('Error executing final bulk write:', error);
                results.errors.push({
                    error: 'Final bulk write failed: ' + error.message
                });
            }
        }
        
        return {
            ...results,
            dryRun,
            message: dryRun ? 'Dry run completed - no changes made' : 'Transformation completed'
        };
    }
};
