const _ = require('lodash');

// Helper to find field definition
function findFieldDef(fields, fieldName) {
    if (!fields) return null;

    // Handle array fields (e.g. 'line_items.$.description')
    const cleanName = fieldName.replace(/\.\$\./g, '.').replace(/\.\$$/, '');

    // Direct match
    if (fields[cleanName]) return fields[cleanName];

    // Search in nested fields
    for (const key in fields) {
        if (fields[key].name === cleanName) return fields[key];
        if (fields[key].code === cleanName) return fields[key];
        // If field is inside a grid/object
        if (key.includes('.') && key.endsWith(cleanName)) return fields[key];
    }
    return null;
}

function getFieldLabel(fieldDef, fieldName) {
    if (fieldDef && fieldDef.label) {
        return fieldDef.label;
    }
    // Fallback: try to derive from name or just return name
    return fieldName;
}

const getArgumentsList = (func)=>{
    let funcString;
    if (typeof func === 'function') {
      funcString = func.toString();
    } else {
      funcString = func;
    }
    const regExp = /function\s*\w*\(([\s\S]*?)\)/;
    if (regExp.test(funcString)) {
      const argList = RegExp.$1.split(',');
      return argList.map(arg => arg.replace(/\s/g, ''));
    } else {
      return [];
    }
}

function fieldToAmis(fieldName, fieldDef, schema, isColumn = false, isPrint = false) {
    // Handle section type special case
    if (fieldDef && fieldDef.type === 'section') {
        const displayName = fieldDef.name || fieldDef.code || '';
        return `<span class="antd-TplField"><span><div class="font-bold">${displayName}<span class="antd-Form-star">*</span><pre class="font-normal"></pre></div></span></span>`;
    }

    let finalName = fieldName;
    let processedFormula = null;
    if (fieldDef) {
        if (fieldDef.code) {
            finalName = fieldDef.code;
        } else if (fieldDef.name) {
            finalName = fieldDef.name;
        }

        // Print Template Simplification
        if (isPrint && !isColumn) {
            const type = fieldDef.type;
            if (['text', 'input', 'textarea', 'url', 'email', 'number', 'currency', 'percent', 'password', 'date', 'datetime', 'boolean', 'select', 'lookup', 'master_detail', 'radio', 'checkbox'].includes(type) || fieldDef.formula) {
                return `{{${finalName}}}`;
            }
        }
    }

    const config = {
        name: finalName,
        id: `u:${Math.random().toString(36).substr(2, 9)}`
    };

    if (isColumn) {
        if (schema && schema.atts && schema.atts.label) {
            config.label = schema.atts.label;
        } else {
            config.label = getFieldLabel(fieldDef, fieldName);
        }
    } else {
        config.label = false;
    }

    // Type mapping
    let type = 'input-text'; // default
    if (fieldDef) {
        switch (fieldDef.type) {
            case 'text':
            case 'input':
                type = 'input-text';
                break;
            case 'textarea': type = 'textarea'; break;
            case 'number': type = 'input-number'; break;
            case 'date': type = 'input-date'; break;
            case 'datetime': type = 'input-datetime'; break;
            case 'boolean': type = 'checkbox'; break;
            case 'radio': type = 'radios'; break;
            case 'checkbox': type = 'checkboxes'; break;
            case 'select': type = 'select'; break;
            case 'lookup': type = 'select'; break; // Simplified for now
            case 'odata':
                type = 'select';
                const field = fieldDef;
                const inTable = isColumn;
                const argsName = getArgumentsList(field.filters);
                let labelField = field.formula ? field.formula.substr(1, field.formula.length - 2) : 'name';
                if(labelField.indexOf(".") > -1){
                    labelField = labelField.substr(labelField.indexOf(".") + 1);
                }
                config.multiple = field.is_multiselect;

                // Compress script to single line to avoid JSON parsing issues with control characters
                const adaptorScript = `
                    return (async () => {
                        let options = _.map(payload.value, (item)=>{
                            const value = item;
                            item["@label"] = item["${labelField}"];
                            delete item['@odata.editLink'];
                            delete item['@odata.etag'];
                            delete item['@odata.id'];
                            return {
                                label: item["@label"],
                                value: value
                            }
                        });
                        
                        if(api.selectedIds && api.selectedIds.length > 0){
                            const loadedIds = options.map(function(opt){ return opt.value._id; });
                            const missingIds = api.selectedIds.filter(function(id){ return !loadedIds.includes(id); });
                            if(missingIds.length > 0){
                                const baseUrl = api.baseUrl;
                                const idFilter = missingIds.map(function(id){ return "_id eq '" + id + "'"; }).join(" or ");
                                const fetchUrl = baseUrl + (baseUrl.indexOf('?') > -1 ? '&' : '?') + "$filter=" + idFilter;
                                try {
                                    const response = await fetch(fetchUrl, {});
                                    const data = await response.json();
                                    if (data && data.value) {
                                        const missingOptions = _.map(data.value, (item) => {
                                            const value = item;
                                            item["@label"] = item["${labelField}"];
                                            delete item['@odata.editLink'];
                                            delete item['@odata.etag'];
                                            delete item['@odata.id'];
                                            return { label: item["@label"], value: value };
                                        });
                                        options.unshift(...missingOptions);
                                    }
                                } catch (e) {
                                    console.error("Failed to fetch missing values", e);
                                }
                            }
                        }
                        payload.data = { options: options };
                        return payload;
                    })();
                `.replace(/\s+/g, ' ');

                const filtersStr = field.filters ? _.replace(field.filters, /_.pluck/g, '_.map') : '';
                const requestAdaptorScript = `
                    const filters = \`${filtersStr}\`;
                    let url = \`${field.url}\`;
                    api.baseUrl = url;
                    if(filters){
                        let joinKey = url.indexOf('?') > 0 ? '&' : '?';
                        let _filter = [];
                        if(filters.startsWith('function(') || filters.startsWith('function (')){
                            const argsName = ${JSON.stringify(argsName)};
                            const fun = eval('_fun='+filters);
                            const funArgs = [];
                            for(const item of argsName){
                                funArgs.push(context[item]);
                            }
                            _filter = fun.apply({}, funArgs);
                        }else{
                            _filter = filters;
                        }

                        const val = context.value || _.get(context, '${field.code}');
                        let ids = [];
                        if(val){
                            const values = Array.isArray(val) ? val : [val];
                            ids = values.map((v) => v && (v._id || v)).filter((v) => typeof v === 'string');
                        }

                        ids = _.uniq(ids);
                        api.selectedIds = ids;
                        
                        if(context.term){
                            _filter = \`(\${_filter}) and contains(name, '\${context.term}')\`;
                        }
                        joinKey = url.indexOf('?') > 0 ? '&' : '?';
                        api.url = url + joinKey + "$filter=" + _filter;
                    }else{
                        api.url = url;
                    }
                    api.query = {};
                    return api;
                `.replace(/\s+/g, ' ');

                config.autoComplete = {
                    url: field.url.indexOf('?') < 0 ? `${field.url}?term=\${term}` : `${field.url}&term=\${term}`, 
                    method: "get",
                    dataType: "json",
                    adaptor: adaptorScript,
                    requestAdaptor: requestAdaptorScript
                };
                
                // Only add trackExpression if there are dependent fields (argsName)
                if (argsName && argsName.length > 0) {
                    // Use simpler tracking without |json to avoid potential newline issues in stringified objects
                    config.autoComplete.trackExpression = _.join(_.map(argsName, (item) => `\${${item}}`), '-');
                }

                config.source = _.cloneDeep(config.autoComplete);
                // trackExpression is needed in source to react to dependency changes
                
                if(!inTable){
                    config.searchable = true;
                }
                break;
            case 'table':
                type = 'steedos-input-table';
                config.columnsTogglable = false;
                if (!isColumn) {
                    const editableCondition = "${record.step.permissions['" + config.name + "'] == 'editable'}";
                    config.addableOn = editableCondition;
                    config.editableOn = editableCondition;
                    config.removableOn = editableCondition;
                }
                if (fieldDef.fields) {
                    config.fields = fieldDef.fields.map(f => {
                        return fieldToAmis(f.name || f.code, f, null, true);
                    });
                }
                break;
        }

        if (fieldDef.options) {
            let options = fieldDef.options;
            if (typeof options === 'string') {
                options = options.split('\n').map(o => o.trim()).filter(o => o);
            }

            if (Array.isArray(options)) {
                config.options = options.map(opt => {
                    if (typeof opt === 'string') {
                        return { label: opt, value: opt };
                    }
                    if (typeof opt === 'object') {
                        return {
                            label: opt.label || opt.name || opt.value,
                            value: opt.value || opt.code || opt.name
                        };
                    }
                    return opt;
                });
            } else {
                config.options = options;
            }
        }
        if (fieldDef.required) {
            config.required = true;
        }
        if (fieldDef.precision) {
            config.precision = fieldDef.precision;
        }

        if (fieldDef.formula && (type === 'input-text' || type === 'input-number' || type === 'textarea')) {
            let formula = fieldDef.formula;
            formula = formula.replace(/numToRMB/g, 'UPPERMONEY');
            const replaceCallback = (match, p1) => {
                const cleanP1 = p1.replace(/（/g, '_').replace(/）/g, '');
                if (type === 'input-number' || formula.includes('UPPERMONEY')) {
                    return `(${cleanP1} || 0)`;
                } else {
                    return `(${cleanP1} || "")`;
                }
            };
            formula = formula.replace(/\{([^}]+)\}/g, replaceCallback);
            
            processedFormula = "${" + formula + "}";

            if (isColumn) {
                config.value = processedFormula;
            }
        }
    }

    config.type = type;

    if (!isColumn) {
        if (processedFormula) {
            config.static = true;
            const formulaComp = {
                name: config.name,
                id: config.id,
                label: false,
                formula: processedFormula,
                type: "formula"
            };
            return [config, formulaComp];
        } else {
            config.staticOn = "${record.step.permissions['" + config.name + "'] != 'editable'}";
        }
    }

    return config;
}

function parseArgs(args) {
    const argMap = {};
    const re = /(\w+)\s*=\s*(["'])(.*?)\2|(\w+)\s*=\s*([^"'\s]+)/g;
    let m;
    while ((m = re.exec(args)) !== null) {
        if (m[1]) argMap[m[1]] = m[3];
        else argMap[m[4]] = m[5];
    }
    return argMap;
}

/**
 * 转换模板内容
 * @param {string} content 原始Blaze模板
 * @param {object} fields 表单字段定义
 * @param {boolean} isPrint 是否为打印模板
 */
function convertTemplate(content, fields, isPrint) {
    if (!content) return content;
    let newContent = content;

    // Phase 1: Blaze -> Liquid
    newContent = newContent.replace(/\{\{![\s\S]*?\}\}/g, '');
    newContent = newContent.replace(/\{\{#if\s+([^}]+)\}\}/g, '{% if $1 %}');
    newContent = newContent.replace(/\{\{else\}\}/g, '{% else %}');
    newContent = newContent.replace(/\{\{\/if\}\}/g, '{% endif %}');
    newContent = newContent.replace(/\{\{#unless\s+([^}]+)\}\}/g, '{% unless $1 %}');
    newContent = newContent.replace(/\{\{\/unless\}\}/g, '{% endunless %}');
    newContent = newContent.replace(/\{\{#each\s+([^}]+)\}\}/g, '{% for item in $1 %}');
    newContent = newContent.replace(/\{\{\/each\}\}/g, '{% endfor %}');
    newContent = newContent.replace(/\{\{\{\s*([^}]+)\s*\}\}\}/g, '{{ $1 | raw }}');

    // Remove table-page-footer from all templates
    newContent = newContent.replace(/<table[^>]*class="[^"]*table-page-footer[\s\S]*?<\/table>/g, '');

    // Phase 2: afFormGroup -> AMIS
    // Remove table-page-footer (Retaining table-page-title based on requirement)
    if (isPrint) {
        newContent = newContent.replace(/<tr([^>]*)style=(["'])([^"']*?height:\s*0px[^"']*?)\2([^>]*)>/gi, '<tr$1style=$2$3; display: none;$2$4>');

        if (!newContent.includes('border: 2px solid black;')) {
            const tableCSS = `
<style>
.form-table {
    border-collapse: collapse;
    border: 2px solid black;
}
.form-table th, 
.form-table td {
    border: 1px solid black;
    padding: 4px 6px;
}
</style>
`;
            if (newContent.includes('<style>')) {
                newContent = newContent.replace('<style>', tableCSS + '\n<style>');
            } else {
                newContent = tableCSS + '\n' + newContent;
            }
        }
    }

    // --- Sub-phase 2a: Convert TD wrapped afFormGroup tags ---
    newContent = newContent.replace(/<td([^>]*)>(\s*)\{\{>\s*afFormGroup\s+([^}]+)\}\}(\s*)<\/td>/g, (match, tdAttrs, space1, args, space2) => {
        const argMap = parseArgs(args);
        const fieldName = argMap.name;
        if (!fieldName) return match;

        const fieldDef = findFieldDef(fields, fieldName);
        const amisSchema = fieldToAmis(fieldName, fieldDef, { atts: argMap }, false, isPrint);

        let contentInner = '';
        if (typeof amisSchema === 'string') {
            contentInner = amisSchema;
        } else if (Array.isArray(amisSchema)) {
            contentInner = amisSchema.map(s => `{% amis %}\n${JSON.stringify(s, null, 2)}\n{% endamis %}`).join('\n    ');
        } else {
            contentInner = `{% amis %}\n${JSON.stringify(amisSchema, null, 2)}\n{% endamis %}`;
        }

        if (fieldDef && fieldDef.type === 'section') {
            let newAttrs = tdAttrs || ' ';
            if (newAttrs.includes('style="')) {
                newAttrs = newAttrs.replace('style="', 'style="background: rgb(241, 241, 241); ');
            } else {
                newAttrs = newAttrs + ' style="background: rgb(241, 241, 241);"';
            }
            return `<td${newAttrs}>${space1}${contentInner}${space2}</td>`;
        }

        return `<td${tdAttrs}>${space1}${contentInner}${space2}</td>`;
    });

    // --- Sub-phase 2b: Convert remaining raw afFormGroup tags ---
    newContent = newContent.replace(/\{\{>\s*afFormGroup\s+([^}]+)\}\}/g, (match, args) => {
        const argMap = parseArgs(args);
        const fieldName = argMap.name;
        if (!fieldName) return match;

        const fieldDef = findFieldDef(fields, fieldName);
        const amisSchema = fieldToAmis(fieldName, fieldDef, { atts: argMap }, false, isPrint);

        if (typeof amisSchema === 'string') {
            return amisSchema;
        }
        if (Array.isArray(amisSchema)) {
            return amisSchema.map(s => `{% amis %}\n${JSON.stringify(s, null, 2)}\n{% endamis %}`).join('\n    ');
        }

        return `{% amis %}\n${JSON.stringify(amisSchema, null, 2)}\n{% endamis %}`;
    });

    // --- Sub-phase 2c: Convert afFieldLabelText ---
    newContent = newContent.replace(/\{\{afFieldLabelText\s+name=["'](.+?)["']\}\}/g, (match, name) => {
        const fieldDef = findFieldDef(fields, name);
        if (fieldDef && fieldDef.is_required) {
            return `${name}<span class="antd-Form-star" style="display: \${record.step.permissions['${name}'] == 'editable' ? 'inline' : 'none'}">*</span>`;
        }
        return name;
    });

    // --- Sub-phase 2d: Convert instanceSignText ---
    newContent = newContent.replace(/\{\{>\s*instanceSignText\s+([\s\S]+?)\}\}/g, (match, args) => {
        const argMap = parseArgs(args);
        const fieldName = argMap.name;
        if (!fieldName) return match;

        const fieldDef = findFieldDef(fields, fieldName);
        let stepName = "";
        
        let formula = argMap.field_formula;
        if (fieldDef && fieldDef.formula) {
            formula = fieldDef.formula;
        }

        if (formula) {
            const formulaMatch = formula.match(/signature\.traces\.([^}]+)/);
            if (formulaMatch) {
                stepName = formulaMatch[1].replace('}', '');
            }
        }

        const randomSuffix = Math.random().toString(36).substr(2, 6);
        const amisSchema = {
            "type": "sfield-approvalcomments",
            "config": {
                "type": "approval_comments",
                "label": false,
                "amis": {
                    "mode": "horizontal",
                    "name": fieldName,
                    "id": `u:${fieldName}`
                },
                "name": `confirm_${randomSuffix}`,
                "steps": [
                    {
                        "name": stepName
                    }
                ]
            },
            "id": `u:sfield-approvalcomments-${randomSuffix}`
        };

        return `{% amis %}\n${JSON.stringify(amisSchema, null, 2)}\n{% endamis %}`;
    });

    // --- Sub-phase 2e: Add header for tr-child-table ---
    // Extract dynamic title from td-childfield-xxx class if present
    newContent = newContent.replace(/(<tr[^>]*class\s*=\s*["'][^"']*tr-child-table[^"']*["'][^>]*>[\s\S]*?class\s*=\s*["'][^"']*?td-childfield-)([^\s"']+)/gi, 
        (match, beforeName, name) => {
            return `<tr><td colspan="10" style="background:#f1f1f1" class="font-bold">${name}</td></tr>${beforeName}${name}`;
        }
    );

    return newContent;
}

module.exports = {
    convertTemplate
};
