"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.migrate = migrate;
const ts = require("typescript");
const ast_utils_1 = require("../../utils/ast-utils");
const html_utils_1 = require("../../utils/html-utils");
// Quill format name → EditorFormat mapping ('bullet' is the only rename)
const FORMAT_MAP = {
    bold: 'bold',
    italic: 'italic',
    underline: 'underline',
    strike: 'strike',
    color: 'color',
    header: 'header',
    link: 'link',
    image: 'image',
    list: 'list',
    bullet: 'list',
    blockquote: 'blockquote',
    'code-block': 'code-block',
    align: 'align',
    clean: 'clean',
};
// Quill formats with no equivalent in EditorFormat
const UNSUPPORTED_FORMATS = new Set([
    'background',
    'font',
    'size',
    'video',
    'indent',
    'direction',
    'formula',
    'code',
    'script',
]);
const PRIMENG_EDITOR_MODULES = ['EditorModule'];
const WARN_REMOVE_ATTRS = ['modules', 'bounds', 'scrollingContainer', 'debug'];
const TIPTAP_DEPS = {
    '@tiptap/core': '^3.27.0',
    '@tiptap/extension-color': '^3.27.0',
    '@tiptap/extension-highlight': '^3.27.0',
    '@tiptap/extension-image': '^3.27.0',
    '@tiptap/extension-placeholder': '^3.27.0',
    '@tiptap/extension-subscript': '^3.27.0',
    '@tiptap/extension-superscript': '^3.27.0',
    '@tiptap/extension-text-align': '^3.27.0',
    '@tiptap/extension-text-style': '^3.27.0',
    '@tiptap/extension-underline': '^3.27.0',
    '@tiptap/pm': '^3.27.0',
    '@tiptap/starter-kit': '^3.27.0',
};
function quickCheck(tree) {
    let detected = false;
    tree.visit((path) => {
        if (detected || path.includes('/node_modules/'))
            return;
        try {
            const content = tree.readText(path);
            if (path.endsWith('.html') && content.includes('p-editor')) {
                detected = true;
                return;
            }
            if (path.endsWith('.ts')) {
                if (content.includes('EditorModule') && content.includes('primeng')) {
                    detected = true;
                    return;
                }
                if (content.includes("type: 'editor'") && (content.includes('modules') || content.includes('formats'))) {
                    detected = true;
                    return;
                }
            }
        }
        catch (_) {
            // ignore unreadable binary files
        }
    });
    return detected;
}
function migrate() {
    return (tree, context) => {
        if (!quickCheck(tree)) {
            context.logger.info('⏭  Nenhum uso de p-editor ou configuração legada de EditorField detectado. Migração não aplicada.');
            return;
        }
        tree.visit((path) => {
            if (path.includes('/node_modules/'))
                return;
            if (path.endsWith('.html')) {
                migrateTemplate(tree, path, context);
            }
            else if (path.endsWith('.ts')) {
                migrateTypeScriptFile(tree, path, context);
            }
        });
        migratePackageJson(tree, context);
    };
}
// ─── HTML Migration ───────────────────────────────────────────────────────────
function migrateTemplate(tree, path, context) {
    const content = tree.readText(path);
    if (!content.includes('p-editor'))
        return;
    let updated = content;
    // Remove <p-header> blocks (custom Quill toolbar — not supported in s-editor)
    if (updated.includes('<p-header>')) {
        context.logger.warn(`${path}: <p-header> removido — toolbar customizada Quill não é suportada no s-editor. ` +
            `Use [formats]="['bold', 'italic', ...]" para definir os botões visíveis.`);
        updated = updated.replace(/<p-header>[\s\S]*?<\/p-header>/g, '');
    }
    // Remove <ng-template pTemplate="header"> blocks
    if (/pTemplate="header"/.test(updated)) {
        context.logger.warn(`${path}: ng-template com pTemplate="header" removido — toolbar customizada não é suportada no s-editor. ` +
            `Use [formats]="['bold', 'italic', ...]" para definir os botões visíveis.`);
        updated = updated.replace(/<ng-template[^>]*pTemplate="header"[^>]*>[\s\S]*?<\/ng-template>/g, '');
    }
    // Warn and remove unsupported attributes
    const attrMessages = {
        modules: `[modules] removido — configuração Quill não tem equivalente direto no TipTap. Use [extensions] para customizar o s-editor.`,
        bounds: `[bounds] removido — atributo não suportado pelo s-editor.`,
        scrollingContainer: `[scrollingContainer] removido — atributo não suportado pelo s-editor.`,
        debug: `[debug] removido — atributo não suportado pelo s-editor.`,
    };
    for (const attr of WARN_REMOVE_ATTRS) {
        const attrPattern = new RegExp(`\\[?${(0, html_utils_1.escapeRegex)(attr)}\\]?(?:\\s*=\\s*"[^"]*")?`);
        if (attrPattern.test(updated)) {
            context.logger.warn(`${path}: ${attrMessages[attr]}`);
            updated = (0, html_utils_1.removeHtmlAttribute)(updated, attr);
        }
    }
    // Warn about [formats] type change (binding to a property reference)
    if (/\[formats\]/.test(updated)) {
        context.logger.warn(`${path}: O atributo [formats] agora requer EditorFormat[] em vez de string[]. Atualize o tipo da propriedade no componente TypeScript.`);
    }
    // Rename events
    updated = updated.replace(/\(onTextChange\)/g, '(textChange)');
    updated = updated.replace(/\(onSelectionChange\)/g, '(selectionChange)');
    updated = updated.replace(/\(onInit\)/g, '(editorInit)');
    // Warn about changed event payloads when handler uses $event
    if (/\(textChange\)\s*=\s*"[^"]*\$event/.test(updated)) {
        context.logger.warn(`${path}: (textChange): payload mudou — delta e source foram removidos. Apenas { htmlValue, textValue } estão disponíveis.`);
    }
    if (/\(selectionChange\)\s*=\s*"[^"]*\$event/.test(updated)) {
        context.logger.warn(`${path}: (selectionChange): payload mudou — range/oldRange/source foram substituídos por { from, to, empty }.`);
    }
    if (/\(editorInit\)\s*=\s*"[^"]*\$event/.test(updated)) {
        context.logger.warn(`${path}: (editorInit): o evento agora emite a instância Editor do TipTap diretamente (não mais { editor: Quill }).`);
    }
    // Rename tags
    updated = updated.replace(/<p-editor\b/g, '<s-editor');
    updated = updated.replace(/<\/p-editor>/g, '</s-editor>');
    if (updated !== content) {
        tree.overwrite(path, updated);
    }
}
// ─── TypeScript Migration ──────────────────────────────────────────────────────
function migrateTypeScriptFile(tree, path, context) {
    const content = tree.readText(path);
    const hasEditorModule = content.includes('EditorModule') && content.includes('primeng');
    const hasDynamicFormEditor = content.includes("type: 'editor'");
    if (!hasEditorModule && !hasDynamicFormEditor)
        return;
    const sourceFile = ts.createSourceFile(path, content, ts.ScriptTarget.Latest, true);
    const allChanges = [];
    let needsEditorImport = false;
    // 1. EditorModule (PrimeNG) → EditorComponent (s-editor)
    if (hasEditorModule) {
        const { changes: importChanges } = (0, ast_utils_1.buildRemoveNamedImportsChanges)(sourceFile, PRIMENG_EDITOR_MODULES, 'primeng');
        const decoratorChanges = [
            ...ast_utils_1.ngModule.replaceInImports(sourceFile, PRIMENG_EDITOR_MODULES, 'EditorComponent'),
            ...ast_utils_1.ngModule.replaceInExports(sourceFile, PRIMENG_EDITOR_MODULES, 'EditorComponent'),
            ...ast_utils_1.ngComponent.replaceInImports(sourceFile, PRIMENG_EDITOR_MODULES, 'EditorComponent'),
        ];
        if (importChanges.length > 0 || decoratorChanges.length > 0) {
            allChanges.push(...importChanges, ...decoratorChanges);
            needsEditorImport = true;
            if (content.includes('$event.delta') || content.includes('$event.source')) {
                context.logger.warn(`${path}: Propriedades $event.delta e $event.source foram removidas do evento textChange. Apenas htmlValue e textValue estão disponíveis.`);
            }
            if (content.includes('$event.range') || content.includes('$event.oldRange')) {
                context.logger.warn(`${path}: Propriedades $event.range/$event.oldRange foram substituídas por { from, to, empty } no evento selectionChange.`);
            }
        }
    }
    // 2. DynamicForm editor config cleanup (modules removal, formats mapping)
    if (hasDynamicFormEditor) {
        allChanges.push(...buildDynamicFormEditorChanges(sourceFile, path, context));
    }
    if (allChanges.length === 0)
        return;
    let updated = (0, ast_utils_1.applyChanges)(content, allChanges);
    updated = updated.replace(/\n\n+/g, '\n\n');
    if (needsEditorImport) {
        updated = (0, ast_utils_1.insertImportStatement)(updated, 'EditorComponent', '@seniorsistemas/angular-components/editor');
    }
    if (updated !== content) {
        tree.overwrite(path, updated);
    }
}
function buildDynamicFormEditorChanges(sourceFile, path, context) {
    const changes = [];
    function visitNode(node) {
        if (ts.isObjectLiteralExpression(node)) {
            const typeProperty = node.properties.find((p) => ts.isPropertyAssignment(p) &&
                p.name.getText(sourceFile) === 'type' &&
                ts.isStringLiteral(p.initializer) &&
                p.initializer.text === 'editor');
            if (typeProperty) {
                // Remove 'modules' property (Quill-specific, no TipTap equivalent)
                const modulesProperty = node.properties.find((p) => ts.isPropertyAssignment(p) && p.name.getText(sourceFile) === 'modules');
                if (modulesProperty) {
                    context.logger.warn(`${path}: Propriedade 'modules' removida de configuração EditorField — ` +
                        `sem equivalente direto no TipTap. Use 'extensions' para customizar o s-editor.`);
                    changes.push(...(0, ast_utils_1.buildRemoveObjectPropertyChange)(sourceFile, node, modulesProperty));
                }
                // Migrate 'formats' array values from Quill names → EditorFormat
                const formatsProperty = node.properties.find((p) => ts.isPropertyAssignment(p) && p.name.getText(sourceFile) === 'formats');
                if (formatsProperty && ts.isArrayLiteralExpression(formatsProperty.initializer)) {
                    changes.push(...buildMigrateFormatsChanges(sourceFile, formatsProperty.initializer, path, context));
                }
            }
        }
        ts.forEachChild(node, visitNode);
    }
    visitNode(sourceFile);
    return changes;
}
function buildMigrateFormatsChanges(sourceFile, arr, path, context) {
    // Skip if any element is not a string literal (can't safely migrate)
    if (arr.elements.some((e) => !ts.isStringLiteral(e))) {
        context.logger.warn(`${path}: O array formats contém expressões não-literais e não pôde ser migrado automaticamente. ` +
            `Atualize manualmente para usar o tipo EditorFormat.`);
        return [];
    }
    const seen = new Set();
    const newValues = [];
    const removed = [];
    for (const element of arr.elements) {
        const quillFormat = element.text;
        if (UNSUPPORTED_FORMATS.has(quillFormat)) {
            removed.push(quillFormat);
            continue;
        }
        const mapped = FORMAT_MAP[quillFormat] ?? quillFormat;
        if (seen.has(mapped))
            continue; // deduplicate (e.g., 'list' + 'bullet' → single 'list')
        seen.add(mapped);
        newValues.push(`'${mapped}'`);
    }
    if (removed.length > 0) {
        context.logger.warn(`${path}: Formatos Quill sem equivalente no s-editor foram removidos: [${removed.join(', ')}]. ` +
            `Formatos disponíveis: bold, italic, underline, strike, color, highlight, ` +
            `header, list, align, blockquote, code-block, link, image, subscript, superscript, clean.`);
    }
    // Only emit change if values actually differ (avoid unnecessary reformatting)
    const hadChanges = removed.length > 0 ||
        arr.elements.some((e) => {
            if (!ts.isStringLiteral(e))
                return false;
            const text = e.text;
            return FORMAT_MAP[text] !== undefined && FORMAT_MAP[text] !== text;
        });
    if (!hadChanges)
        return [];
    return [{ start: arr.getStart(), end: arr.getEnd(), replacement: `[${newValues.join(', ')}]` }];
}
// ─── package.json Migration ───────────────────────────────────────────────────
function migratePackageJson(tree, context) {
    if (!tree.exists('/package.json'))
        return;
    let pkg;
    try {
        pkg = JSON.parse(tree.readText('/package.json'));
    }
    catch (_) {
        return;
    }
    let changed = false;
    // Remove quill
    for (const depType of ['dependencies', 'devDependencies']) {
        if (pkg[depType]?.['quill']) {
            delete pkg[depType]['quill'];
            context.logger.info(`package.json: "quill" removido de ${depType}.`);
            changed = true;
        }
    }
    // Add @tiptap/* to dependencies (skip if already present)
    if (!pkg['dependencies'])
        pkg['dependencies'] = {};
    for (const [name, version] of Object.entries(TIPTAP_DEPS)) {
        if (!pkg['dependencies'][name]) {
            pkg['dependencies'][name] = version;
            changed = true;
        }
    }
    if (changed) {
        tree.overwrite('/package.json', JSON.stringify(pkg, null, 2) + '\n');
        context.logger.info('package.json: dependências @tiptap/* adicionadas. Execute "npm install" para instalar as novas dependências.');
    }
}
//# sourceMappingURL=index.js.map